def _mx_pac_edi_export_invoice_cfdi(self, invoice):
        ''' Create the CFDI attachment for the invoice passed as parameter.

        :param move:    An account.move record.
        :return:        A dictionary with one of the following key:
        * cfdi_str:     A string of the unsigned cfdi of the invoice.
        * error:        An error if the cfdi was not successfuly generated.
        '''

        # == CFDI values ==
        cfdi_values = self._mx_pac_edi_get_invoice_cfdi_values(invoice)

        # == Generate the CFDI ==
        cfdi = self.env.ref('mx_pac_edi.cfdiv33')._render(cfdi_values)
        decoded_cfdi_values = invoice._mx_pac_edi_decode_cfdi(cfdi_data=cfdi)
        cfdi_cadena_crypted = cfdi_values['certificate'].sudo().get_encrypted_cadena(decoded_cfdi_values['cadena'])
        decoded_cfdi_values['cfdi_node'].attrib['Sello'] = cfdi_cadena_crypted

        # == Optional check using the XSD ==
        xsd_attachment = self.env.ref('mx_pac_edi.xsd_cached_cfdv33_xsd', False)
        xsd_datas = base64.b64decode(xsd_attachment.datas) if xsd_attachment else None

        if xsd_datas:
            try:
                with BytesIO(xsd_datas) as xsd:
                    _check_with_xsd(decoded_cfdi_values['cfdi_node'], xsd)
            except (IOError, ValueError):
                _logger.info(_('The xsd file to validate the XML structure was not found'))
            except Exception as e:
                return {'errors': str(e).split('\\n')}

        return {
            'cfdi_str': etree.tostring(decoded_cfdi_values['cfdi_node'], pretty_print=True, xml_declaration=True, encoding='UTF-8'),
        }
Example #2
0
 def get_xml(self, options):
     """
     Generate the IRAS Audit File in xml format
     """
     qweb = self.env['ir.qweb']
     values = self._get_generic_data(options['date_from'], options['date_to'])
     doc = qweb.render(IRAS_XML_TEMPLATE, values=values)
     with tools.file_open(IRAS_XSD, 'rb') as xsd:
         _check_with_xsd(doc, xsd)
     tree = fromstring(doc)
     return etree.tostring(tree, pretty_print=True, xml_declaration=True, encoding='UTF-8')
Example #3
0
    def get_xml(self, options):
        qweb = self.env['ir.qweb']
        version = '1.3'
        ctx = self._set_context(options)
        values = self.with_context(ctx)._get_coa_dict(options)
        cfdicoa = qweb.render(CFDICOA_TEMPLATE, values=values)
        for key, value in MX_NS_REFACTORING.items():
            cfdicoa = cfdicoa.replace(key.encode('UTF-8'),
                                      value.encode('UTF-8') + b':')
        cfdicoa = self._l10n_mx_edi_add_digital_stamp(
            CFDICOA_XSLT_CADENA % version, cfdicoa)

        with tools.file_open(CFDICOA_XSD % version, "rb") as xsd:
            _check_with_xsd(cfdicoa, xsd)
        return cfdicoa
    def get_xml(self, options):
        qweb = self.env['ir.qweb']
        version = '1.3'
        ctx = self.set_context(options)
        if not ctx.get('date_to'):
            return False
        ctx['no_format'] = True
        values = self.with_context(ctx).get_bce_dict(options)
        cfdicoa = qweb.render(CFDIBCE_TEMPLATE, values=values)
        for key, value in MX_NS_REFACTORING.items():
            cfdicoa = cfdicoa.replace(key.encode('UTF-8'),
                                      value.encode('UTF-8') + b':')
        cfdicoa = self.l10n_mx_edi_add_digital_stamp(
            CFDIBCE_XSLT_CADENA % version, cfdicoa)

        with tools.file_open(CFDIBCE_XSD % version, "rb") as xsd:
            _check_with_xsd(cfdicoa, xsd)
        return cfdicoa
