Exemple #1
0
 def get_access_token(self, scope=None):
     Config = self.env['ir.config_parameter'].sudo()
     google_drive_refresh_token = Config.get_param('google_drive_refresh_token')
     user_is_admin = self.env['res.users'].browse(self.env.user.id)._is_admin()
     if not google_drive_refresh_token:
         if user_is_admin:
             dummy, action_id = self.env['ir.model.data'].get_object_reference('base_setup', 'action_general_configuration')
             msg = _("You haven't configured 'Authorization Code' generated from google, Please generate and configure it .")
             raise RedirectWarning(msg, action_id, _('Go to the configuration panel'))
         else:
             raise UserError(_("Google Drive is not yet configured. Please contact your administrator."))
     google_drive_client_id = Config.get_param('google_drive_client_id')
     google_drive_client_secret = Config.get_param('google_drive_client_secret')
     #For Getting New Access Token With help of old Refresh Token
     data = {
         'client_id': google_drive_client_id,
         'refresh_token': google_drive_refresh_token,
         'client_secret': google_drive_client_secret,
         'grant_type': "refresh_token",
         'scope': scope or 'https://www.googleapis.com/auth/drive'
     }
     headers = {"Content-type": "application/x-www-form-urlencoded"}
     try:
         req = requests.post(GOOGLE_TOKEN_ENDPOINT, data=data, headers=headers, timeout=TIMEOUT)
         req.raise_for_status()
     except requests.HTTPError:
         if user_is_admin:
             dummy, action_id = self.env['ir.model.data'].get_object_reference('base_setup', 'action_general_configuration')
             msg = _("Something went wrong during the token generation. Please request again an authorization code .")
             raise RedirectWarning(msg, action_id, _('Go to the configuration panel'))
         else:
             raise UserError(_("Google Drive is not yet configured. Please contact your administrator."))
     return req.json().get('access_token')
Exemple #2
0
    def _validate_fiscalyear_lock(self, values):
        if values.get('fiscalyear_lock_date'):

            draft_entries = self.env['account.move'].search([
                ('company_id', 'in', self.ids), ('state', '=', 'draft'),
                ('date', '<=', values['fiscalyear_lock_date'])
            ])
            if draft_entries:
                error_msg = _(
                    'There are still unposted entries in the period you want to lock. You should either post or delete them.'
                )
                action_error = {
                    'view_mode':
                    'tree',
                    'name':
                    'Unposted Entries',
                    'res_model':
                    'account.move',
                    'type':
                    'ir.actions.act_window',
                    'domain': [('id', 'in', draft_entries.ids)],
                    'search_view_id': [
                        self.env.ref('account.view_account_move_filter').id,
                        'search'
                    ],
                    'views':
                    [[self.env.ref('account.view_move_tree').id, 'list'],
                     [self.env.ref('account.view_move_form').id, 'form']],
                }
                raise RedirectWarning(error_msg, action_error,
                                      _('Show unposted entries'))

            unreconciled_statement_lines = self.env[
                'account.bank.statement.line'].search([
                    ('company_id', 'in', self.ids),
                    ('is_reconciled', '=', False),
                    ('date', '<=', values['fiscalyear_lock_date']),
                    ('move_id.state', 'in', ('draft', 'posted')),
                ])
            if unreconciled_statement_lines:
                error_msg = _(
                    "There are still unreconciled bank statement lines in the period you want to lock."
                    "You should either reconcile or delete them.")
                action_error = {
                    'type': 'ir.actions.client',
                    'tag': 'bank_statement_reconciliation_view',
                    'context': {
                        'statement_line_ids': unreconciled_statement_lines.ids,
                        'company_ids': self.ids
                    },
                }
                raise RedirectWarning(
                    error_msg, action_error,
                    _('Show Unreconciled Bank Statement Line'))
Exemple #3
0
 def do_print_checks(self):
     check_layout = self.company_id.account_check_printing_layout
     redirect_action = self.env.ref('account.action_account_config')
     if not check_layout or check_layout == 'disabled':
         msg = _("You have to choose a check layout. For this, go in Invoicing/Accounting Settings, search for 'Checks layout' and set one.")
         raise RedirectWarning(msg, redirect_action.id, _('Go to the configuration panel'))
     report_action = self.env.ref(check_layout, False)
     if not report_action:
         msg = _("Something went wrong with Check Layout, please select another layout in Invoicing/Accounting Settings and try again.")
         raise RedirectWarning(msg, redirect_action.id, _('Go to the configuration panel'))
     self.write({'is_move_sent': True})
     return report_action.report_action(self)
