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