Example #5
0
    def l10n_mx_edi_is_cfdi33(self):
        self.ensure_one()
        if not self.datas:
            return False
        try:
            datas = base64.b64decode(self.datas).replace(
                b'xmlns:schemaLocation', b'xsi:schemaLocation')
            cfdi = objectify.fromstring(datas)
        except (SyntaxError, ValueError):
            return False

        attachment = self.env.ref('l10n_mx_edi.xsd_cached_cfdv33_xsd', False)
        schema = base64.b64decode(attachment.datas) if attachment else b''
        try:
            if cfdi.get('Version') != '3.3':
                return False
            if hasattr(cfdi, 'Addenda'):
                cfdi.remove(cfdi.Addenda)
            if not hasattr(cfdi, 'Complemento'):
                return False
            if not attachment:
                return cfdi
            attribute = 'registrofiscal:CFDIRegistroFiscal'
            namespace = {
                'registrofiscal': 'http://www.sat.gob.mx/registrofiscal'
            }
            node = cfdi.Complemento.xpath(attribute, namespaces=namespace)
            if node:
                cfdi.Complemento.remove(node[0])
            with BytesIO(schema) as xsd:
                _check_with_xsd(cfdi, xsd)
            return cfdi
        except ValueError:
            return False
        except IOError:
            return False
        except UserError:
            return False
        except etree.XMLSyntaxError:
            return False
        return False
Example #6
0
 def get_xml(self, options):
     qweb = self.env['ir.qweb']
     version = '1.3'
     ctx = self._set_context(options)
     if not ctx.get('date_to'):
         return False
     ctx['no_format'] = True
     ctx['print_mode'] = True
     values = self.with_context(ctx).get_bce_dict(options)
     values.update({
         'request_type': options.get('request_type'),
         'order_number': options.get('order_number') or False,
         'process_number': options.get('process_number') or False,
     })
     cfdimoves = qweb.render(CFDIPLZ_TEMPLATE, values=values)
     for key, value in MX_NS_REFACTORING.items():
         cfdimoves = cfdimoves.replace(key.encode('UTF-8'),
                                       value.encode('UTF-8') + b':')
     cfdimoves = self.l10n_mx_edi_add_digital_stamp(
         CFDIPLZ_XSLT % version, cfdimoves)
     with tools.file_open(CFDIPLZ_XSD % version, "rb") as xsd:
         _check_with_xsd(cfdimoves, xsd)
     return cfdimoves
    def test_lxml_import_from_filestore(self):
        resolver_schema_int = b"""
            <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                        xmlns:etype="http://codespeak.net/lxml/test/external">
                <xsd:import namespace="http://codespeak.net/lxml/test/external" schemaLocation="imported_schema.xsd"/>
                <xsd:element name="a" type="etype:AType"/>
            </xsd:schema>
        """

        incomplete_schema_int = b"""
            <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                        xmlns:etype="http://codespeak.net/lxml/test/external">
                <xsd:import namespace="http://codespeak.net/lxml/test/external" schemaLocation="non_existing_schema.xsd"/>
                <xsd:element name="a" type="etype:AType"/>
            </xsd:schema>
        """

        imported_schema = b"""
            <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                        targetNamespace="http://codespeak.net/lxml/test/external">
                <xsd:complexType name="AType">
                    <xsd:sequence><xsd:element name="b" type="xsd:string" minOccurs="0" maxOccurs="unbounded"/></xsd:sequence>
                </xsd:complexType>
            </xsd:schema>
        """

        self.env['ir.attachment'].create([{
            'datas':
            base64.b64encode(resolver_schema_int),
            'name':
            'resolver_schema_int.xsd'
        }, {
            'datas':
            base64.b64encode(incomplete_schema_int),
            'name':
            'incomplete_schema_int.xsd'
        }, {
            'datas':
            base64.b64encode(imported_schema),
            'name':
            'imported_schema.xsd'
        }])

        _check_with_xsd("<a><b></b></a>", 'resolver_schema_int.xsd', self.env)

        with self.assertRaises(XMLSchemaError):
            _check_with_xsd("<a><b></b></a>", 'incomplete_schema_int.xsd',
                            self.env)

        with self.assertRaises(FileNotFoundError):
            _check_with_xsd("<a><b></b></a>", 'non_existing_schema.xsd',
                            self.env)