Exemple #4
0
 def get_chart_of_accounts_or_fail(self):
     account = self.env['account.account'].search(
         [('company_id', '=', self.id)], limit=1)
     if len(account) == 0:
         action = self.env.ref('account.action_account_config')
         msg = _(
             "We cannot find a chart of accounts for this company, you should configure it. \n"
             "Please go to Account Configuration and select or install a fiscal localization."
         )
         raise RedirectWarning(msg, action.id,
                               _("Go to the configuration panel"))
     return account
Exemple #5
0
 def _get_default_category_id(self):
     if self._context.get('categ_id') or self._context.get('default_categ_id'):
         return self._context.get('categ_id') or self._context.get('default_categ_id')
     category = self.env.ref('product.product_category_all', raise_if_not_found=False)
     if not category:
         category = self.env['product.category'].search([], limit=1)
     if category:
         return category.id
     else:
         err_msg = _('You must define at least one product category in order to be able to create products.')
         redir_msg = _('Go to Internal Categories')
         raise RedirectWarning(err_msg, self.env.ref('product.product_category_action_form').id, redir_msg)
Exemple #6
0
    def get_config_warning(self, msg):
        """
        Helper: return a Warning exception with the given message where the %(field:xxx)s
        and/or %(menu:yyy)s are replaced by the human readable field's name and/or menuitem's
        full path.

        Usage:
        ------
        Just include in your error message %(field:model_name.field_name)s to obtain the human
        readable field's name, and/or %(menu:module_name.menuitem_xml_id)s to obtain the menuitem's
        full path.

        Example of use:
        ---------------
        from flectra.addons.base.models.res_config import get_warning_config
        raise get_warning_config(cr, _("Error: this action is prohibited. You should check the field %(field:sale.config.settings.fetchmail_lead)s in %(menu:sales_team.menu_sale_config)s."), context=context)

        This will return an exception containing the following message:
            Error: this action is prohibited. You should check the field Create leads from incoming mails in Settings/Configuration/Sales.

        What if there is another substitution in the message already?
        -------------------------------------------------------------
        You could have a situation where the error message you want to upgrade already contains a substitution. Example:
            Cannot find any account journal of %s type for this company.\n\nYou can create one in the menu: \nConfiguration\Journals\Journals.
        What you want to do here is simply to replace the path by %menu:account.menu_account_config)s, and leave the rest alone.
        In order to do that, you can use the double percent (%%) to escape your new substitution, like so:
            Cannot find any account journal of %s type for this company.\n\nYou can create one in the %%(menu:account.menu_account_config)s.
        """
        self = self.sudo()

        # Process the message
        # 1/ find the menu and/or field references, put them in a list
        regex_path = r'%\(((?:menu|field):[a-z_\.]*)\)s'
        references = re.findall(regex_path, msg, flags=re.I)

        # 2/ fetch the menu and/or field replacement values (full path and
        #    human readable field's name) and the action_id if any
        values = {}
        action_id = None
        for item in references:
            ref_type, ref = item.split(':')
            if ref_type == 'menu':
                values[item], action_id = self.get_option_path(ref)
            elif ref_type == 'field':
                values[item] = self.get_option_name(ref)

        # 3/ substitute and return the result
        if (action_id):
            return RedirectWarning(msg % values, action_id,
                                   _('Go to the configuration panel'))
        return UserError(msg % values)
