Esempio n. 1
0
    def process_xml_file(self, refresh=True):

        try:
            xml = fromstring(base64.b64decode(self.xml_file))
            self.xml_content = etree.tostring(xml, pretty_print=True)
        except Exception:
            raise ValidationError('Unable to parse XML file')

        self.version = xml.attrib.get('Version')
        self.name = "{}/{}".format(xml.attrib.get('Serie'),
                                   xml.attrib.get('Folio')) if xml.attrib.get(
                                       'Serie') else xml.attrib.get('Folio')
        self.date_invoice = datetime.strptime(
            xml.attrib.get('Fecha'),
            '%Y-%m-%dT%H:%M:%S') if xml.attrib.get('Fecha') else False
        self.currency_code = xml.attrib.get('Moneda')
        self.exchange_rate = xml.attrib.get('TipoCambio', 1)
        self.l10n_mx_edi_cfdi_amount = float(
            xml.attrib.get('Total', xml.attrib.get('total', 0)))

        self.invoice_type = 'in_invoice' if xml.attrib.get(
            'TipoDeComprobante', 'I').upper() == 'I' else 'in_refund'

        self.amount_untaxed = float(
            xml.attrib.get('SubTotal', xml.attrib.get('subTotal', 0)))
        self.amount_total = float(
            xml.attrib.get('Total', xml.attrib.get('total', 0)))

        taxes_section = getattr(xml, 'Impuestos', False)
        if isinstance(taxes_section, ObjectifiedElement):
            self.amount_tax = float(
                taxes_section.attrib.get('TotalImpuestosTrasladados', 0))

        self.payment_term_name = xml.attrib.get('CondicionesDePago') or ''

        self.l10n_mx_edi_cfdi_supplier_name = xml.Receptor.attrib.get(
            'Nombre', xml.Receptor.attrib.get('nombre'))
        self.l10n_mx_edi_cfdi_supplier_rfc = xml.Receptor.attrib.get(
            'Rfc', xml.Receptor.attrib.get('rfc'))
        self.l10n_mx_edi_cfdi_customer_name = xml.Emisor.attrib.get(
            'Nombre', xml.Emisor.attrib.get('nombre'))
        self.l10n_mx_edi_cfdi_customer_rfc = xml.Emisor.attrib.get(
            'Rfc', xml.Emisor.attrib.get('rfc'))

        self.l10n_mx_edi_usage = xml.Receptor.attrib.get('UsoCFDI')

        complemento_section = getattr(xml, 'Complemento', False)

        timbre = complemento_section.find(
            'tfd:TimbreFiscalDigital',
            EDI_NAMESPACES) if complemento_section else False

        if isinstance(timbre, StringElement):
            self.l10n_mx_edi_cfdi_uuid = timbre.get('UUID')

            if self.l10n_mx_edi_cfdi_uuid:
                self.l10n_mx_edi_pac_status = 'signed'
                self.l10n_mx_edi_sat_status = 'valid'

        concepts_section = getattr(xml, 'Conceptos', False)

        if refresh is False and isinstance(concepts_section,
                                           ObjectifiedElement):
            AccountTax = self.env['account.tax']

            lines = []
            for i in range(concepts_section.countchildren()):
                item = concepts_section.Concepto[i]
                line = self._get_invoice_line_from_xml(item)

                lines.append((0, 0, line))

            self.line_ids = lines

            if taxes_section:
                tax_lines = []
                transfers_section = getattr(taxes_section, 'Traslados', False)

                if isinstance(transfers_section, ObjectifiedElement):

                    for i in range(transfers_section.countchildren()):
                        item = transfers_section.Traslado[i]
                        importe = float(
                            item.attrib.get('Importe',
                                            item.attrib.get('importe', 0)))
                        impuesto = item.attrib.get(
                            'Impuesto', item.attrib.get('impuesto', False))
                        tasa = float(
                            item.attrib.get('TasaOCuota',
                                            item.attrib.get('tasa', 0))) * 100
                        tax_item = AccountTax.search(
                            [('amount', '=', tasa),
                             ('type_tax_use', '=', 'purchase')],
                            limit=1)

                        if tax_item.id:
                            line = {
                                'name': tax_item.name,
                                'tax_id': tax_item.id,
                                'account_id': tax_item.account_id.id,
                                'currency_id': self.currency_id,
                                'company_id':
                                self.env.user.partner_id.company_id.id,
                                'amount': importe,
                                'base': self.amount_untaxed,
                                'manual': True,
                            }

                            tax_lines.append((0, 0, line))

                    self.tax_line_ids = tax_lines

        if self.invoice_type == 'in_refund':
            # cfdi: CfdiRelacionados
            related_section = getattr(xml, 'CfdiRelacionados', False)

            if isinstance(related_section, ObjectifiedElement):
                self.l10n_mx_edi_redunded_invoice_cfdi_uuid = related_section.CfdiRelacionado.attrib.get(
                    'UUID')

                if not self.refund_invoice_id.id:
                    raise MissingRelatedUUIDError(
                        _('Missing Related UUID %s in XML') %
                        (self.l10n_mx_edi_redunded_invoice_cfdi_uuid, ),
                        uuid=self.l10n_mx_edi_redunded_invoice_cfdi_uuid)

        if self.l10n_mx_edi_cfdi_uuid:
            uuid_search = self.env['account.invoice'].sudo().search([
                ('l10n_mx_cfdi_uuid', '=', self.l10n_mx_edi_cfdi_uuid)
            ])
            if uuid_search:
                raise DuplicateUUIDError(_('Duplicated UUID %s') %
                                         (self.l10n_mx_edi_cfdi_uuid, ),
                                         uuid=self.l10n_mx_edi_cfdi_uuid)

        if not self.company_id:
            raise MissingCompanyError(
                _('Unable to find company %s with RFC %s') %
                (self.l10n_mx_edi_cfdi_supplier_name,
                 self.l10n_mx_edi_cfdi_supplier_rfc),
                name=self.l10n_mx_edi_cfdi_supplier_name,
                rfc=self.l10n_mx_edi_cfdi_supplier_rfc)

        # if self.company_id.id != self.env.user.partner_id.company_id.id:
        if self.company_id.id not in self.env.user.company_ids.mapped('id') + [
                self.env.user.partner_id.company_id.id
        ]:
            raise InvalidCompanyError(_(
                'Unable to process XML from company other than "%s" with RFC "%s". Invoice RFC: "%s".'
            ) % (self.env.user.partner_id.company_id.name,
                 self.env.user.partner_id.company_id.vat,
                 self.company_id.partner_id.vat),
                                      user_company=self.env.user.partner_id.
                                      company_id,
                                      invoice_company=self.company_id)

        if not self.partner_id:
            raise MissingPartnerError(
                _('Unable to find client "%s" with RFC "%s".') %
                (self.l10n_mx_edi_cfdi_customer_name,
                 self.l10n_mx_edi_cfdi_customer_rfc),
                name=self.l10n_mx_edi_cfdi_customer_name,
                rfc=self.l10n_mx_edi_cfdi_customer_rfc)

        if not self.currency_id:
            raise InvalidCurrencyError(_('Unable to find currency "%s".') %
                                       (self.currency_code, ),
                                       currency_code=self.currency_code)

        invalid_lines = filter(lambda p: not p.product_id, self.line_ids)

        if len(list(invalid_lines)):
            raise InvalidProductLinesError(
                _('Some invoice lines doesnt have a valid product associated.')
            )

        return True
    def process_xml_file(self):
        try:
            xml = fromstring(base64.b64decode(self.xml_file))
            self.xml_content = etree.tostring(xml, pretty_print=True)
        except:
            raise ValidationError('Unable to parse XML file')

        self.version = xml.attrib.get('Version')
        self.name = xml.attrib.get('Folio')
        self.date_invoice = datetime.strptime(
            xml.attrib.get('Fecha'),
            '%Y-%m-%dT%H:%M:%S') if xml.attrib.get('Fecha') else False
        self.currency_code = xml.attrib.get('Moneda')
        self.exchange_rate = xml.attrib.get('TipoCambio', 1)
        self.l10n_mx_edi_cfdi_amount = float(
            xml.attrib.get('Total', xml.attrib.get('total', 0)))

        self.amount_untaxed = float(
            xml.attrib.get('SubTotal', xml.attrib.get('subTotal', 0)))
        self.amount_total = float(
            xml.attrib.get('Total', xml.attrib.get('total', 0)))

        taxes_section = getattr(xml, 'Impuestos', False)
        if taxes_section:
            self.amount_tax = float(
                taxes_section.attrib.get('TotalImpuestosTrasladados', 0))

        self.payment_term_name = xml.attrib.get('CondicionesDePago') or ''

        self.l10n_mx_edi_cfdi_supplier_name = xml.Emisor.attrib.get(
            'Nombre', xml.Emisor.attrib.get('nombre'))
        self.l10n_mx_edi_cfdi_supplier_rfc = xml.Emisor.attrib.get(
            'Rfc', xml.Emisor.attrib.get('rfc'))
        self.l10n_mx_edi_cfdi_customer_name = xml.Receptor.attrib.get(
            'Nombre', xml.Receptor.attrib.get('nombre'))
        self.l10n_mx_edi_cfdi_customer_rfc = xml.Receptor.attrib.get(
            'Rfc', xml.Receptor.attrib.get('rfc'))
        self.l10n_mx_edi_usage = xml.Receptor.attrib.get('UsoCFDI')

        if not self.company_id:
            raise UserError(
                _('Unable to find company %s with RFC %s') %
                (self.l10n_mx_edi_cfdi_supplier_name,
                 self.l10n_mx_edi_cfdi_supplier_rfc))

        if self.company_id.id != self.env.user.partner_id.company_id.id:
            raise UserError(
                _('Unable to process XML from company other than "%s" with RFC "%s". Invoice RFC: "%s".'
                  ) %
                (self.env.user.partner_id.company_id.name,
                 self.env.user.partner_id.company_id.vat, self.company_id.vat))

        if not self.partner_id:
            raise UserError(
                _('Unable to find client "%s" with RFC "%s".') %
                (self.l10n_mx_edi_cfdi_customer_name,
                 self.l10n_mx_edi_cfdi_customer_rfc))

        complemento_section = getattr(xml, 'Complemento', False)

        timbre = complemento_section.find(
            'tfd:TimbreFiscalDigital',
            EDI_NAMESPACES) if complemento_section else False

        if timbre is not None:
            self.l10n_mx_edi_cfdi_uuid = timbre.get('UUID')

            if self.l10n_mx_edi_cfdi_uuid:
                self.l10n_mx_edi_pac_status = 'signed'
                self.l10n_mx_edi_sat_status = 'valid'

        concepts_section = getattr(xml, 'Conceptos', False)

        if concepts_section:

            taxes = {}

            lines = []
            for i in range(concepts_section.countchildren()):
                item = concepts_section.Concepto[i]
                line = self._get_invoice_line_from_xml(item)

                tax_info = line.pop('tax_lines')

                for tline in tax_info:
                    tax_item = tline['tax']

                    if tax_item.id not in taxes:
                        taxes[tax_item.id] = {
                            'name': tax_item.name,
                            'tax_id': tax_item.id,
                            'account_id': tax_item.account_id.id,
                            'currency_id': self.currency_id,
                            'company_id':
                            self.env.user.partner_id.company_id.id,
                            'amount': tline['amount'],
                            'base': self.amount_untaxed,
                            'manual': True,
                        }
                    else:
                        taxes[tline['tax'].id]['amount'] += tline['amount']

                lines.append((0, 0, line))

            self.line_ids = lines
            self.tax_line_ids = [(0, 0, item) for item in taxes.values()]

        return True