Example #8
0
    def _l10n_mx_edi_create_cfdi(self):
        """Creates and returns a dictionnary containing 'cfdi' if the cfdi is
        well created, 'error' otherwise."""
        if not self:
            return {}
        qweb = self.env['ir.qweb']
        invoice_obj = self.env['account.invoice']
        company_id = self.mapped('company_id')
        error_log = []
        pac_name = company_id.l10n_mx_edi_pac

        values = self._l10n_mx_edi_create_cfdi_values()

        # -Check certificate
        certificate_ids = company_id.l10n_mx_edi_certificate_ids
        certificate_id = certificate_ids.sudo().get_valid_certificate()
        if not certificate_id:
            error_log.append(_('No valid certificate found'))

        # -Check PAC
        if pac_name:
            pac_test_env = company_id.l10n_mx_edi_pac_test_env
            pac_password = company_id.l10n_mx_edi_pac_password
            if not pac_test_env and not pac_password:
                error_log.append(_('No PAC credentials specified.'))
        else:
            error_log.append(_('No PAC specified.'))

        if error_log:
            return {
                'error':
                _('Please check your configuration: ') +
                create_list_html(error_log)
            }

        values['certificate_number'] = certificate_id.serial_number
        values['certificate'] = certificate_id.sudo().get_data()[0]
        values['date'] = (certificate_id.sudo().get_mx_current_datetime().
                          strftime('%Y-%m-%dT%H:%M:%S'))

        cfdi = qweb.render(CFDI_TEMPLATE_33, values=values)
        attachment = self.env.ref('l10n_mx_edi.xsd_cached_cfdv33_xsd', False)
        xsd_datas = base64.b64decode(attachment.datas) if attachment else b''
        # -Compute cadena
        tree = objectify.fromstring(cfdi)
        cadena = invoice_obj.l10n_mx_edi_generate_cadena(
            CFDI_XSLT_CADENA, tree)
        tree.attrib['Sello'] = certificate_id.sudo().get_encrypted_cadena(
            cadena)

        # Check with xsd
        if xsd_datas:
            try:
                with BytesIO(xsd_datas) as xsd:
                    _check_with_xsd(tree, xsd)
            except (IOError, ValueError):
                _logger.info(
                    _('The xsd file to validate the XML structure '
                      'was not found'))
            except BaseException as e:
                return {
                    'error': (_('The cfdi generated is not valid') +
                              create_list_html(str(e).split('\\n')))
                }

        return {
            'cfdi':
            etree.tostring(tree,
                           pretty_print=True,
                           xml_declaration=True,
                           encoding='UTF-8')
        }