Exemple #7
0
    def _get_journal_letter(self, counterpart_partner=False):
        """ Regarding the AFIP responsibility of the company and the type of journal (sale/purchase), get the allowed
        letters. Optionally, receive the counterpart partner (customer/supplier) and get the allowed letters to work
        with him. This method is used to populate document types on journals and also to filter document types on
        specific invoices to/from customer/supplier
        """
        self.ensure_one()
        letters_data = {
            'issued': {
                '1': ['A', 'B', 'E', 'M'],
                '3': [],
                '4': ['C'],
                '5': [],
                '6': ['C', 'E'],
                '9': ['I'],
                '10': [],
                '13': ['C', 'E'],
                '99': []
            },
            'received': {
                '1': ['A', 'B', 'C', 'E', 'M', 'I'],
                '3': ['B', 'C', 'I'],
                '4': ['B', 'C', 'I'],
                '5': ['B', 'C', 'I'],
                '6': ['A', 'B', 'C', 'I'],
                '9': ['E'],
                '10': ['E'],
                '13': ['A', 'B', 'C', 'I'],
                '99': ['B', 'C', 'I']
            },
        }
        if not self.company_id.l10n_ar_afip_responsibility_type_id:
            action = self.env.ref('base.action_res_company_form')
            msg = _(
                'Can not create chart of account until you configure your company AFIP Responsibility and VAT.'
            )
            raise RedirectWarning(msg, action.id, _('Go to Companies'))

        letters = letters_data[
            'issued' if self.type == 'sale' else 'received'][
                self.company_id.l10n_ar_afip_responsibility_type_id.code]
        if counterpart_partner:
            counterpart_letters = letters_data[
                'issued' if self.type == 'purchase' else 'received'].get(
                    counterpart_partner.l10n_ar_afip_responsibility_type_id.
                    code, [])
            letters = list(set(letters) & set(counterpart_letters))
        return letters
 def default_get(self, fields):
     res = super(ImportMwsProducts, self).default_get(fields)
     context = dict(self._context)
     params = context.get('params', {})
     active_id = context.get('active_id') or params.get('id')
     model = 'multi.channel.sale'
     if (active_id and (context.get('active_model') == model
                        or params.get('model') == model)):
         context['channel_id'] = active_id
         report_status = self.channel_id.with_context(
             context).mws_ensure_report()
         report_id = report_status.get('report_id')
         if report_status.get('message'):
             action_id = self.env.ref(
                 'amazon_flectra_bridge.action_reports').id
             raise RedirectWarning(report_status.get('message'), action_id,
                                   _('Go to the report panel.'))
     return res
Exemple #9
0
 def _onchange_partner_journal(self):
     """ This method is used when the invoice is created from the sale or subscription """
     expo_journals = ['FEERCEL', 'FEEWS', 'FEERCELP']
     for rec in self.filtered(
             lambda x: x.company_id.country_id.code == "AR" and x.journal_id
             .type == 'sale' and x.l10n_latam_use_documents and x.partner_id
             .l10n_ar_afip_responsibility_type_id):
         res_code = rec.partner_id.l10n_ar_afip_responsibility_type_id.code
         domain = [('company_id', '=', rec.company_id.id),
                   ('l10n_latam_use_documents', '=', True),
                   ('type', '=', 'sale')]
         journal = self.env['account.journal']
         msg = False
         if res_code in [
                 '9', '10'
         ] and rec.journal_id.l10n_ar_afip_pos_system not in expo_journals:
             # if partner is foregin and journal is not of expo, we try to change to expo journal
             journal = journal.search(
                 domain +
                 [('l10n_ar_afip_pos_system', 'in', expo_journals)],
                 limit=1)
             msg = _(
                 'You are trying to create an invoice for foreign partner but you don\'t have an exportation journal'
             )
         elif res_code not in [
                 '9', '10'
         ] and rec.journal_id.l10n_ar_afip_pos_system in expo_journals:
             # if partner is NOT foregin and journal is for expo, we try to change to local journal
             journal = journal.search(
                 domain +
                 [('l10n_ar_afip_pos_system', 'not in', expo_journals)],
                 limit=1)
             msg = _(
                 'You are trying to create an invoice for domestic partner but you don\'t have a domestic market journal'
             )
         if journal:
             rec.journal_id = journal.id
         elif msg:
             # Throw an error to user in order to proper configure the journal for the type of operation
             action = self.env.ref('account.action_account_journal_form')
             raise RedirectWarning(msg, action.id, _('Go to Journals'))
Exemple #10
0
 def unlink(self):
     """
     If some tasks to unlink have some timesheets entries, these
     timesheets entries must be unlinked first.
     In this case, a warning message is displayed through a RedirectWarning
     and allows the user to see timesheets entries to unlink.
     """
     tasks_with_timesheets = self.filtered(lambda t: t.timesheet_ids)
     if tasks_with_timesheets:
         if len(tasks_with_timesheets) > 1:
             warning_msg = _(
                 "These tasks have some timesheet entries referencing them. Before removing these tasks, you have to remove these timesheet entries."
             )
         else:
             warning_msg = _(
                 "This task has some timesheet entries referencing it. Before removing this task, you have to remove these timesheet entries."
             )
         raise RedirectWarning(
             warning_msg,
             self.env.ref('hr_timesheet.timesheet_action_task').id,
             _('See timesheet entries'),
             {'active_ids': tasks_with_timesheets.ids})
     return super(Task, self).unlink()
