mas pruebas y algunos pequenos cambios
FossilOrigin-Name: 02bc90719bb17216a749568086887a7d27878fdbd81febfb88a9fb1e68aa8205
This commit is contained in:
		| @@ -257,6 +257,9 @@ class FachoXML: | |||||||
|     def get_element_text(self, xpath, format_=str): |     def get_element_text(self, xpath, format_=str): | ||||||
|         xpath = self.fragment_prefix + self._path_xpath_for(xpath) |         xpath = self.fragment_prefix + self._path_xpath_for(xpath) | ||||||
|         elem = self.builder.xpath(self.root, xpath) |         elem = self.builder.xpath(self.root, xpath) | ||||||
|  |         if elem is None: | ||||||
|  |             raise ValueError('xpath %s invalid' % (xpath)) | ||||||
|  |  | ||||||
|         text = self.builder.get_text(elem) |         text = self.builder.get_text(elem) | ||||||
|         return format_(text) |         return format_(text) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -227,15 +227,18 @@ class LegalMonetaryTotal(model.Model): | |||||||
|         self.payable_amount = self.tax_inclusive_amount + self.charge_total_amount |         self.payable_amount = self.tax_inclusive_amount + self.charge_total_amount | ||||||
|  |  | ||||||
|  |  | ||||||
| class DIANExtension(model.Model): | class DIANExtensionContent(model.Model): | ||||||
|     __name__ = 'UBLExtension' |     __name__ = 'ExtensionContent' | ||||||
|  |  | ||||||
|     _content = fields.Many2One(Element, name='ExtensionContent', namespace='ext') |  | ||||||
|      |      | ||||||
|     dian = fields.Many2One(dian.DianExtensions, name='DianExtensions', namespace='sts') |     dian = fields.Many2One(dian.DianExtensions, name='DianExtensions', namespace='sts') | ||||||
|  |  | ||||||
|  | class DIANExtension(model.Model): | ||||||
|  |     __name__ = 'UBLExtension' | ||||||
|  |  | ||||||
|  |     content = fields.Many2One(DIANExtensionContent, namespace='ext') | ||||||
|  |  | ||||||
|     def __default_get__(self, name, value): |     def __default_get__(self, name, value): | ||||||
|         return self.dian |         return self.content.dian | ||||||
|  |  | ||||||
| class UBLExtension(model.Model): | class UBLExtension(model.Model): | ||||||
|     __name__ = 'UBLExtension' |     __name__ = 'UBLExtension' | ||||||
| @@ -250,16 +253,7 @@ class UBLExtensions(model.Model): | |||||||
|  |  | ||||||
| class Invoice(model.Model): | class Invoice(model.Model): | ||||||
|     __name__ = 'Invoice' |     __name__ = 'Invoice' | ||||||
|     __namespace__ = { |     __namespace__ = fe.NAMESPACES | ||||||
|         'cac': 'urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2', |  | ||||||
|         'cbc': 'urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2', |  | ||||||
|         'cdt': 'urn:DocumentInformation:names:specification:ubl:colombia:schema:xsd:DocumentInformationAggregateComponents-1', |  | ||||||
|         'ext': 'urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2', |  | ||||||
|         'sts': 'http://www.dian.gov.co/contratos/facturaelectronica/v1/Structures', |  | ||||||
|         'xades': 'http://uri.etsi.org/01903/v1.3.2#', |  | ||||||
|         'ds': 'http://www.w3.org/2000/09/xmldsig#' |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     _ubl_extensions = fields.Many2One(UBLExtensions, namespace='ext') |     _ubl_extensions = fields.Many2One(UBLExtensions, namespace='ext') | ||||||
|     dian = fields.Virtual(getter='get_dian_extension') |     dian = fields.Virtual(getter='get_dian_extension') | ||||||
| @@ -284,6 +278,7 @@ class Invoice(model.Model): | |||||||
|     taxtotal_03 = fields.Many2One(TaxTotal) |     taxtotal_03 = fields.Many2One(TaxTotal) | ||||||
|  |  | ||||||
|     def __setup__(self): |     def __setup__(self): | ||||||
|  |         self._namespace_prefix = 'fe' | ||||||
|         # Se requieren minimo estos impuestos para |         # Se requieren minimo estos impuestos para | ||||||
|         # validar el cufe |         # validar el cufe | ||||||
|         self._subtotal_01 = self.taxtotal_01.subtotals.create() |         self._subtotal_01 = self.taxtotal_01.subtotals.create() | ||||||
| @@ -351,3 +346,10 @@ class Invoice(model.Model): | |||||||
|  |  | ||||||
|     def get_dian_extension(self, name, _value): |     def get_dian_extension(self, name, _value): | ||||||
|         return self._ubl_extensions.dian |         return self._ubl_extensions.dian | ||||||
|  |  | ||||||
|  |     def to_xml(self, **kw): | ||||||
|  |         # al generar documento el namespace | ||||||
|  |         # se hace respecto a la raiz | ||||||
|  |         return super().to_xml(**kw)\ | ||||||
|  |             .replace("fe:", "")\ | ||||||
|  |             .replace("xmlns:fe", "xmlns") | ||||||
|   | |||||||
| @@ -2,6 +2,19 @@ import facho.model as model | |||||||
| import facho.model.fields as fields | import facho.model.fields as fields | ||||||
| from .common import * | from .common import * | ||||||
|  |  | ||||||
|  | class DIANElement(Element): | ||||||
|  |     """ | ||||||
|  |     Elemento que contiene atributos por defecto. | ||||||
|  |      | ||||||
|  |     Puede extender esta clase y modificar los atributos nuevamente | ||||||
|  |     """ | ||||||
|  |     __name__ = 'DIANElement' | ||||||
|  |      | ||||||
|  |     scheme_id = fields.Attribute('schemeID', default='4') | ||||||
|  |     scheme_name = fields.Attribute('schemeName', default='31') | ||||||
|  |     scheme_agency_name = fields.Attribute('schemeAgencyName', default='CO, DIAN (Dirección de Impuestos y Aduanas Nacionales)') | ||||||
|  |     scheme_agency_id = fields.Attribute('schemeAgencyID', default='195') | ||||||
|  |      | ||||||
| class SoftwareProvider(model.Model): | class SoftwareProvider(model.Model): | ||||||
|     __name__ = 'SoftwareProvider' |     __name__ = 'SoftwareProvider' | ||||||
|  |  | ||||||
| @@ -27,9 +40,17 @@ class InvoiceControl(model.Model): | |||||||
|     period = fields.Many2One(Period, name='AuthorizationPeriod', namespace='sts') |     period = fields.Many2One(Period, name='AuthorizationPeriod', namespace='sts') | ||||||
|     invoices = fields.Many2One(AuthorizedInvoices, namespace='sts') |     invoices = fields.Many2One(AuthorizedInvoices, namespace='sts') | ||||||
|  |  | ||||||
|  | class AuthorizationProvider(model.Model): | ||||||
|  |     __name__ = 'AuthorizationProvider' | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     id = fields.Many2One(DIANElement, name='AuthorizationProviderID', namespace='sts', default='800197268') | ||||||
|  |      | ||||||
| class DianExtensions(model.Model): | class DianExtensions(model.Model): | ||||||
|     __name__ = 'DianExtensions' |     __name__ = 'DianExtensions' | ||||||
|  |  | ||||||
|  |     authorization_provider = fields.Many2One(AuthorizationProvider, namespace='sts', create=True) | ||||||
|  |  | ||||||
|     software_security_code = fields.Many2One(Element, name='SoftwareSecurityCode', namespace='sts') |     software_security_code = fields.Many2One(Element, name='SoftwareSecurityCode', namespace='sts') | ||||||
|     software_provider = fields.Many2One(SoftwareProvider, namespace='sts') |     software_provider = fields.Many2One(SoftwareProvider, namespace='sts') | ||||||
|     source = fields.Many2One(InvoiceSource, namespace='sts') |     source = fields.Many2One(InvoiceSource, namespace='sts') | ||||||
|   | |||||||
| @@ -13,7 +13,9 @@ import facho.fe.model as model | |||||||
| import facho.fe.form as form | import facho.fe.form as form | ||||||
| from facho import fe | from facho import fe | ||||||
|  |  | ||||||
| def test_simple_invoice(): | import helpers | ||||||
|  |  | ||||||
|  | def simple_invoice(): | ||||||
|     invoice = model.Invoice() |     invoice = model.Invoice() | ||||||
|     invoice.dian.software_security_code = '12345' |     invoice.dian.software_security_code = '12345' | ||||||
|     invoice.dian.software_provider.provider_id = 'provider-id' |     invoice.dian.software_provider.provider_id = 'provider-id' | ||||||
| @@ -21,25 +23,6 @@ def test_simple_invoice(): | |||||||
|     invoice.dian.control.prefix = 'SETP' |     invoice.dian.control.prefix = 'SETP' | ||||||
|     invoice.dian.control.from_range =  '1000' |     invoice.dian.control.from_range =  '1000' | ||||||
|     invoice.dian.control.to_range = '1000' |     invoice.dian.control.to_range = '1000' | ||||||
|  |  | ||||||
|     invoice.id = '323200000129' |  | ||||||
|     invoice.issue = datetime.strptime('2019-01-16 10:53:10-05:00', '%Y-%m-%d %H:%M:%S%z') |  | ||||||
|     invoice.supplier.party.id = '700085371' |  | ||||||
|     invoice.customer.party.id = '800199436' |  | ||||||
|  |  | ||||||
|     line = invoice.lines.create() |  | ||||||
|     line.quantity = 1 |  | ||||||
|     line.price = form.Amount(5_000) |  | ||||||
|     subtotal = line.taxtotal.subtotals.create() |  | ||||||
|     subtotal.percent = 19.0 |  | ||||||
|     assert '<Invoice><ID>323200000129</ID><IssueDate>2019-01-16T10:53:10-05:00</IssueDate><IssueTime>10:5310-05:00</IssueTime><AccountingSupplierParty><Party><ID>700085371</ID></Party></AccountingSupplierParty><AccountingCustomerParty><Party><ID>800199436</ID></Party></AccountingCustomerParty><InvoiceLine><InvoicedQuantity unitCode="NAR">1</InvoicedQuantity><TaxTotal><TaxAmount currencyID="COP">0.0</TaxAmount><TaxSubTotal><TaxableAmount currencyID="COP">0.0</TaxableAmount><TaxAmount currencyID="COP">0.0</TaxAmount><Percent>19.0</Percent><TaxCategory><Percent>19.0</Percent></TaxCategory></TaxSubTotal></TaxTotal><Price><PriceAmount currencyID="COP">5000.0</PriceAmount>5000.0</Price><LineExtensionAmount currencyID="COP">5000.0</LineExtensionAmount></InvoiceLine><LegalMonetaryTotal><LineExtensionAmount currencyID="COP">0.0</LineExtensionAmount></LegalMonetaryTotal><TaxTotal><TaxAmount currencyID="COP">0.0</TaxAmount><TaxSubTotal><TaxableAmount currencyID="COP">0.0</TaxableAmount><TaxAmount currencyID="COP">0.0</TaxAmount><Percent>19.0</Percent><TaxCategory><Percent>19.0</Percent><TaxScheme><ID>01</ID></TaxScheme></TaxCategory></TaxSubTotal></TaxTotal><TaxTotal><TaxAmount currencyID="COP">0.0</TaxAmount><TaxSubTotal><TaxableAmount currencyID="COP">0.0</TaxableAmount><TaxAmount currencyID="COP">0.0</TaxAmount><TaxCategory><TaxScheme><ID>04</ID></TaxScheme></TaxCategory></TaxSubTotal></TaxTotal><TaxTotal><TaxAmount currencyID="COP">0.0</TaxAmount><TaxSubTotal><TaxableAmount currencyID="COP">0.0</TaxableAmount><TaxAmount currencyID="COP">0.0</TaxAmount><TaxCategory><TaxScheme><ID>03</ID></TaxScheme></TaxCategory></TaxSubTotal></TaxTotal></Invoice>' == invoice.to_xml() |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def test_simple_invoice_cufe(): |  | ||||||
|     token = '693ff6f2a553c3646a063436fd4dd9ded0311471' |  | ||||||
|     environment = fe.AMBIENTE_PRODUCCION |  | ||||||
|  |  | ||||||
|     invoice = model.Invoice() |  | ||||||
|     invoice.id = '323200000129' |     invoice.id = '323200000129' | ||||||
|     invoice.issue = datetime.strptime('2019-01-16 10:53:10-05:00', '%Y-%m-%d %H:%M:%S%z') |     invoice.issue = datetime.strptime('2019-01-16 10:53:10-05:00', '%Y-%m-%d %H:%M:%S%z') | ||||||
|     invoice.supplier.party.id = '700085371' |     invoice.supplier.party.id = '700085371' | ||||||
| @@ -54,4 +37,34 @@ def test_simple_invoice_cufe(): | |||||||
|     line.quantity = 1 |     line.quantity = 1 | ||||||
|     line.price = 1_500_000 |     line.price = 1_500_000 | ||||||
|  |  | ||||||
|  |     return invoice | ||||||
|  |  | ||||||
|  | def test_simple_invoice_cufe(): | ||||||
|  |     token = '693ff6f2a553c3646a063436fd4dd9ded0311471' | ||||||
|  |     environment = fe.AMBIENTE_PRODUCCION | ||||||
|  |     invoice = simple_invoice() | ||||||
|     assert invoice.cufe(token, environment) == '8bb918b19ba22a694f1da11c643b5e9de39adf60311cf179179e9b33381030bcd4c3c3f156c506ed5908f9276f5bd9b4' |     assert invoice.cufe(token, environment) == '8bb918b19ba22a694f1da11c643b5e9de39adf60311cf179179e9b33381030bcd4c3c3f156c506ed5908f9276f5bd9b4' | ||||||
|  |  | ||||||
|  | def test_simple_invoice_sign_dian(monkeypatch): | ||||||
|  |     invoice = simple_invoice() | ||||||
|  |  | ||||||
|  |     xmlstring = invoice.to_xml() | ||||||
|  |     p12_data = open('./tests/example.p12', 'rb').read() | ||||||
|  |     signer = fe.DianXMLExtensionSigner.from_bytes(p12_data) | ||||||
|  |  | ||||||
|  |     with monkeypatch.context() as m: | ||||||
|  |         helpers.mock_urlopen(m) | ||||||
|  |         xmlsigned = signer.sign_xml_string(xmlstring) | ||||||
|  |     assert "Signature" in xmlsigned | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def test_dian_extension_authorization_provider(): | ||||||
|  |     invoice = simple_invoice() | ||||||
|  |     xml = fe.FeXML.from_string(invoice.to_xml()) | ||||||
|  |     provider_id = xml.get_element('/fe:Invoice/ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:AuthorizationProvider/sts:AuthorizationProviderID') | ||||||
|  |  | ||||||
|  |     assert provider_id.attrib['schemeID'] == '4' | ||||||
|  |     assert provider_id.attrib['schemeName'] == '31' | ||||||
|  |     assert provider_id.attrib['schemeAgencyName'] == 'CO, DIAN (Dirección de Impuestos y Aduanas Nacionales)' | ||||||
|  |     assert provider_id.attrib['schemeAgencyID'] == '195' | ||||||
|  |     assert provider_id.text == '800197268' | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user