Example #9
0
    def _l10n_mx_edi_create_cfdi(self):
        '''Creates and returns a dictionnary containing 'cfdi' if the cfdi is well created, 'error' otherwise.
        '''
        self.ensure_one()
        qweb = self.env['ir.qweb']
        error_log = []
        company_id = self.company_id
        pac_name = company_id.l10n_mx_edi_pac
        values = self._l10n_mx_edi_create_cfdi_values()

        # -----------------------
        # Check the configuration
        # -----------------------
        # -Check certificate
        certificate_ids = company_id.l10n_mx_edi_certificate_ids
        certificate_id = certificate_ids.sudo().get_valid_certificate()
        if not certificate_id:
            error_log.append(_('No valid certificate found'))

        # -Check PAC
        if pac_name:
            pac_test_env = company_id.l10n_mx_edi_pac_test_env
            pac_username = company_id.l10n_mx_edi_pac_username
            pac_password = company_id.l10n_mx_edi_pac_password
            if not pac_test_env and not (pac_username and pac_password):
                error_log.append(_('No PAC credentials specified.'))
        else:
            error_log.append(_('No PAC specified.'))

        if error_log:
            return {'error': _('Please check your configuration: ') + create_list_html(error_log)}

        # -Compute date and time of the invoice
        time_invoice = datetime.strptime(
            self.l10n_mx_edi_time_invoice, DEFAULT_SERVER_TIME_FORMAT).time()
        # -----------------------
        # Create the EDI document
        # -----------------------
        version = self.l10n_mx_edi_get_pac_version()

        # -Compute certificate data
        values['date'] = datetime.combine(
            fields.Datetime.from_string(self.date_invoice), time_invoice).strftime('%Y-%m-%dT%H:%M:%S')
        values['certificate_number'] = certificate_id.serial_number
        values['certificate'] = certificate_id.sudo().get_data()[0]

        # -Compute cfdi
        if version == '3.2':
            cfdi = qweb.render(CFDI_TEMPLATE, values=values)
            node_sello = 'sello'
            with tools.file_open('l10n_mx_edi/data/%s/cfdi.xsd' % version, 'rb') as xsd:
                xsd_datas = xsd.read()
        elif version == '3.3':
            cfdi = qweb.render(CFDI_TEMPLATE_33, values=values)
            node_sello = 'Sello'
            attachment = self.env.ref('l10n_mx_edi.xsd_cached_cfdv33_xsd', False)
            xsd_datas = base64.b64decode(attachment.datas) if attachment else b''
        else:
            return {'error': _('Unsupported version %s') % version}

        # -Compute cadena
        tree = self.l10n_mx_edi_get_xml_etree(cfdi)
        cadena = self.l10n_mx_edi_generate_cadena(CFDI_XSLT_CADENA % version, tree)
        tree.attrib[node_sello] = certificate_id.sudo().get_encrypted_cadena(cadena)

        # Check with xsd
        if xsd_datas:
            try:
                with BytesIO(xsd_datas) as xsd:
                    _check_with_xsd(tree, xsd)
            except (IOError, ValueError):
                _logger.info(
                    _('The xsd file to validate the XML structure was not found'))
            except Exception as e:
                return {'error': (_('The cfdi generated is not valid') +
                                    create_list_html(str(e).split('\\n')))}

        return {'cfdi': etree.tostring(tree, pretty_print=True, xml_declaration=True, encoding='UTF-8')}
    def _l10n_mx_edi_create_cfdi(self):
        self.ensure_one()
        if not self.use_addenda:
            return super(AccountMove, self)._l10n_mx_edi_create_cfdi()
        else:
            qweb = self.env['ir.qweb']
            error_log = []
            company_id = self.company_id
            pac_name = company_id.l10n_mx_edi_pac
            if self.l10n_mx_edi_external_trade:
                # Call the onchange to obtain the values of l10n_mx_edi_qty_umt
                # and l10n_mx_edi_price_unit_umt, this is necessary when the
                # invoice is created from the sales order or from the picking
                self.invoice_line_ids.onchange_quantity()
                self.invoice_line_ids._set_price_unit_umt()
            values = self._l10n_mx_edi_create_cfdi_values()

            # -----------------------
            # Check the configuration
            # -----------------------
            # -Check certificate
            certificate_ids = company_id.l10n_mx_edi_certificate_ids
            certificate_id = certificate_ids.sudo().get_valid_certificate()
            if not certificate_id:
                error_log.append(_('No valid certificate found'))

            # -Check PAC
            if pac_name:
                pac_test_env = company_id.l10n_mx_edi_pac_test_env
                pac_password = company_id.l10n_mx_edi_pac_password
                if not pac_test_env and not pac_password:
                    error_log.append(_('No PAC credentials specified.'))
            else:
                error_log.append(_('No PAC specified.'))

            if error_log:
                return {
                    'error':
                    _('Please check your configuration: ') +
                    create_list_html(error_log)
                }

            # -Compute date and time of the invoice
            time_invoice = datetime.strptime(
                self.l10n_mx_edi_time_invoice,
                DEFAULT_SERVER_TIME_FORMAT).time()
            # -----------------------
            # Create the EDI document
            # -----------------------
            version = self.l10n_mx_edi_get_pac_version()

            # -Compute certificate data
            values['date'] = datetime.combine(
                fields.Datetime.from_string(self.invoice_date),
                time_invoice).strftime('%Y-%m-%dT%H:%M:%S')
            values['certificate_number'] = certificate_id.serial_number
            values['certificate'] = certificate_id.sudo().get_data()[0]

            # -Compute cfdi
            cfdi = qweb.render(CFDI_TEMPLATE_33, values=values)
            cfdi = cfdi.replace(b'xmlns__', b'xmlns:')
            #Patch for Finkok specifications
            cfdi_str = cfdi.decode('utf-8')
            cfdi_str = cfdi_str.replace(
                'xmlns:detallista="http://www.sat.gob.mx/detallista"', '')
            cfdi_str = cfdi_str.replace(
                '<cfdi:Comprobante',
                '<cfdi:Comprobante xmlns:detallista="http://www.sat.gob.mx/detallista" '
            )
            cfdi = cfdi_str.encode('utf-8')
            #End patch
            node_sello = 'Sello'
            attachment = self.env.ref('l10n_mx_edi.xsd_cached_cfdv33_xsd',
                                      False)
            xsd_datas = base64.b64decode(
                attachment.datas) if attachment else b''
            # -Compute cadena
            tree = self.l10n_mx_edi_get_xml_etree(cfdi)
            cadena = self.l10n_mx_edi_generate_cadena(
                CFDI_XSLT_CADENA % version, tree)
            tree.attrib[node_sello] = certificate_id.sudo(
            ).get_encrypted_cadena(cadena)

            # Check with xsd
            if xsd_datas:
                try:
                    with BytesIO(xsd_datas) as xsd:
                        _check_with_xsd(tree, xsd)
                except (IOError, ValueError):
                    _logger.info(
                        _('The xsd file to validate the XML structure was not found'
                          ))
                except Exception as e:
                    return {
                        'error': (_('The cfdi generated is not valid') +
                                  create_list_html(str(e).split('\\n')))
                    }
            return {
                'cfdi':
                etree.tostring(tree,
                               pretty_print=True,
                               xml_declaration=True,
                               encoding='UTF-8')
            }
    def get_xaf(self, options):
        def cust_sup_tp(partner_id):
            so_count = partner_id.sale_order_count
            po_count = partner_id.purchase_order_count
            if so_count and po_count:
                return 'B'
            if so_count:
                return 'C'
            if po_count:
                return 'S'
            return 'O'

        def acc_tp(account_id):
            if account_id.user_type_id.type in ['income', 'expense']:
                return 'P'
            if account_id.user_type_id.type in ['asset', 'liability']:
                return 'B'
            return 'M'

        def jrn_tp(journal_id):
            if journal_id.type == 'bank':
                return 'B'
            if journal_id.type == 'cash':
                return 'C'
            if journal_id.type == 'situation':
                return 'O'
            if journal_id.type in ['sale', 'sale_refund']:
                return 'S'
            if journal_id.type in ['purchase', 'purchase_refund']:
                return 'P'
            return 'Z'

        def amnt_tp(move_line_id):
            return 'C' if move_line_id.credit else 'D'

        def compute_period_number(date_str):
            date = fields.Date.from_string(date_str)
            return date.strftime('%y%m')[1:]

        def change_date_time(record):
            return record.write_date.strftime('%Y-%m-%dT%H:%M:%S')

        company_id = self.env.company

        msgs = []

        if not company_id.vat:
            msgs.append(_('- VAT number'))
        if not company_id.country_id:
            msgs.append(_('- Country'))

        if msgs:
            msgs = [_('Some fields must be specified on the company:')] + msgs
            raise UserError('\n'.join(msgs))

        date_from = options['date']['date_from']
        date_to = options['date']['date_to']
        partner_ids = self.env['res.partner'].search([
            '|', ('company_id', '=', False), ('company_id', '=', company_id.id)
        ])
        account_ids = self.env['account.account'].search([('company_id', '=',
                                                           company_id.id)])
        tax_ids = self.env['account.tax'].search([('company_id', '=',
                                                   company_id.id)])
        journal_ids = self.env['account.journal'].search([('company_id', '=',
                                                           company_id.id)])
        # Retrieve periods values
        periods = []
        Period = namedtuple('Period', 'number name date_from date_to')
        for period in rrule(freq=MONTHLY,
                            bymonth=(),
                            dtstart=fields.Date.from_string(date_from),
                            until=fields.Date.from_string(date_to)):
            period_from = fields.Date.to_string(period.date())
            period_to = period.replace(
                day=calendar.monthrange(period.year, period.month)[1])
            period_to = fields.Date.to_string(period_to.date())
            periods.append(
                Period(number=compute_period_number(period_from),
                       name=period.strftime('%B') + ' ' + date_from[0:4],
                       date_from=period_from,
                       date_to=period_to))
        # Retrieve move lines values
        total_query = """
            SELECT COUNT(*), SUM(l.debit), SUM(l.credit)
            FROM account_move_line l, account_move m
            WHERE l.move_id = m.id
            AND l.date >= %s
            AND l.date <= %s
            AND l.company_id = %s
            AND m.state != 'draft'
        """
        self.env.cr.execute(total_query, (
            date_from,
            date_to,
            company_id.id,
        ))
        moves_count, moves_debit, moves_credit = self.env.cr.fetchall()[0]
        journal_x_moves = {}
        for journal in journal_ids:
            journal_x_moves[journal] = self.env['account.move'].search([
                ('date', '>=', date_from), ('date', '<=', date_to),
                ('state', '!=', 'draft'), ('journal_id', '=', journal.id)
            ])
        values = {
            'company_id': company_id,
            'partner_ids': partner_ids,
            'account_ids': account_ids,
            'journal_ids': journal_ids,
            'journal_x_moves': journal_x_moves,
            'compute_period_number': compute_period_number,
            'periods': periods,
            'tax_ids': tax_ids,
            'cust_sup_tp': cust_sup_tp,
            'acc_tp': acc_tp,
            'jrn_tp': jrn_tp,
            'amnt_tp': amnt_tp,
            'change_date_time': change_date_time,
            'fiscal_year': date_from[0:4],
            'date_from': date_from,
            'date_to': date_to,
            'date_created': fields.Date.context_today(self),
            'software_version': release.version,
            'moves_count': moves_count,
            'moves_debit': moves_debit or 0.0,
            'moves_credit': moves_credit or 0.0,
        }
        audit_content = self.env['ir.qweb'].render(
            'l10n_nl_reports.xaf_audit_file', values)
        with tools.file_open('l10n_nl_reports/data/xml_audit_file_3_2.xsd',
                             'rb') as xsd:
            _check_with_xsd(audit_content, xsd)
        return audit_content