Exemple #11
0
    def _update_reserved_quantity(self,
                                  product_id,
                                  location_id,
                                  quantity,
                                  lot_id=None,
                                  package_id=None,
                                  owner_id=None,
                                  strict=False):
        """ Increase the reserved quantity, i.e. increase `reserved_quantity` for the set of quants
        sharing the combination of `product_id, location_id` if `strict` is set to False or sharing
        the *exact same characteristics* otherwise. Typically, this method is called when reserving
        a move or updating a reserved move line. When reserving a chained move, the strict flag
        should be enabled (to reserve exactly what was brought). When the move is MTS,it could take
        anything from the stock, so we disable the flag. When editing a move line, we naturally
        enable the flag, to reflect the reservation according to the edition.

        :return: a list of tuples (quant, quantity_reserved) showing on which quant the reservation
            was done and how much the system was able to reserve on it
        """
        self = self.sudo()
        rounding = product_id.uom_id.rounding
        quants = self._gather(product_id,
                              location_id,
                              lot_id=lot_id,
                              package_id=package_id,
                              owner_id=owner_id,
                              strict=strict)
        reserved_quants = []

        if float_compare(quantity, 0, precision_rounding=rounding) > 0:
            # if we want to reserve
            available_quantity = sum(
                quants.filtered(lambda q: float_compare(
                    q.quantity, 0, precision_rounding=rounding) > 0).mapped(
                        'quantity')) - sum(quants.mapped('reserved_quantity'))
            if float_compare(quantity,
                             available_quantity,
                             precision_rounding=rounding) > 0:
                raise UserError(
                    _(
                        'It is not possible to reserve more products of %s than you have in stock.',
                        product_id.display_name))
        elif float_compare(quantity, 0, precision_rounding=rounding) < 0:
            # if we want to unreserve
            available_quantity = sum(quants.mapped('reserved_quantity'))
            if float_compare(abs(quantity),
                             available_quantity,
                             precision_rounding=rounding) > 0:
                action_fix_unreserve = self.env.ref(
                    'stock.stock_quant_stock_move_line_desynchronization',
                    raise_if_not_found=False)
                if action_fix_unreserve and self.user_has_groups(
                        'base.group_system'):
                    raise RedirectWarning(
                        _(
                            """It is not possible to unreserve more products of %s than you have in stock.
The correction could unreserve some operations with problematics products.""",
                            product_id.display_name), action_fix_unreserve.id,
                        _('Automated action to fix it'))
                else:
                    raise UserError(
                        _(
                            'It is not possible to unreserve more products of %s than you have in stock. Contact an administrator.',
                            product_id.display_name))
        else:
            return reserved_quants

        for quant in quants:
            if float_compare(quantity, 0, precision_rounding=rounding) > 0:
                max_quantity_on_quant = quant.quantity - quant.reserved_quantity
                if float_compare(max_quantity_on_quant,
                                 0,
                                 precision_rounding=rounding) <= 0:
                    continue
                max_quantity_on_quant = min(max_quantity_on_quant, quantity)
                quant.reserved_quantity += max_quantity_on_quant
                reserved_quants.append((quant, max_quantity_on_quant))
                quantity -= max_quantity_on_quant
                available_quantity -= max_quantity_on_quant
            else:
                max_quantity_on_quant = min(quant.reserved_quantity,
                                            abs(quantity))
                quant.reserved_quantity -= max_quantity_on_quant
                reserved_quants.append((quant, -max_quantity_on_quant))
                quantity += max_quantity_on_quant
                available_quantity += max_quantity_on_quant

            if float_is_zero(
                    quantity, precision_rounding=rounding) or float_is_zero(
                        available_quantity, precision_rounding=rounding):
                break
        return reserved_quants
Exemple #12
0
    def _import_facturx(self, tree, invoice):
        """ Decodes a factur-x invoice into an invoice.

        :param tree:    the factur-x tree to decode.
        :param invoice: the invoice to update or an empty recordset.
        :returns:       the invoice where the factur-x data was imported.
        """
        def _find_value(xpath, element=tree):
            return self._find_value(xpath, element, tree.nsmap)

        amount_total_import = None

        default_move_type = False
        if invoice._context.get('default_journal_id'):
            journal = self.env['account.journal'].browse(
                self.env.context['default_journal_id'])
            default_move_type = 'out_invoice' if journal.type == 'sale' else 'in_invoice'
        elif invoice._context.get('default_move_type'):
            default_move_type = self._context['default_move_type']
        elif invoice.move_type in self.env['account.move'].get_invoice_types(
                include_receipts=True):
            # in case an attachment is saved on a draft invoice previously created, we might
            # have lost the default value in context but the type was already set
            default_move_type = invoice.move_type

        if not default_move_type:
            raise UserError(
                _("No information about the journal or the type of invoice is passed"
                  ))
        if default_move_type == 'entry':
            return

        # Total amount.
        elements = tree.xpath('//ram:GrandTotalAmount', namespaces=tree.nsmap)
        total_amount = elements and float(elements[0].text) or 0.0

        # Refund type.
        # There is two modes to handle refund in Factur-X:
        # a) type_code == 380 for invoice, type_code == 381 for refund, all positive amounts.
        # b) type_code == 380, negative amounts in case of refund.
        # To handle both, we consider the 'a' mode and switch to 'b' if a negative amount is encountered.
        elements = tree.xpath('//rsm:ExchangedDocument/ram:TypeCode',
                              namespaces=tree.nsmap)
        type_code = elements[0].text

        default_move_type.replace('_refund', '_invoice')
        if type_code == '381':
            default_move_type = 'out_refund' if default_move_type == 'out_invoice' else 'in_refund'
            refund_sign = -1
        else:
            # Handle 'b' refund mode.
            if total_amount < 0:
                default_move_type = 'out_refund' if default_move_type == 'out_invoice' else 'in_refund'
            refund_sign = -1 if 'refund' in default_move_type else 1

        # Write the type as the journal entry is already created.
        invoice.move_type = default_move_type

        # self could be a single record (editing) or be empty (new).
        with Form(invoice.with_context(
                default_move_type=default_move_type)) as invoice_form:
            self_ctx = self.with_company(invoice.company_id)

            # Partner (first step to avoid warning 'Warning! You must first select a partner.').
            partner_type = invoice_form.journal_id.type == 'purchase' and 'SellerTradeParty' or 'BuyerTradeParty'
            invoice_form.partner_id = self_ctx._retrieve_partner(
                name=self._find_value('//ram:' + partner_type + '/ram:Name',
                                      tree,
                                      namespaces=tree.nsmap),
                mail=self._find_value('//ram:' + partner_type +
                                      '//ram:URIID[@schemeID=\'SMTP\']',
                                      tree,
                                      namespaces=tree.nsmap),
                vat=self._find_value('//ram:' + partner_type +
                                     '/ram:SpecifiedTaxRegistration/ram:ID',
                                     tree,
                                     namespaces=tree.nsmap),
            )

            # Reference.
            elements = tree.xpath('//rsm:ExchangedDocument/ram:ID',
                                  namespaces=tree.nsmap)
            if elements:
                invoice_form.ref = elements[0].text

            # Name.
            elements = tree.xpath(
                '//ram:BuyerOrderReferencedDocument/ram:IssuerAssignedID',
                namespaces=tree.nsmap)
            if elements:
                invoice_form.payment_reference = elements[0].text

            # Comment.
            elements = tree.xpath('//ram:IncludedNote/ram:Content',
                                  namespaces=tree.nsmap)
            if elements:
                invoice_form.narration = elements[0].text

            # Total amount.
            elements = tree.xpath('//ram:GrandTotalAmount',
                                  namespaces=tree.nsmap)
            if elements:

                # Currency.
                if elements[0].attrib.get('currencyID'):
                    currency_str = elements[0].attrib['currencyID']
                    currency = self.env.ref('base.%s' % currency_str.upper(),
                                            raise_if_not_found=False)
                    if currency and not currency.active:
                        error_msg = _(
                            'The currency (%s) of the document you are uploading is not active in this database.\n'
                            'Please activate it before trying again to import.',
                            currency.name)
                        error_action = {
                            'view_mode': 'form',
                            'res_model': 'res.currency',
                            'type': 'ir.actions.act_window',
                            'target': 'new',
                            'res_id': currency.id,
                            'views': [[False, 'form']]
                        }
                        raise RedirectWarning(error_msg, error_action,
                                              _('Display the currency'))
                    if currency != self.env.company.currency_id and currency.active:
                        invoice_form.currency_id = currency

                    # Store xml total amount.
                    amount_total_import = total_amount * refund_sign

            # Date.
            elements = tree.xpath(
                '//rsm:ExchangedDocument/ram:IssueDateTime/udt:DateTimeString',
                namespaces=tree.nsmap)
            if elements:
                date_str = elements[0].text
                date_obj = datetime.strptime(date_str,
                                             DEFAULT_FACTURX_DATE_FORMAT)
                invoice_form.invoice_date = date_obj.strftime(
                    DEFAULT_SERVER_DATE_FORMAT)

            # Due date.
            elements = tree.xpath(
                '//ram:SpecifiedTradePaymentTerms/ram:DueDateDateTime/udt:DateTimeString',
                namespaces=tree.nsmap)
            if elements:
                date_str = elements[0].text
                date_obj = datetime.strptime(date_str,
                                             DEFAULT_FACTURX_DATE_FORMAT)
                invoice_form.invoice_date_due = date_obj.strftime(
                    DEFAULT_SERVER_DATE_FORMAT)

            # Invoice lines.
            elements = tree.xpath('//ram:IncludedSupplyChainTradeLineItem',
                                  namespaces=tree.nsmap)
            if elements:
                for element in elements:
                    with invoice_form.invoice_line_ids.new(
                    ) as invoice_line_form:

                        # Sequence.
                        line_elements = element.xpath(
                            './/ram:AssociatedDocumentLineDocument/ram:LineID',
                            namespaces=tree.nsmap)
                        if line_elements:
                            invoice_line_form.sequence = int(
                                line_elements[0].text)

                        # Product.
                        name = _find_value(
                            './/ram:SpecifiedTradeProduct/ram:Name', element)
                        if name:
                            invoice_line_form.name = name
                        invoice_line_form.product_id = self_ctx._retrieve_product(
                            default_code=_find_value(
                                './/ram:SpecifiedTradeProduct/ram:SellerAssignedID',
                                element),
                            name=_find_value(
                                './/ram:SpecifiedTradeProduct/ram:Name',
                                element),
                            barcode=_find_value(
                                './/ram:SpecifiedTradeProduct/ram:GlobalID',
                                element))

                        # Quantity.
                        line_elements = element.xpath(
                            './/ram:SpecifiedLineTradeDelivery/ram:BilledQuantity',
                            namespaces=tree.nsmap)
                        if line_elements:
                            invoice_line_form.quantity = float(
                                line_elements[0].text)

                        # Price Unit.
                        line_elements = element.xpath(
                            './/ram:GrossPriceProductTradePrice/ram:ChargeAmount',
                            namespaces=tree.nsmap)
                        if line_elements:
                            quantity_elements = element.xpath(
                                './/ram:GrossPriceProductTradePrice/ram:BasisQuantity',
                                namespaces=tree.nsmap)
                            if quantity_elements:
                                invoice_line_form.price_unit = float(
                                    line_elements[0].text) / float(
                                        quantity_elements[0].text)
                            else:
                                invoice_line_form.price_unit = float(
                                    line_elements[0].text)
                        else:
                            line_elements = element.xpath(
                                './/ram:NetPriceProductTradePrice/ram:ChargeAmount',
                                namespaces=tree.nsmap)
                            if line_elements:
                                quantity_elements = element.xpath(
                                    './/ram:NetPriceProductTradePrice/ram:BasisQuantity',
                                    namespaces=tree.nsmap)
                                if quantity_elements:
                                    invoice_line_form.price_unit = float(
                                        line_elements[0].text) / float(
                                            quantity_elements[0].text)
                                else:
                                    invoice_line_form.price_unit = float(
                                        line_elements[0].text)
                        # Discount.
                        line_elements = element.xpath(
                            './/ram:AppliedTradeAllowanceCharge/ram:CalculationPercent',
                            namespaces=tree.nsmap)
                        if line_elements:
                            invoice_line_form.discount = float(
                                line_elements[0].text)

                        # Taxes
                        tax_element = element.xpath(
                            './/ram:SpecifiedLineTradeSettlement/ram:ApplicableTradeTax/ram:RateApplicablePercent',
                            namespaces=tree.nsmap)
                        invoice_line_form.tax_ids.clear()
                        for eline in tax_element:
                            tax = self_ctx._retrieve_tax(
                                amount=eline.text,
                                type_tax_use=invoice_form.journal_id.type)
                            if tax:
                                invoice_line_form.tax_ids.add(tax)
            elif amount_total_import:
                # No lines in BASICWL.
                with invoice_form.invoice_line_ids.new() as invoice_line_form:
                    invoice_line_form.name = invoice_form.comment or '/'
                    invoice_line_form.quantity = 1
                    invoice_line_form.price_unit = amount_total_import

        return invoice_form.save()