trevol 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- trevol-0.1.0/.gitignore +7 -0
- trevol-0.1.0/CHANGELOG.md +14 -0
- trevol-0.1.0/GENERADO.md +14 -0
- trevol-0.1.0/LICENSE +21 -0
- trevol-0.1.0/PKG-INFO +206 -0
- trevol-0.1.0/README.md +175 -0
- trevol-0.1.0/pruebas/conftest.py +127 -0
- trevol-0.1.0/pruebas/datos/errores.json +33 -0
- trevol-0.1.0/pruebas/datos/eventos.json +52 -0
- trevol-0.1.0/pruebas/datos/fragmentos.json +1107 -0
- trevol-0.1.0/pruebas/datos/openapi.json +27926 -0
- trevol-0.1.0/pruebas/datos/operaciones.json +4237 -0
- trevol-0.1.0/pruebas/datos/respuestas-futuras.json +287 -0
- trevol-0.1.0/pruebas/datos/tags.json +17 -0
- trevol-0.1.0/pruebas/datos/transporte.json +889 -0
- trevol-0.1.0/pruebas/datos/webhooks.json +342 -0
- trevol-0.1.0/pruebas/fragmentos/programas/correctCreditNote.py +27 -0
- trevol-0.1.0/pruebas/fragmentos/programas/correctDebitNote.py +25 -0
- trevol-0.1.0/pruebas/fragmentos/programas/correctEquivalentDocument.py +41 -0
- trevol-0.1.0/pruebas/fragmentos/programas/correctEquivalentDocumentAdjustment.py +26 -0
- trevol-0.1.0/pruebas/fragmentos/programas/correctInvoice.py +41 -0
- trevol-0.1.0/pruebas/fragmentos/programas/correctPayroll.py +53 -0
- trevol-0.1.0/pruebas/fragmentos/programas/correctPayrollAdjustment.py +55 -0
- trevol-0.1.0/pruebas/fragmentos/programas/correctSupportDocument.py +35 -0
- trevol-0.1.0/pruebas/fragmentos/programas/correctSupportDocumentAdjustment.py +25 -0
- trevol-0.1.0/pruebas/fragmentos/programas/createBalanceTopUp.py +11 -0
- trevol-0.1.0/pruebas/fragmentos/programas/createCreditNote.py +26 -0
- trevol-0.1.0/pruebas/fragmentos/programas/createDebitNote.py +24 -0
- trevol-0.1.0/pruebas/fragmentos/programas/createEquivalentDocument.py +40 -0
- trevol-0.1.0/pruebas/fragmentos/programas/createEquivalentDocumentAdjustment.py +25 -0
- trevol-0.1.0/pruebas/fragmentos/programas/createInvoice.py +40 -0
- trevol-0.1.0/pruebas/fragmentos/programas/createPayroll.py +52 -0
- trevol-0.1.0/pruebas/fragmentos/programas/createPayrollAdjustment.py +54 -0
- trevol-0.1.0/pruebas/fragmentos/programas/createRadianEvent.py +25 -0
- trevol-0.1.0/pruebas/fragmentos/programas/createSupportDocument.py +34 -0
- trevol-0.1.0/pruebas/fragmentos/programas/createSupportDocumentAdjustment.py +24 -0
- trevol-0.1.0/pruebas/fragmentos/programas/createWebhookEndpoint.py +14 -0
- trevol-0.1.0/pruebas/fragmentos/programas/deleteWebhookEndpoint.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/downloadCreditNoteDianResponse.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/downloadCreditNotePdf.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/downloadCreditNoteXml.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/downloadCreditNoteZip.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/downloadDebitNoteDianResponse.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/downloadDebitNotePdf.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/downloadDebitNoteXml.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/downloadDebitNoteZip.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/downloadEquivalentDocumentAdjustmentDianResponse.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/downloadEquivalentDocumentAdjustmentPdf.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/downloadEquivalentDocumentAdjustmentXml.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/downloadEquivalentDocumentAdjustmentZip.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/downloadEquivalentDocumentDianResponse.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/downloadEquivalentDocumentPdf.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/downloadEquivalentDocumentXml.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/downloadEquivalentDocumentZip.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/downloadInvoiceDianResponse.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/downloadInvoicePdf.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/downloadInvoiceXml.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/downloadInvoiceZip.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/downloadPayrollAdjustmentDianResponse.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/downloadPayrollAdjustmentXml.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/downloadPayrollAdjustmentZip.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/downloadPayrollDianResponse.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/downloadPayrollXml.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/downloadPayrollZip.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/downloadRadianEventXml.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/downloadSupportDocumentAdjustmentDianResponse.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/downloadSupportDocumentAdjustmentXml.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/downloadSupportDocumentDianResponse.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/downloadSupportDocumentXml.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/getHealth.py +8 -0
- trevol-0.1.0/pruebas/fragmentos/programas/listBalanceTransactions.py +8 -0
- trevol-0.1.0/pruebas/fragmentos/programas/listCreditNotes.py +8 -0
- trevol-0.1.0/pruebas/fragmentos/programas/listDebitNotes.py +8 -0
- trevol-0.1.0/pruebas/fragmentos/programas/listEquivalentDocumentAdjustments.py +8 -0
- trevol-0.1.0/pruebas/fragmentos/programas/listEquivalentDocuments.py +8 -0
- trevol-0.1.0/pruebas/fragmentos/programas/listEvents.py +8 -0
- trevol-0.1.0/pruebas/fragmentos/programas/listInvoices.py +8 -0
- trevol-0.1.0/pruebas/fragmentos/programas/listPayrollAdjustments.py +8 -0
- trevol-0.1.0/pruebas/fragmentos/programas/listPayrolls.py +8 -0
- trevol-0.1.0/pruebas/fragmentos/programas/listRadianEvents.py +8 -0
- trevol-0.1.0/pruebas/fragmentos/programas/listRequests.py +8 -0
- trevol-0.1.0/pruebas/fragmentos/programas/listSupportDocumentAdjustments.py +8 -0
- trevol-0.1.0/pruebas/fragmentos/programas/listSupportDocuments.py +8 -0
- trevol-0.1.0/pruebas/fragmentos/programas/listWebhookEndpointDeliveries.py +8 -0
- trevol-0.1.0/pruebas/fragmentos/programas/listWebhookEndpoints.py +9 -0
- trevol-0.1.0/pruebas/fragmentos/programas/previewCreditNote.py +25 -0
- trevol-0.1.0/pruebas/fragmentos/programas/previewDebitNote.py +23 -0
- trevol-0.1.0/pruebas/fragmentos/programas/previewEquivalentDocument.py +39 -0
- trevol-0.1.0/pruebas/fragmentos/programas/previewEquivalentDocumentAdjustment.py +24 -0
- trevol-0.1.0/pruebas/fragmentos/programas/previewInvoice.py +39 -0
- trevol-0.1.0/pruebas/fragmentos/programas/previewPayroll.py +51 -0
- trevol-0.1.0/pruebas/fragmentos/programas/previewPayrollAdjustment.py +53 -0
- trevol-0.1.0/pruebas/fragmentos/programas/previewSupportDocument.py +33 -0
- trevol-0.1.0/pruebas/fragmentos/programas/previewSupportDocumentAdjustment.py +23 -0
- trevol-0.1.0/pruebas/fragmentos/programas/resendEvent.py +11 -0
- trevol-0.1.0/pruebas/fragmentos/programas/retrieveBalance.py +8 -0
- trevol-0.1.0/pruebas/fragmentos/programas/retrieveCompany.py +8 -0
- trevol-0.1.0/pruebas/fragmentos/programas/retrieveCreditNote.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/retrieveDebitNote.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/retrieveDianAcquirer.py +11 -0
- trevol-0.1.0/pruebas/fragmentos/programas/retrieveEquivalentDocument.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/retrieveEquivalentDocumentAdjustment.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/retrieveEvent.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/retrieveInvoice.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/retrieveInvoiceTacitAcceptance.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/retrievePayroll.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/retrievePayrollAdjustment.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/retrieveRadianEvent.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/retrieveSupportDocument.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/retrieveSupportDocumentAdjustment.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/retrieveTacitAcceptanceByCufe.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/retrieveWebhookEndpoint.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/rotateWebhookEndpointSecret.py +11 -0
- trevol-0.1.0/pruebas/fragmentos/programas/sendCreditNote.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/sendDebitNote.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/sendEquivalentDocument.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/sendEquivalentDocumentAdjustment.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/sendInvoice.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/sendPayroll.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/sendPayrollAdjustment.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/sendRadianEvent.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/sendSupportDocument.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/sendSupportDocumentAdjustment.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/testWebhookEndpoint.py +10 -0
- trevol-0.1.0/pruebas/fragmentos/programas/updateWebhookEndpoint.py +14 -0
- trevol-0.1.0/pruebas/fragmentos/test_fragmentos.py +33 -0
- trevol-0.1.0/pruebas/humo/humo.py +142 -0
- trevol-0.1.0/pruebas/prism/test_prism.py +124 -0
- trevol-0.1.0/pruebas/test_cabeceras.py +49 -0
- trevol-0.1.0/pruebas/test_cobertura.py +24 -0
- trevol-0.1.0/pruebas/test_errores.py +88 -0
- trevol-0.1.0/pruebas/test_futuras.py +56 -0
- trevol-0.1.0/pruebas/test_idempotencia.py +61 -0
- trevol-0.1.0/pruebas/test_llave.py +45 -0
- trevol-0.1.0/pruebas/test_paginacion.py +67 -0
- trevol-0.1.0/pruebas/test_reintentos.py +92 -0
- trevol-0.1.0/pruebas/test_transporte.py +32 -0
- trevol-0.1.0/pruebas/test_webhooks.py +93 -0
- trevol-0.1.0/pyproject.toml +51 -0
- trevol-0.1.0/src/trevol/__init__.py +47 -0
- trevol-0.1.0/src/trevol/_generado/__init__.py +468 -0
- trevol-0.1.0/src/trevol/_generado/api/__init__.py +19 -0
- trevol-0.1.0/src/trevol/_generado/api/balance_api.py +897 -0
- trevol-0.1.0/src/trevol/_generado/api/credit_notes_api.py +2862 -0
- trevol-0.1.0/src/trevol/_generado/api/debit_notes_api.py +2862 -0
- trevol-0.1.0/src/trevol/_generado/api/equivalent_document_adjustments_api.py +2892 -0
- trevol-0.1.0/src/trevol/_generado/api/equivalent_documents_api.py +2909 -0
- trevol-0.1.0/src/trevol/_generado/api/events_api.py +939 -0
- trevol-0.1.0/src/trevol/_generado/api/general_api.py +803 -0
- trevol-0.1.0/src/trevol/_generado/api/invoices_api.py +3131 -0
- trevol-0.1.0/src/trevol/_generado/api/payroll_adjustments_api.py +2697 -0
- trevol-0.1.0/src/trevol/_generado/api/payrolls_api.py +2697 -0
- trevol-0.1.0/src/trevol/_generado/api/radian_events_api.py +1806 -0
- trevol-0.1.0/src/trevol/_generado/api/requests_api.py +389 -0
- trevol-0.1.0/src/trevol/_generado/api/support_document_adjustments_api.py +2362 -0
- trevol-0.1.0/src/trevol/_generado/api/support_documents_api.py +2362 -0
- trevol-0.1.0/src/trevol/_generado/api/webhook_endpoints_api.py +2287 -0
- trevol-0.1.0/src/trevol/_generado/api_client.py +842 -0
- trevol-0.1.0/src/trevol/_generado/api_response.py +21 -0
- trevol-0.1.0/src/trevol/_generado/configuration.py +654 -0
- trevol-0.1.0/src/trevol/_generado/exceptions.py +218 -0
- trevol-0.1.0/src/trevol/_generado/models/__init__.py +212 -0
- trevol-0.1.0/src/trevol/_generado/models/acquirer.py +121 -0
- trevol-0.1.0/src/trevol/_generado/models/air_collection.py +110 -0
- trevol-0.1.0/src/trevol/_generado/models/air_extension.py +118 -0
- trevol-0.1.0/src/trevol/_generado/models/allowance_charge.py +118 -0
- trevol-0.1.0/src/trevol/_generado/models/alternative_currency.py +106 -0
- trevol-0.1.0/src/trevol/_generado/models/balance.py +128 -0
- trevol-0.1.0/src/trevol/_generado/models/balance_event_data.py +103 -0
- trevol-0.1.0/src/trevol/_generado/models/balance_transaction.py +150 -0
- trevol-0.1.0/src/trevol/_generado/models/balance_transaction_company.py +113 -0
- trevol-0.1.0/src/trevol/_generado/models/balance_transaction_document.py +113 -0
- trevol-0.1.0/src/trevol/_generado/models/balance_transaction_list.py +119 -0
- trevol-0.1.0/src/trevol/_generado/models/carrier.py +117 -0
- trevol-0.1.0/src/trevol/_generado/models/certificate_event_data.py +106 -0
- trevol-0.1.0/src/trevol/_generado/models/cinema_extension.py +112 -0
- trevol-0.1.0/src/trevol/_generado/models/cinema_screening.py +112 -0
- trevol-0.1.0/src/trevol/_generado/models/company.py +157 -0
- trevol-0.1.0/src/trevol/_generado/models/company_key.py +110 -0
- trevol-0.1.0/src/trevol/_generado/models/company_numbering.py +135 -0
- trevol-0.1.0/src/trevol/_generado/models/consumption.py +128 -0
- trevol-0.1.0/src/trevol/_generado/models/consumption_adjustment.py +103 -0
- trevol-0.1.0/src/trevol/_generado/models/contingency.py +105 -0
- trevol-0.1.0/src/trevol/_generado/models/credit_note_list.py +119 -0
- trevol-0.1.0/src/trevol/_generado/models/currency.py +112 -0
- trevol-0.1.0/src/trevol/_generado/models/customer.py +145 -0
- trevol-0.1.0/src/trevol/_generado/models/debit_note_list.py +119 -0
- trevol-0.1.0/src/trevol/_generado/models/deleted_object.py +105 -0
- trevol-0.1.0/src/trevol/_generado/models/delivery.py +123 -0
- trevol-0.1.0/src/trevol/_generado/models/delivery_email.py +115 -0
- trevol-0.1.0/src/trevol/_generado/models/delivery_terms.py +103 -0
- trevol-0.1.0/src/trevol/_generado/models/dian_result.py +122 -0
- trevol-0.1.0/src/trevol/_generado/models/dian_rule.py +118 -0
- trevol-0.1.0/src/trevol/_generado/models/document.py +250 -0
- trevol-0.1.0/src/trevol/_generado/models/document_event_data.py +188 -0
- trevol-0.1.0/src/trevol/_generado/models/document_reference.py +104 -0
- trevol-0.1.0/src/trevol/_generado/models/document_totals.py +159 -0
- trevol-0.1.0/src/trevol/_generado/models/document_totals_currency.py +110 -0
- trevol-0.1.0/src/trevol/_generado/models/document_totals_item.py +109 -0
- trevol-0.1.0/src/trevol/_generado/models/document_totals_tax.py +114 -0
- trevol-0.1.0/src/trevol/_generado/models/document_totals_withholding.py +116 -0
- trevol-0.1.0/src/trevol/_generado/models/equivalent_adjustment_item.py +169 -0
- trevol-0.1.0/src/trevol/_generado/models/equivalent_document.py +286 -0
- trevol-0.1.0/src/trevol/_generado/models/equivalent_document_adjustment.py +256 -0
- trevol-0.1.0/src/trevol/_generado/models/equivalent_document_adjustment_input.py +180 -0
- trevol-0.1.0/src/trevol/_generado/models/equivalent_document_adjustment_list.py +119 -0
- trevol-0.1.0/src/trevol/_generado/models/equivalent_document_contingency.py +123 -0
- trevol-0.1.0/src/trevol/_generado/models/equivalent_document_contingency_backup.py +109 -0
- trevol-0.1.0/src/trevol/_generado/models/equivalent_document_event_data.py +178 -0
- trevol-0.1.0/src/trevol/_generado/models/equivalent_document_input.py +206 -0
- trevol-0.1.0/src/trevol/_generado/models/equivalent_document_list.py +119 -0
- trevol-0.1.0/src/trevol/_generado/models/equivalent_document_reference.py +122 -0
- trevol-0.1.0/src/trevol/_generado/models/equivalent_item.py +178 -0
- trevol-0.1.0/src/trevol/_generado/models/event.py +114 -0
- trevol-0.1.0/src/trevol/_generado/models/event_claim.py +103 -0
- trevol-0.1.0/src/trevol/_generado/models/event_delivery.py +109 -0
- trevol-0.1.0/src/trevol/_generado/models/event_delivery_email.py +101 -0
- trevol-0.1.0/src/trevol/_generado/models/event_dian_result.py +115 -0
- trevol-0.1.0/src/trevol/_generado/models/event_dian_rule.py +103 -0
- trevol-0.1.0/src/trevol/_generado/models/event_document_error.py +103 -0
- trevol-0.1.0/src/trevol/_generado/models/event_equivalent_document_ref.py +101 -0
- trevol-0.1.0/src/trevol/_generado/models/event_error.py +103 -0
- trevol-0.1.0/src/trevol/_generado/models/event_invoice_ref.py +101 -0
- trevol-0.1.0/src/trevol/_generado/models/event_list.py +119 -0
- trevol-0.1.0/src/trevol/_generado/models/event_payroll_ref.py +101 -0
- trevol-0.1.0/src/trevol/_generado/models/event_radian_invoice_ref.py +120 -0
- trevol-0.1.0/src/trevol/_generado/models/event_resend_input.py +101 -0
- trevol-0.1.0/src/trevol/_generado/models/event_support_document_ref.py +101 -0
- trevol-0.1.0/src/trevol/_generado/models/extension.py +141 -0
- trevol-0.1.0/src/trevol/_generado/models/foreign_address.py +113 -0
- trevol-0.1.0/src/trevol/_generado/models/free_of_charge.py +103 -0
- trevol-0.1.0/src/trevol/_generado/models/game.py +103 -0
- trevol-0.1.0/src/trevol/_generado/models/game_credit.py +103 -0
- trevol-0.1.0/src/trevol/_generado/models/games_extension.py +123 -0
- trevol-0.1.0/src/trevol/_generado/models/games_venue.py +103 -0
- trevol-0.1.0/src/trevol/_generado/models/interoperability.py +110 -0
- trevol-0.1.0/src/trevol/_generado/models/interoperability_collection.py +110 -0
- trevol-0.1.0/src/trevol/_generado/models/interoperability_data.py +103 -0
- trevol-0.1.0/src/trevol/_generado/models/invoice_input.py +202 -0
- trevol-0.1.0/src/trevol/_generado/models/invoice_list.py +119 -0
- trevol-0.1.0/src/trevol/_generado/models/invoice_reference.py +110 -0
- trevol-0.1.0/src/trevol/_generado/models/item.py +203 -0
- trevol-0.1.0/src/trevol/_generado/models/item_allowance.py +107 -0
- trevol-0.1.0/src/trevol/_generado/models/item_consignment.py +109 -0
- trevol-0.1.0/src/trevol/_generado/models/item_mandator.py +105 -0
- trevol-0.1.0/src/trevol/_generado/models/item_notary.py +103 -0
- trevol-0.1.0/src/trevol/_generado/models/item_property.py +107 -0
- trevol-0.1.0/src/trevol/_generado/models/item_tax.py +115 -0
- trevol-0.1.0/src/trevol/_generado/models/item_withholding.py +105 -0
- trevol-0.1.0/src/trevol/_generado/models/meter.py +114 -0
- trevol-0.1.0/src/trevol/_generado/models/note_input.py +180 -0
- trevol-0.1.0/src/trevol/_generado/models/numbering.py +101 -0
- trevol-0.1.0/src/trevol/_generado/models/numbering_expiring_event_data.py +117 -0
- trevol-0.1.0/src/trevol/_generado/models/numbering_low_event_data.py +116 -0
- trevol-0.1.0/src/trevol/_generado/models/origin_note.py +106 -0
- trevol-0.1.0/src/trevol/_generado/models/payment.py +122 -0
- trevol-0.1.0/src/trevol/_generado/models/payment_method.py +103 -0
- trevol-0.1.0/src/trevol/_generado/models/payroll.py +251 -0
- trevol-0.1.0/src/trevol/_generado/models/payroll_adjustment.py +263 -0
- trevol-0.1.0/src/trevol/_generado/models/payroll_adjustment_input.py +171 -0
- trevol-0.1.0/src/trevol/_generado/models/payroll_adjustment_list.py +119 -0
- trevol-0.1.0/src/trevol/_generado/models/payroll_basic.py +103 -0
- trevol-0.1.0/src/trevol/_generado/models/payroll_compensated_vacation.py +103 -0
- trevol-0.1.0/src/trevol/_generado/models/payroll_compensation.py +103 -0
- trevol-0.1.0/src/trevol/_generado/models/payroll_deductions.py +166 -0
- trevol-0.1.0/src/trevol/_generado/models/payroll_disability.py +110 -0
- trevol-0.1.0/src/trevol/_generado/models/payroll_earnings.py +227 -0
- trevol-0.1.0/src/trevol/_generado/models/payroll_employee.py +131 -0
- trevol-0.1.0/src/trevol/_generado/models/payroll_employer.py +107 -0
- trevol-0.1.0/src/trevol/_generado/models/payroll_epctv_bonus.py +107 -0
- trevol-0.1.0/src/trevol/_generado/models/payroll_event_data.py +182 -0
- trevol-0.1.0/src/trevol/_generado/models/payroll_files.py +105 -0
- trevol-0.1.0/src/trevol/_generado/models/payroll_input.py +162 -0
- trevol-0.1.0/src/trevol/_generado/models/payroll_leaves.py +125 -0
- trevol-0.1.0/src/trevol/_generado/models/payroll_list.py +119 -0
- trevol-0.1.0/src/trevol/_generado/models/payroll_loan.py +103 -0
- trevol-0.1.0/src/trevol/_generado/models/payroll_novelty.py +101 -0
- trevol-0.1.0/src/trevol/_generado/models/payroll_other_concept.py +105 -0
- trevol-0.1.0/src/trevol/_generado/models/payroll_overtime.py +111 -0
- trevol-0.1.0/src/trevol/_generado/models/payroll_paid_period.py +108 -0
- trevol-0.1.0/src/trevol/_generado/models/payroll_payment.py +109 -0
- trevol-0.1.0/src/trevol/_generado/models/payroll_percent_amount.py +103 -0
- trevol-0.1.0/src/trevol/_generado/models/payroll_period.py +110 -0
- trevol-0.1.0/src/trevol/_generado/models/payroll_place.py +105 -0
- trevol-0.1.0/src/trevol/_generado/models/payroll_reference.py +108 -0
- trevol-0.1.0/src/trevol/_generado/models/payroll_salary_amounts.py +103 -0
- trevol-0.1.0/src/trevol/_generado/models/payroll_sanction.py +103 -0
- trevol-0.1.0/src/trevol/_generado/models/payroll_service_bonus.py +105 -0
- trevol-0.1.0/src/trevol/_generado/models/payroll_severance.py +105 -0
- trevol-0.1.0/src/trevol/_generado/models/payroll_solidarity_fund.py +107 -0
- trevol-0.1.0/src/trevol/_generado/models/payroll_totals.py +107 -0
- trevol-0.1.0/src/trevol/_generado/models/payroll_transport.py +105 -0
- trevol-0.1.0/src/trevol/_generado/models/payroll_unpaid_period.py +106 -0
- trevol-0.1.0/src/trevol/_generado/models/payroll_vacation.py +117 -0
- trevol-0.1.0/src/trevol/_generado/models/payroll_workplace.py +107 -0
- trevol-0.1.0/src/trevol/_generado/models/period.py +104 -0
- trevol-0.1.0/src/trevol/_generado/models/physical_document.py +106 -0
- trevol-0.1.0/src/trevol/_generado/models/pos.py +111 -0
- trevol-0.1.0/src/trevol/_generado/models/pos_buyer.py +105 -0
- trevol-0.1.0/src/trevol/_generado/models/pos_register.py +109 -0
- trevol-0.1.0/src/trevol/_generado/models/prepayment.py +110 -0
- trevol-0.1.0/src/trevol/_generado/models/preview.py +112 -0
- trevol-0.1.0/src/trevol/_generado/models/problem.py +162 -0
- trevol-0.1.0/src/trevol/_generado/models/problem_error.py +105 -0
- trevol-0.1.0/src/trevol/_generado/models/public_services_extension.py +142 -0
- trevol-0.1.0/src/trevol/_generado/models/radian_event.py +189 -0
- trevol-0.1.0/src/trevol/_generado/models/radian_event_data.py +165 -0
- trevol-0.1.0/src/trevol/_generado/models/radian_event_input.py +141 -0
- trevol-0.1.0/src/trevol/_generado/models/radian_event_invoice.py +125 -0
- trevol-0.1.0/src/trevol/_generado/models/radian_event_list.py +119 -0
- trevol-0.1.0/src/trevol/_generado/models/received_invoice.py +113 -0
- trevol-0.1.0/src/trevol/_generado/models/received_invoice_issuer.py +107 -0
- trevol-0.1.0/src/trevol/_generado/models/references.py +121 -0
- trevol-0.1.0/src/trevol/_generado/models/request_list.py +119 -0
- trevol-0.1.0/src/trevol/_generado/models/request_log.py +144 -0
- trevol-0.1.0/src/trevol/_generado/models/seller.py +139 -0
- trevol-0.1.0/src/trevol/_generado/models/shipment.py +114 -0
- trevol-0.1.0/src/trevol/_generado/models/show_event.py +120 -0
- trevol-0.1.0/src/trevol/_generado/models/show_extension.py +111 -0
- trevol-0.1.0/src/trevol/_generado/models/show_producer.py +105 -0
- trevol-0.1.0/src/trevol/_generado/models/standard_code.py +103 -0
- trevol-0.1.0/src/trevol/_generado/models/stock_exchange_extension.py +118 -0
- trevol-0.1.0/src/trevol/_generado/models/stock_exchange_prices.py +103 -0
- trevol-0.1.0/src/trevol/_generado/models/support_adjustment_item.py +142 -0
- trevol-0.1.0/src/trevol/_generado/models/support_adjustment_reference.py +108 -0
- trevol-0.1.0/src/trevol/_generado/models/support_currency.py +106 -0
- trevol-0.1.0/src/trevol/_generado/models/support_document.py +241 -0
- trevol-0.1.0/src/trevol/_generado/models/support_document_adjustment.py +238 -0
- trevol-0.1.0/src/trevol/_generado/models/support_document_adjustment_input.py +155 -0
- trevol-0.1.0/src/trevol/_generado/models/support_document_adjustment_list.py +119 -0
- trevol-0.1.0/src/trevol/_generado/models/support_document_delivery.py +119 -0
- trevol-0.1.0/src/trevol/_generado/models/support_document_event_data.py +177 -0
- trevol-0.1.0/src/trevol/_generado/models/support_document_input.py +169 -0
- trevol-0.1.0/src/trevol/_generado/models/support_document_list.py +119 -0
- trevol-0.1.0/src/trevol/_generado/models/support_document_reference.py +110 -0
- trevol-0.1.0/src/trevol/_generado/models/support_item.py +154 -0
- trevol-0.1.0/src/trevol/_generado/models/support_item_charge.py +105 -0
- trevol-0.1.0/src/trevol/_generado/models/support_item_tax.py +105 -0
- trevol-0.1.0/src/trevol/_generado/models/support_item_withholding.py +103 -0
- trevol-0.1.0/src/trevol/_generado/models/tacit_acceptance.py +126 -0
- trevol-0.1.0/src/trevol/_generado/models/tacit_acceptance_rule.py +120 -0
- trevol-0.1.0/src/trevol/_generado/models/test_event_data.py +103 -0
- trevol-0.1.0/src/trevol/_generado/models/third_party_charge.py +126 -0
- trevol-0.1.0/src/trevol/_generado/models/third_party_company.py +103 -0
- trevol-0.1.0/src/trevol/_generado/models/top_up.py +113 -0
- trevol-0.1.0/src/trevol/_generado/models/top_up_buyer.py +136 -0
- trevol-0.1.0/src/trevol/_generado/models/top_up_input.py +107 -0
- trevol-0.1.0/src/trevol/_generado/models/top_up_wompi.py +112 -0
- trevol-0.1.0/src/trevol/_generado/models/transport_extension.py +109 -0
- trevol-0.1.0/src/trevol/_generado/models/webhook_delivery.py +153 -0
- trevol-0.1.0/src/trevol/_generado/models/webhook_delivery_attempt.py +128 -0
- trevol-0.1.0/src/trevol/_generado/models/webhook_delivery_list.py +119 -0
- trevol-0.1.0/src/trevol/_generado/models/webhook_endpoint.py +161 -0
- trevol-0.1.0/src/trevol/_generado/models/webhook_endpoint_input.py +118 -0
- trevol-0.1.0/src/trevol/_generado/models/webhook_endpoint_list.py +119 -0
- trevol-0.1.0/src/trevol/_generado/models/webhook_endpoint_rotate_input.py +101 -0
- trevol-0.1.0/src/trevol/_generado/models/webhook_endpoint_update.py +133 -0
- trevol-0.1.0/src/trevol/_generado/models/withholding.py +107 -0
- trevol-0.1.0/src/trevol/_generado/rest.py +334 -0
- trevol-0.1.0/src/trevol/_generado/tablas.py +1122 -0
- trevol-0.1.0/src/trevol/cliente.py +106 -0
- trevol-0.1.0/src/trevol/errores.py +226 -0
- trevol-0.1.0/src/trevol/llave.py +22 -0
- trevol-0.1.0/src/trevol/paginacion.py +25 -0
- trevol-0.1.0/src/trevol/py.typed +0 -0
- trevol-0.1.0/src/trevol/transporte.py +190 -0
- trevol-0.1.0/src/trevol/webhooks.py +95 -0
trevol-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Registro de cambios
|
|
2
|
+
|
|
3
|
+
El formato sigue [Keep a Changelog](https://keepachangelog.com/es-ES/1.1.0/) y el versionado, [SemVer](https://semver.org/lang/es/): antes
|
|
4
|
+
de la 1.0, un cambio incompatible sube el número menor.
|
|
5
|
+
|
|
6
|
+
## [0.1.0] — sin publicar
|
|
7
|
+
|
|
8
|
+
Primera versión (API `2026-10-22`).
|
|
9
|
+
|
|
10
|
+
- Cliente `Trevol` con un recurso por etiqueta de la API (`invoices`, `credit_notes`, `debit_notes`, `support_documents`, `payrolls`,
|
|
11
|
+
`radian_events`, `webhook_endpoints`, `events`, `requests`, `balance`, `general`…), modelos pydantic generados de la especificación.
|
|
12
|
+
- `Idempotency-Key` automática (la misma en los reintentos), reintentos con espera creciente que respetan `Retry-After`, errores tipados por
|
|
13
|
+
`code`, entorno deducido del prefijo de la llave, `Trevol-Version` fijada.
|
|
14
|
+
- `paginate` (iterador), descargas de PDF, XML y ZIP como `bytes`, `Webhook.verify` y `Webhook.event_data` (Standard Webhooks).
|
trevol-0.1.0/GENERADO.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Código generado
|
|
2
|
+
|
|
3
|
+
**No edites `src/trevol/_generado/`**: `sdks/generar.sh` la borra y la vuelve a escribir entera. La capa propia (cliente,
|
|
4
|
+
reintentos, errores, paginación, webhooks) vive fuera de esa carpeta.
|
|
5
|
+
|
|
6
|
+
| | |
|
|
7
|
+
|---|---|
|
|
8
|
+
| Generador | OpenAPI Generator 7.26.0 (`python`) |
|
|
9
|
+
| Especificación | `especificacion/openapi.sdk.enums-abiertos.json` (variante 3.0.3 de `docs/api/openapi.json`, versión de la API 2026-10-22) |
|
|
10
|
+
| sha256 de la especificación | `ca431c24f76cc576b9f989aa4c9df300f5df85fdce051a0704558fae65374bd1` |
|
|
11
|
+
| Configuración | `openapi-generator.yaml` |
|
|
12
|
+
|
|
13
|
+
Regenerar (desde el monorepo de Trevol): `sdks/generar.sh python`. Comprobar que lo del repositorio es lo que sale de la
|
|
14
|
+
especificación: `sdks/generar.sh --comprobar python`.
|
trevol-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Joshua Montclair (Trevol)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
trevol-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: trevol
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: SDK de Python para la API de Trevol: facturación electrónica ante la DIAN (Colombia). Python SDK for the Trevol API.
|
|
5
|
+
Project-URL: Documentation, https://platform.trevol.si/docs
|
|
6
|
+
Project-URL: Repository, https://github.com/trevol-si/trevol-python
|
|
7
|
+
Project-URL: Issues, https://github.com/trevol-si/trevol-python/issues
|
|
8
|
+
Author: Joshua Montclair (Trevol)
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: colombia,dian,factura electronica,nomina electronica,sdk,trevol
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Requires-Dist: pydantic<3,>=2.5.0
|
|
23
|
+
Requires-Dist: python-dateutil>=2.8.2
|
|
24
|
+
Requires-Dist: standardwebhooks<2,>=1.1.0
|
|
25
|
+
Requires-Dist: typing-extensions>=4.7.1
|
|
26
|
+
Requires-Dist: urllib3<3,>=2.1.0
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest-cov==7.0.0; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest==8.4.2; extra == 'dev'
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
# trevol
|
|
33
|
+
|
|
34
|
+
SDK de Python para la **API de Trevol**: facturación electrónica, documento soporte, documento equivalente, nómina electrónica y eventos
|
|
35
|
+
RADIAN ante la DIAN (Colombia). Documentación: <https://platform.trevol.si/docs>.
|
|
36
|
+
|
|
37
|
+
> **In English.** Python SDK for the Trevol API (Colombian e-invoicing, DIAN). Python ≥ 3.10, typed (pydantic v2) models generated from the
|
|
38
|
+
> OpenAPI spec (OpenAPI Generator), plus a thin hand-written layer: automatic `Idempotency-Key` (the same one on retries), retries with
|
|
39
|
+
> exponential backoff honouring `Retry-After`, typed errors by problem+json `code`, environment inferred from the key prefix (`tvl_test_` =
|
|
40
|
+
> DIAN test environment, `tvl_live_` = production), pinned `Trevol-Version`, Standard Webhooks signature verification, cursor pagination
|
|
41
|
+
> iterators and PDF/XML/ZIP downloads (as `bytes`). Docs are in Spanish like the API.
|
|
42
|
+
|
|
43
|
+
**Versión 0.1.0** — API `2026-10-22`. Licencia MIT.
|
|
44
|
+
|
|
45
|
+
## Instalación
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
pip install trevol
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Necesita Python 3.10 o más nuevo. Dependencias: `urllib3`, `pydantic>=2`, `python-dateutil`, `typing-extensions` y
|
|
52
|
+
[`standardwebhooks`](https://pypi.org/project/standardwebhooks/).
|
|
53
|
+
|
|
54
|
+
## Uso
|
|
55
|
+
|
|
56
|
+
La llave de la API se crea en la consola de desarrolladores (<https://platform.trevol.si/llaves>) y se guarda en el entorno de tu **servidor**:
|
|
57
|
+
`TREVOL_API_KEY`. Una llave `tvl_test_…` llama a la habilitación de la DIAN; una `tvl_live_…`, a producción. El SDK deduce el entorno del
|
|
58
|
+
prefijo (`trevol.environment`).
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
import os
|
|
62
|
+
from trevol import Trevol, TrevolError, ValidationError, Webhook
|
|
63
|
+
|
|
64
|
+
trevol = Trevol() # lee TREVOL_API_KEY; tvl_test_… → trevol.environment == "test"
|
|
65
|
+
|
|
66
|
+
try:
|
|
67
|
+
factura = trevol.invoices.create_invoice(
|
|
68
|
+
invoice_input={
|
|
69
|
+
"external_id": "VENTA-2026-000123",
|
|
70
|
+
"customer": {"final_consumer": True},
|
|
71
|
+
"items": [{"description": "Café especial 500 g", "quantity": "1", "unit_price": "32000", "taxes": [{"code": "01", "rate": "19"}]}],
|
|
72
|
+
"payment": {"form": "cash", "methods": [{"code": "10"}]},
|
|
73
|
+
},
|
|
74
|
+
idempotency_key="VENTA-2026-000123", # opcional: si falta, el SDK pone una y la repite en los reintentos
|
|
75
|
+
)
|
|
76
|
+
if factura.status == "processing":
|
|
77
|
+
pass # 202: la DIAN no contestó; Trevol lo termina y avisa por webhook. NO crees otro documento para la misma venta.
|
|
78
|
+
except ValidationError as e:
|
|
79
|
+
for x in e.errors:
|
|
80
|
+
print(x.pointer, x.message)
|
|
81
|
+
except TrevolError as e:
|
|
82
|
+
print(e.code, e.request_id)
|
|
83
|
+
|
|
84
|
+
# Todas las páginas de un listado, perezosamente
|
|
85
|
+
for f in trevol.paginate(lambda cursor: trevol.invoices.list_invoices(limit=100, starting_after=cursor)):
|
|
86
|
+
print(f.id, f.status)
|
|
87
|
+
|
|
88
|
+
# Los archivos llegan como bytes
|
|
89
|
+
pdf: bytes = trevol.invoices.download_invoice_pdf(id="inv_…")
|
|
90
|
+
|
|
91
|
+
# Un webhook: verifica sobre el CUERPO CRUDO
|
|
92
|
+
evento = Webhook(os.environ["TREVOL_WEBHOOK_SECRET"]).verify(request.get_data(), request.headers)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Los métodos son los `operationId` de la [especificación](https://platform.trevol.si/docs/api) en `snake_case` (`create_invoice`,
|
|
96
|
+
`list_invoices`, `retrieve_invoice`, `correct_invoice`, `send_invoice`, `download_invoice_pdf`…) y el cuerpo es el nombre del esquema en
|
|
97
|
+
`snake_case` (`invoice_input`). Puedes pasar un `dict` (como arriba) o el modelo (`InvoiceInput(...)`). Los campos se escriben **igual que en la
|
|
98
|
+
API** (`external_id`), y los importes y las fechas viajan como texto (`"32000.50"`, `"2026-10-31"`).
|
|
99
|
+
|
|
100
|
+
## Lo que conviene saber
|
|
101
|
+
|
|
102
|
+
### Idempotencia: manda `external_id` y tu propia clave
|
|
103
|
+
|
|
104
|
+
El SDK genera una `Idempotency-Key` por llamada y **la repite en todos los reintentos de esa llamada**. Pero si tu proceso muere y vuelves a
|
|
105
|
+
llamar, saldrá otra clave y, si no mandaste `external_id` (es opcional), podría duplicarse el documento. Por eso:
|
|
106
|
+
|
|
107
|
+
- manda siempre `external_id` (tu id de la venta), y
|
|
108
|
+
- pasa una `idempotency_key` derivada de tu venta (`idempotency_key="VENTA-2026-000123"`).
|
|
109
|
+
|
|
110
|
+
Ante un `ConflictError` con `code == "external_id_in_use"`, el documento ya existe: usa `error.problem["existing_id"]`. Todo error lleva la
|
|
111
|
+
`idempotency_key` que se usó (`error.idempotency_key`) para reintentar más tarde con la misma. Un `APIConnectionError` también.
|
|
112
|
+
|
|
113
|
+
### Respuestas 202 (`processing`, `sending`, `validating`, `contingency`)
|
|
114
|
+
|
|
115
|
+
No son errores ni se reintentan: se devuelven como el modelo de la respuesta. **No crees otro documento**: espera el webhook o consulta con
|
|
116
|
+
`retrieve_invoice`; repetir la misma llamada con la misma `Idempotency-Key` continúa el mismo documento. El PDF y el ZIP llegan después del
|
|
117
|
+
`201` (evento `invoice.delivered`, o `delivery.status == "completed"`).
|
|
118
|
+
|
|
119
|
+
### Reintentos
|
|
120
|
+
|
|
121
|
+
Hasta 2 reintentos (3 intentos) con espera creciente (0,5 s, 1 s, 2 s… con tope de 8 s y un poco de azar) ante un error de red, 429, 500,
|
|
122
|
+
502, 503, 504, un 409 `request_in_progress` o `previous_event_pending`, y todo problema que diga `retryable: true` (salvo el 402). Respeta
|
|
123
|
+
`Retry-After`; si pide más de `max_retry_after` (60 s) no espera y lanza el error con `error.retry_after`. Las operaciones sin
|
|
124
|
+
`Idempotency-Key` (crear un endpoint, `…/send`, `DELETE`) solo se reintentan con 429. Los 202 y los errores finales (400, 401, 402, 403, 404,
|
|
125
|
+
422 y los 409 `invalid_state`, `external_id_in_use`, `duplicate_document`) **nunca** se reintentan. Los reintentos de `urllib3` están apagados:
|
|
126
|
+
el SDK es el único que reintenta.
|
|
127
|
+
|
|
128
|
+
### La versión de la API
|
|
129
|
+
|
|
130
|
+
El SDK fija `Trevol-Version: 2026-10-22` en cada petición. **Ojo:** una llave creada con la versión `2026-10-21` que empiece a usar este SDK
|
|
131
|
+
pasa a `2026-10-22` (el encabezado manda): recibe `202 processing` donde antes recibía `503`. Si todavía no quieres ese cambio:
|
|
132
|
+
`Trevol(api_version="2026-10-21")`. Más en <https://platform.trevol.si/docs/versiones>.
|
|
133
|
+
|
|
134
|
+
### Errores tipados
|
|
135
|
+
|
|
136
|
+
`TrevolError` es la base; las clases salen del `code` del problem+json: `AuthenticationError`, `PermissionDeniedError`, `NotFoundError`,
|
|
137
|
+
`InvalidRequestError`, `IdempotencyError`, `ValidationError` (con `errors`: `field`, `pointer`, `message`), `ConflictError`,
|
|
138
|
+
`PreconditionError`, `DianRejectedError` (con `problem["document"]` y `problem["rules"]`), `BalanceError`, `RateLimitError`,
|
|
139
|
+
`DianUnavailableError` e `InternalServerError`. Un `code` que el SDK todavía no conoce llega como `TrevolError` con ese `code`: nunca
|
|
140
|
+
rompe. Además: `APIConnectionError` (no hubo respuesta), `ConfigurationError` (mal uso, antes de llamar) y `WebhookVerificationError`.
|
|
141
|
+
Cada error tiene `status`, `code`, `type`, `title`, `detail`, `request_id`, `retryable`, `retry_after`, `errors`, `idempotency_key`,
|
|
142
|
+
`problem` (el cuerpo completo) y `headers` (los de la respuesta). Ninguno incluye tu llave (ni su `repr`, ni su traceback).
|
|
143
|
+
|
|
144
|
+
### Webhooks
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
from flask import Flask, request
|
|
148
|
+
from trevol import Webhook, WebhookVerificationError
|
|
149
|
+
|
|
150
|
+
app = Flask(__name__)
|
|
151
|
+
wh = Webhook(os.environ["TREVOL_WEBHOOK_SECRET"]) # whsec_…
|
|
152
|
+
|
|
153
|
+
@app.post("/webhooks/trevol")
|
|
154
|
+
def recibir():
|
|
155
|
+
try:
|
|
156
|
+
evento = wh.verify(request.get_data(), request.headers) # el cuerpo CRUDO, no request.json
|
|
157
|
+
except WebhookVerificationError:
|
|
158
|
+
return "", 400
|
|
159
|
+
# Deduplica por evento.id (también llega en la cabecera webhook-id): la verificación no evita repeticiones.
|
|
160
|
+
datos = wh.event_data(evento) # el modelo según evento.type (p. ej. DocumentEventData)
|
|
161
|
+
return "", 200
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
En FastAPI usa `await request.body()`; en Django, `request.body`. La firma se calcula sobre los **bytes exactos**. `verify` acepta cualquier mapa de
|
|
165
|
+
cabeceras (Flask, Django, Starlette, `dict`) y el cuerpo como `str` o `bytes`; tolera 5 minutos hacia atrás y hacia adelante en `webhook-timestamp`.
|
|
166
|
+
|
|
167
|
+
### Estado y cabeceras de la respuesta
|
|
168
|
+
|
|
169
|
+
Cada método tiene una variante `…_with_http_info` que devuelve `ApiResponse` con `status_code`, `headers` y `data`:
|
|
170
|
+
`r = trevol.invoices.create_invoice_with_http_info(...); r.headers["request-id"]; r.headers["idempotent-replayed"]; r.data`.
|
|
171
|
+
|
|
172
|
+
### Cabeceras propias por llamada
|
|
173
|
+
|
|
174
|
+
Cada método acepta `_headers={...}` (se mezcla con las del SDK) y `_request_timeout=` (segundos).
|
|
175
|
+
|
|
176
|
+
## Opciones
|
|
177
|
+
|
|
178
|
+
```python
|
|
179
|
+
Trevol(
|
|
180
|
+
api_key,
|
|
181
|
+
api_version="2026-10-22", # Trevol-Version en cada petición
|
|
182
|
+
max_retries=2, # 0 los apaga
|
|
183
|
+
timeout=65.0, # segundos por intento
|
|
184
|
+
max_retry_after=60.0, # segundos
|
|
185
|
+
base_url="https://api.trevol.si/v1",
|
|
186
|
+
environment="test", # si la llave no coincide: ConfigurationError antes de llamar
|
|
187
|
+
proxy=None, # p. ej. "http://proxy.miempresa.co:3128"
|
|
188
|
+
)
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Cómo está hecho
|
|
192
|
+
|
|
193
|
+
- `src/trevol/_generado/` lo escribe [OpenAPI Generator](https://openapi-generator.tech) 7.26.0 (`python`, pydantic v2, urllib3) desde la
|
|
194
|
+
especificación pública y **no se edita** (ver `GENERADO.md`). Para que un valor nuevo de un `enum` en lo que la API devuelve no rompa a quien
|
|
195
|
+
ya integró, los modelos de respuesta se generan sin `enum` (los valores conocidos quedan en la descripción).
|
|
196
|
+
- Lo propio (`cliente.py`, `transporte.py`, `errores.py`, `paginacion.py`, `webhooks.py`) vive fuera y solo usa los puntos de extensión del
|
|
197
|
+
código generado (una subclase de `ApiClient`).
|
|
198
|
+
- Las pruebas: `pip install -e ".[dev]" && pytest` (unitarias sin red contra un servidor HTTP local, con los mismos casos de los demás SDK en
|
|
199
|
+
`pruebas/datos/`); `TREVOL_PRISM_URL=http://127.0.0.1:4010 pytest pruebas/prism pruebas/fragmentos` contra un servidor simulado
|
|
200
|
+
(`npx @stoplight/prism-cli mock pruebas/datos/openapi.json -p 4010`) que valida cada petición contra la especificación, y que además corre
|
|
201
|
+
como programa cada fragmento de código de la documentación.
|
|
202
|
+
- `python pruebas/humo/humo.py` es la prueba de humo **real** (solo con una llave `tvl_test_`; no emite documentos).
|
|
203
|
+
|
|
204
|
+
## Licencia
|
|
205
|
+
|
|
206
|
+
MIT © 2026 Joshua Montclair (Trevol).
|
trevol-0.1.0/README.md
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# trevol
|
|
2
|
+
|
|
3
|
+
SDK de Python para la **API de Trevol**: facturación electrónica, documento soporte, documento equivalente, nómina electrónica y eventos
|
|
4
|
+
RADIAN ante la DIAN (Colombia). Documentación: <https://platform.trevol.si/docs>.
|
|
5
|
+
|
|
6
|
+
> **In English.** Python SDK for the Trevol API (Colombian e-invoicing, DIAN). Python ≥ 3.10, typed (pydantic v2) models generated from the
|
|
7
|
+
> OpenAPI spec (OpenAPI Generator), plus a thin hand-written layer: automatic `Idempotency-Key` (the same one on retries), retries with
|
|
8
|
+
> exponential backoff honouring `Retry-After`, typed errors by problem+json `code`, environment inferred from the key prefix (`tvl_test_` =
|
|
9
|
+
> DIAN test environment, `tvl_live_` = production), pinned `Trevol-Version`, Standard Webhooks signature verification, cursor pagination
|
|
10
|
+
> iterators and PDF/XML/ZIP downloads (as `bytes`). Docs are in Spanish like the API.
|
|
11
|
+
|
|
12
|
+
**Versión 0.1.0** — API `2026-10-22`. Licencia MIT.
|
|
13
|
+
|
|
14
|
+
## Instalación
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
pip install trevol
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Necesita Python 3.10 o más nuevo. Dependencias: `urllib3`, `pydantic>=2`, `python-dateutil`, `typing-extensions` y
|
|
21
|
+
[`standardwebhooks`](https://pypi.org/project/standardwebhooks/).
|
|
22
|
+
|
|
23
|
+
## Uso
|
|
24
|
+
|
|
25
|
+
La llave de la API se crea en la consola de desarrolladores (<https://platform.trevol.si/llaves>) y se guarda en el entorno de tu **servidor**:
|
|
26
|
+
`TREVOL_API_KEY`. Una llave `tvl_test_…` llama a la habilitación de la DIAN; una `tvl_live_…`, a producción. El SDK deduce el entorno del
|
|
27
|
+
prefijo (`trevol.environment`).
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
import os
|
|
31
|
+
from trevol import Trevol, TrevolError, ValidationError, Webhook
|
|
32
|
+
|
|
33
|
+
trevol = Trevol() # lee TREVOL_API_KEY; tvl_test_… → trevol.environment == "test"
|
|
34
|
+
|
|
35
|
+
try:
|
|
36
|
+
factura = trevol.invoices.create_invoice(
|
|
37
|
+
invoice_input={
|
|
38
|
+
"external_id": "VENTA-2026-000123",
|
|
39
|
+
"customer": {"final_consumer": True},
|
|
40
|
+
"items": [{"description": "Café especial 500 g", "quantity": "1", "unit_price": "32000", "taxes": [{"code": "01", "rate": "19"}]}],
|
|
41
|
+
"payment": {"form": "cash", "methods": [{"code": "10"}]},
|
|
42
|
+
},
|
|
43
|
+
idempotency_key="VENTA-2026-000123", # opcional: si falta, el SDK pone una y la repite en los reintentos
|
|
44
|
+
)
|
|
45
|
+
if factura.status == "processing":
|
|
46
|
+
pass # 202: la DIAN no contestó; Trevol lo termina y avisa por webhook. NO crees otro documento para la misma venta.
|
|
47
|
+
except ValidationError as e:
|
|
48
|
+
for x in e.errors:
|
|
49
|
+
print(x.pointer, x.message)
|
|
50
|
+
except TrevolError as e:
|
|
51
|
+
print(e.code, e.request_id)
|
|
52
|
+
|
|
53
|
+
# Todas las páginas de un listado, perezosamente
|
|
54
|
+
for f in trevol.paginate(lambda cursor: trevol.invoices.list_invoices(limit=100, starting_after=cursor)):
|
|
55
|
+
print(f.id, f.status)
|
|
56
|
+
|
|
57
|
+
# Los archivos llegan como bytes
|
|
58
|
+
pdf: bytes = trevol.invoices.download_invoice_pdf(id="inv_…")
|
|
59
|
+
|
|
60
|
+
# Un webhook: verifica sobre el CUERPO CRUDO
|
|
61
|
+
evento = Webhook(os.environ["TREVOL_WEBHOOK_SECRET"]).verify(request.get_data(), request.headers)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Los métodos son los `operationId` de la [especificación](https://platform.trevol.si/docs/api) en `snake_case` (`create_invoice`,
|
|
65
|
+
`list_invoices`, `retrieve_invoice`, `correct_invoice`, `send_invoice`, `download_invoice_pdf`…) y el cuerpo es el nombre del esquema en
|
|
66
|
+
`snake_case` (`invoice_input`). Puedes pasar un `dict` (como arriba) o el modelo (`InvoiceInput(...)`). Los campos se escriben **igual que en la
|
|
67
|
+
API** (`external_id`), y los importes y las fechas viajan como texto (`"32000.50"`, `"2026-10-31"`).
|
|
68
|
+
|
|
69
|
+
## Lo que conviene saber
|
|
70
|
+
|
|
71
|
+
### Idempotencia: manda `external_id` y tu propia clave
|
|
72
|
+
|
|
73
|
+
El SDK genera una `Idempotency-Key` por llamada y **la repite en todos los reintentos de esa llamada**. Pero si tu proceso muere y vuelves a
|
|
74
|
+
llamar, saldrá otra clave y, si no mandaste `external_id` (es opcional), podría duplicarse el documento. Por eso:
|
|
75
|
+
|
|
76
|
+
- manda siempre `external_id` (tu id de la venta), y
|
|
77
|
+
- pasa una `idempotency_key` derivada de tu venta (`idempotency_key="VENTA-2026-000123"`).
|
|
78
|
+
|
|
79
|
+
Ante un `ConflictError` con `code == "external_id_in_use"`, el documento ya existe: usa `error.problem["existing_id"]`. Todo error lleva la
|
|
80
|
+
`idempotency_key` que se usó (`error.idempotency_key`) para reintentar más tarde con la misma. Un `APIConnectionError` también.
|
|
81
|
+
|
|
82
|
+
### Respuestas 202 (`processing`, `sending`, `validating`, `contingency`)
|
|
83
|
+
|
|
84
|
+
No son errores ni se reintentan: se devuelven como el modelo de la respuesta. **No crees otro documento**: espera el webhook o consulta con
|
|
85
|
+
`retrieve_invoice`; repetir la misma llamada con la misma `Idempotency-Key` continúa el mismo documento. El PDF y el ZIP llegan después del
|
|
86
|
+
`201` (evento `invoice.delivered`, o `delivery.status == "completed"`).
|
|
87
|
+
|
|
88
|
+
### Reintentos
|
|
89
|
+
|
|
90
|
+
Hasta 2 reintentos (3 intentos) con espera creciente (0,5 s, 1 s, 2 s… con tope de 8 s y un poco de azar) ante un error de red, 429, 500,
|
|
91
|
+
502, 503, 504, un 409 `request_in_progress` o `previous_event_pending`, y todo problema que diga `retryable: true` (salvo el 402). Respeta
|
|
92
|
+
`Retry-After`; si pide más de `max_retry_after` (60 s) no espera y lanza el error con `error.retry_after`. Las operaciones sin
|
|
93
|
+
`Idempotency-Key` (crear un endpoint, `…/send`, `DELETE`) solo se reintentan con 429. Los 202 y los errores finales (400, 401, 402, 403, 404,
|
|
94
|
+
422 y los 409 `invalid_state`, `external_id_in_use`, `duplicate_document`) **nunca** se reintentan. Los reintentos de `urllib3` están apagados:
|
|
95
|
+
el SDK es el único que reintenta.
|
|
96
|
+
|
|
97
|
+
### La versión de la API
|
|
98
|
+
|
|
99
|
+
El SDK fija `Trevol-Version: 2026-10-22` en cada petición. **Ojo:** una llave creada con la versión `2026-10-21` que empiece a usar este SDK
|
|
100
|
+
pasa a `2026-10-22` (el encabezado manda): recibe `202 processing` donde antes recibía `503`. Si todavía no quieres ese cambio:
|
|
101
|
+
`Trevol(api_version="2026-10-21")`. Más en <https://platform.trevol.si/docs/versiones>.
|
|
102
|
+
|
|
103
|
+
### Errores tipados
|
|
104
|
+
|
|
105
|
+
`TrevolError` es la base; las clases salen del `code` del problem+json: `AuthenticationError`, `PermissionDeniedError`, `NotFoundError`,
|
|
106
|
+
`InvalidRequestError`, `IdempotencyError`, `ValidationError` (con `errors`: `field`, `pointer`, `message`), `ConflictError`,
|
|
107
|
+
`PreconditionError`, `DianRejectedError` (con `problem["document"]` y `problem["rules"]`), `BalanceError`, `RateLimitError`,
|
|
108
|
+
`DianUnavailableError` e `InternalServerError`. Un `code` que el SDK todavía no conoce llega como `TrevolError` con ese `code`: nunca
|
|
109
|
+
rompe. Además: `APIConnectionError` (no hubo respuesta), `ConfigurationError` (mal uso, antes de llamar) y `WebhookVerificationError`.
|
|
110
|
+
Cada error tiene `status`, `code`, `type`, `title`, `detail`, `request_id`, `retryable`, `retry_after`, `errors`, `idempotency_key`,
|
|
111
|
+
`problem` (el cuerpo completo) y `headers` (los de la respuesta). Ninguno incluye tu llave (ni su `repr`, ni su traceback).
|
|
112
|
+
|
|
113
|
+
### Webhooks
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
from flask import Flask, request
|
|
117
|
+
from trevol import Webhook, WebhookVerificationError
|
|
118
|
+
|
|
119
|
+
app = Flask(__name__)
|
|
120
|
+
wh = Webhook(os.environ["TREVOL_WEBHOOK_SECRET"]) # whsec_…
|
|
121
|
+
|
|
122
|
+
@app.post("/webhooks/trevol")
|
|
123
|
+
def recibir():
|
|
124
|
+
try:
|
|
125
|
+
evento = wh.verify(request.get_data(), request.headers) # el cuerpo CRUDO, no request.json
|
|
126
|
+
except WebhookVerificationError:
|
|
127
|
+
return "", 400
|
|
128
|
+
# Deduplica por evento.id (también llega en la cabecera webhook-id): la verificación no evita repeticiones.
|
|
129
|
+
datos = wh.event_data(evento) # el modelo según evento.type (p. ej. DocumentEventData)
|
|
130
|
+
return "", 200
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
En FastAPI usa `await request.body()`; en Django, `request.body`. La firma se calcula sobre los **bytes exactos**. `verify` acepta cualquier mapa de
|
|
134
|
+
cabeceras (Flask, Django, Starlette, `dict`) y el cuerpo como `str` o `bytes`; tolera 5 minutos hacia atrás y hacia adelante en `webhook-timestamp`.
|
|
135
|
+
|
|
136
|
+
### Estado y cabeceras de la respuesta
|
|
137
|
+
|
|
138
|
+
Cada método tiene una variante `…_with_http_info` que devuelve `ApiResponse` con `status_code`, `headers` y `data`:
|
|
139
|
+
`r = trevol.invoices.create_invoice_with_http_info(...); r.headers["request-id"]; r.headers["idempotent-replayed"]; r.data`.
|
|
140
|
+
|
|
141
|
+
### Cabeceras propias por llamada
|
|
142
|
+
|
|
143
|
+
Cada método acepta `_headers={...}` (se mezcla con las del SDK) y `_request_timeout=` (segundos).
|
|
144
|
+
|
|
145
|
+
## Opciones
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
Trevol(
|
|
149
|
+
api_key,
|
|
150
|
+
api_version="2026-10-22", # Trevol-Version en cada petición
|
|
151
|
+
max_retries=2, # 0 los apaga
|
|
152
|
+
timeout=65.0, # segundos por intento
|
|
153
|
+
max_retry_after=60.0, # segundos
|
|
154
|
+
base_url="https://api.trevol.si/v1",
|
|
155
|
+
environment="test", # si la llave no coincide: ConfigurationError antes de llamar
|
|
156
|
+
proxy=None, # p. ej. "http://proxy.miempresa.co:3128"
|
|
157
|
+
)
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Cómo está hecho
|
|
161
|
+
|
|
162
|
+
- `src/trevol/_generado/` lo escribe [OpenAPI Generator](https://openapi-generator.tech) 7.26.0 (`python`, pydantic v2, urllib3) desde la
|
|
163
|
+
especificación pública y **no se edita** (ver `GENERADO.md`). Para que un valor nuevo de un `enum` en lo que la API devuelve no rompa a quien
|
|
164
|
+
ya integró, los modelos de respuesta se generan sin `enum` (los valores conocidos quedan en la descripción).
|
|
165
|
+
- Lo propio (`cliente.py`, `transporte.py`, `errores.py`, `paginacion.py`, `webhooks.py`) vive fuera y solo usa los puntos de extensión del
|
|
166
|
+
código generado (una subclase de `ApiClient`).
|
|
167
|
+
- Las pruebas: `pip install -e ".[dev]" && pytest` (unitarias sin red contra un servidor HTTP local, con los mismos casos de los demás SDK en
|
|
168
|
+
`pruebas/datos/`); `TREVOL_PRISM_URL=http://127.0.0.1:4010 pytest pruebas/prism pruebas/fragmentos` contra un servidor simulado
|
|
169
|
+
(`npx @stoplight/prism-cli mock pruebas/datos/openapi.json -p 4010`) que valida cada petición contra la especificación, y que además corre
|
|
170
|
+
como programa cada fragmento de código de la documentación.
|
|
171
|
+
- `python pruebas/humo/humo.py` es la prueba de humo **real** (solo con una llave `tvl_test_`; no emite documentos).
|
|
172
|
+
|
|
173
|
+
## Licencia
|
|
174
|
+
|
|
175
|
+
MIT © 2026 Joshua Montclair (Trevol).
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"""Ayudas de las pruebas: un servidor HTTP falso en localhost (pasa por urllib3 de verdad) y un cliente con reloj y azar inyectados."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import threading
|
|
7
|
+
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any, Callable, Optional
|
|
10
|
+
|
|
11
|
+
import pytest
|
|
12
|
+
|
|
13
|
+
from trevol import Trevol
|
|
14
|
+
|
|
15
|
+
DATOS = Path(__file__).parent / "datos"
|
|
16
|
+
HEX = "3f0c1a2b" * 8
|
|
17
|
+
LLAVE_TEST = "tvl_test_" + "a1" * 32
|
|
18
|
+
LLAVE_LIVE = "tvl_live_" + "b2" * 32
|
|
19
|
+
DOCUMENTO = {"id": "inv_" + HEX[:32], "object": "invoice", "environment": "test", "status": "accepted", "number": "SETP990012345",
|
|
20
|
+
"external_id": "VENTA-2026-000123"}
|
|
21
|
+
ENTRADA_FACTURA = {"external_id": "VENTA-2026-000123", "customer": {"final_consumer": True},
|
|
22
|
+
"items": [{"description": "Café", "quantity": "1", "unit_price": "1000"}]}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def datos(nombre: str) -> Any:
|
|
26
|
+
return json.loads((DATOS / nombre).read_text(encoding="utf-8"))
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class Respuesta:
|
|
30
|
+
def __init__(self, estado: int, cuerpo: Any = None, cabeceras: Optional[dict] = None, crudo: Optional[bytes] = None,
|
|
31
|
+
tipo: str = "application/json"):
|
|
32
|
+
self.estado, self.cabeceras, self.tipo = estado, cabeceras or {}, tipo
|
|
33
|
+
self.crudo = crudo if crudo is not None else (b"" if cuerpo is None else json.dumps(cuerpo).encode("utf-8"))
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def json_(estado: int, cuerpo: Any, cabeceras: Optional[dict] = None) -> Respuesta:
|
|
37
|
+
return Respuesta(estado, cuerpo, cabeceras)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def problema(estado: int, code: str, extra: Optional[dict] = None, cabeceras: Optional[dict] = None) -> Respuesta:
|
|
41
|
+
cuerpo = {"type": f"https://platform.trevol.si/docs/errores#{code}", "title": "Título", "status": estado, "code": code,
|
|
42
|
+
"retryable": False, "detail": f"Detalle de {code}", "request_id": "req_" + "c3" * 12, **(extra or {})}
|
|
43
|
+
return Respuesta(estado, cuerpo, cabeceras, tipo="application/problem+json")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
CORTAR = object() # el servidor cierra la conexión sin responder (un fallo de red)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class Servidor:
|
|
50
|
+
def __init__(self) -> None:
|
|
51
|
+
self.respuestas: list[Any] = []
|
|
52
|
+
self.peticiones: list[dict] = []
|
|
53
|
+
servidor = self
|
|
54
|
+
|
|
55
|
+
class Manejador(BaseHTTPRequestHandler):
|
|
56
|
+
protocol_version = "HTTP/1.1"
|
|
57
|
+
|
|
58
|
+
def log_message(self, *a: Any) -> None:
|
|
59
|
+
pass
|
|
60
|
+
|
|
61
|
+
def _atender(self) -> None:
|
|
62
|
+
largo = int(self.headers.get("content-length") or 0)
|
|
63
|
+
cuerpo = self.rfile.read(largo) if largo else b""
|
|
64
|
+
peticion = {"metodo": self.command, "ruta": self.path, "cabeceras": {k.lower(): v for k, v in self.headers.items()},
|
|
65
|
+
"cuerpo": cuerpo.decode("utf-8", "replace")}
|
|
66
|
+
servidor.peticiones.append(peticion)
|
|
67
|
+
i = min(len(servidor.peticiones) - 1, len(servidor.respuestas) - 1)
|
|
68
|
+
r = servidor.respuestas[i]
|
|
69
|
+
r = r(peticion) if callable(r) else r # una función recibe la petición y contesta según la ruta
|
|
70
|
+
if r is CORTAR:
|
|
71
|
+
self.close_connection = True
|
|
72
|
+
self.connection.close()
|
|
73
|
+
return
|
|
74
|
+
self.send_response(r.estado)
|
|
75
|
+
self.send_header("Content-Type", r.tipo)
|
|
76
|
+
self.send_header("Content-Length", str(len(r.crudo)))
|
|
77
|
+
for k, v in r.cabeceras.items():
|
|
78
|
+
self.send_header(k, v)
|
|
79
|
+
self.end_headers()
|
|
80
|
+
self.wfile.write(r.crudo)
|
|
81
|
+
|
|
82
|
+
do_GET = do_POST = do_PUT = do_PATCH = do_DELETE = _atender
|
|
83
|
+
|
|
84
|
+
self._http = ThreadingHTTPServer(("127.0.0.1", 0), Manejador)
|
|
85
|
+
self._http.daemon_threads = True
|
|
86
|
+
self.url = f"http://127.0.0.1:{self._http.server_address[1]}/v1"
|
|
87
|
+
threading.Thread(target=lambda: self._http.serve_forever(poll_interval=0.01), daemon=True).start()
|
|
88
|
+
|
|
89
|
+
def cerrar(self) -> None:
|
|
90
|
+
self._http.shutdown()
|
|
91
|
+
self._http.server_close()
|
|
92
|
+
|
|
93
|
+
def vaciar(self, *respuestas: Any) -> "Servidor":
|
|
94
|
+
self.respuestas, self.peticiones = list(respuestas), []
|
|
95
|
+
return self
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@pytest.fixture()
|
|
99
|
+
def servidor():
|
|
100
|
+
s = Servidor()
|
|
101
|
+
yield s
|
|
102
|
+
s.cerrar()
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class Banco:
|
|
106
|
+
"""Un cliente contra el servidor falso, con las esperas anotadas (no se duerme) y el azar en 1 (factor 1,0)."""
|
|
107
|
+
|
|
108
|
+
def __init__(self, servidor: Servidor, *respuestas: Any, llave: str = LLAVE_TEST, **opciones: Any) -> None:
|
|
109
|
+
self.servidor = servidor.vaciar(*respuestas)
|
|
110
|
+
self.esperas: list[float] = []
|
|
111
|
+
self._n = 0
|
|
112
|
+
|
|
113
|
+
def uuid() -> str:
|
|
114
|
+
self._n += 1
|
|
115
|
+
return f"00000000-0000-4000-8000-{self._n:012d}"
|
|
116
|
+
|
|
117
|
+
self.trevol = Trevol(llave, base_url=servidor.url, _internos={"sleep": self.esperas.append, "random": lambda: 1.0, "uuid": uuid},
|
|
118
|
+
**opciones)
|
|
119
|
+
|
|
120
|
+
@property
|
|
121
|
+
def peticiones(self) -> list[dict]:
|
|
122
|
+
return self.servidor.peticiones
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@pytest.fixture()
|
|
126
|
+
def banco(servidor):
|
|
127
|
+
return lambda *r, **kw: Banco(servidor, *r, **kw)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"descripcion": "A mano (docs/api/43-sdks-y-documentacion.md §2.6.5). Cada code de Problem pertenece a una clase de error de los SDK; preparar.py falla si el enum de Problem tiene un code que no está aquí. Un code que el SDK no conoce (la API sumó uno) da la clase base, nunca rompe.",
|
|
3
|
+
"base": "TrevolError",
|
|
4
|
+
"clases": {
|
|
5
|
+
"AuthenticationError": ["authentication_required", "invalid_api_key"],
|
|
6
|
+
"PermissionDeniedError": ["permission_denied"],
|
|
7
|
+
"NotFoundError": ["not_found"],
|
|
8
|
+
"InvalidRequestError": ["method_not_allowed", "unsupported_version", "invalid_json", "invalid_request", "idempotency_key_required"],
|
|
9
|
+
"IdempotencyError": ["idempotency_key_reused"],
|
|
10
|
+
"ValidationError": ["validation_error"],
|
|
11
|
+
"ConflictError": ["external_id_in_use", "invalid_state", "duplicate_document", "request_in_progress", "previous_event_pending", "file_unavailable", "webhook_limit_reached"],
|
|
12
|
+
"PreconditionError": ["company_not_enabled", "numbering_unavailable", "certificate_problem", "tacit_acceptance_not_due"],
|
|
13
|
+
"DianRejectedError": ["dian_rejected"],
|
|
14
|
+
"BalanceError": ["insufficient_balance", "top_up_below_minimum", "top_up_above_maximum", "top_ups_unavailable"],
|
|
15
|
+
"RateLimitError": ["rate_limited"],
|
|
16
|
+
"DianUnavailableError": ["dian_unavailable"],
|
|
17
|
+
"InternalServerError": ["internal_error"]
|
|
18
|
+
},
|
|
19
|
+
"estados": {
|
|
20
|
+
"400": "InvalidRequestError",
|
|
21
|
+
"401": "AuthenticationError",
|
|
22
|
+
"402": "BalanceError",
|
|
23
|
+
"403": "PermissionDeniedError",
|
|
24
|
+
"404": "NotFoundError",
|
|
25
|
+
"409": "ConflictError",
|
|
26
|
+
"422": "ValidationError",
|
|
27
|
+
"429": "RateLimitError",
|
|
28
|
+
"5xx": "InternalServerError"
|
|
29
|
+
},
|
|
30
|
+
"sinRespuesta": "APIConnectionError",
|
|
31
|
+
"configuracion": "ConfigurationError",
|
|
32
|
+
"webhook": "WebhookVerificationError"
|
|
33
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"balance.low": "BalanceEventData",
|
|
3
|
+
"certificate.expiring": "CertificateEventData",
|
|
4
|
+
"credit_note.accepted": "DocumentEventData",
|
|
5
|
+
"credit_note.delivered": "DocumentEventData",
|
|
6
|
+
"credit_note.email_bounced": "DocumentEventData",
|
|
7
|
+
"credit_note.email_failed": "DocumentEventData",
|
|
8
|
+
"credit_note.error": "DocumentEventData",
|
|
9
|
+
"credit_note.rejected": "DocumentEventData",
|
|
10
|
+
"debit_note.accepted": "DocumentEventData",
|
|
11
|
+
"debit_note.delivered": "DocumentEventData",
|
|
12
|
+
"debit_note.email_bounced": "DocumentEventData",
|
|
13
|
+
"debit_note.email_failed": "DocumentEventData",
|
|
14
|
+
"debit_note.error": "DocumentEventData",
|
|
15
|
+
"debit_note.rejected": "DocumentEventData",
|
|
16
|
+
"equivalent_document.accepted": "EquivalentDocumentEventData",
|
|
17
|
+
"equivalent_document.delivered": "EquivalentDocumentEventData",
|
|
18
|
+
"equivalent_document.email_bounced": "EquivalentDocumentEventData",
|
|
19
|
+
"equivalent_document.email_failed": "EquivalentDocumentEventData",
|
|
20
|
+
"equivalent_document.error": "EquivalentDocumentEventData",
|
|
21
|
+
"equivalent_document.rejected": "EquivalentDocumentEventData",
|
|
22
|
+
"equivalent_document_adjustment.accepted": "EquivalentDocumentEventData",
|
|
23
|
+
"equivalent_document_adjustment.delivered": "EquivalentDocumentEventData",
|
|
24
|
+
"equivalent_document_adjustment.email_bounced": "EquivalentDocumentEventData",
|
|
25
|
+
"equivalent_document_adjustment.email_failed": "EquivalentDocumentEventData",
|
|
26
|
+
"equivalent_document_adjustment.error": "EquivalentDocumentEventData",
|
|
27
|
+
"equivalent_document_adjustment.rejected": "EquivalentDocumentEventData",
|
|
28
|
+
"invoice.accepted": "DocumentEventData",
|
|
29
|
+
"invoice.delivered": "DocumentEventData",
|
|
30
|
+
"invoice.email_bounced": "DocumentEventData",
|
|
31
|
+
"invoice.email_failed": "DocumentEventData",
|
|
32
|
+
"invoice.error": "DocumentEventData",
|
|
33
|
+
"invoice.rejected": "DocumentEventData",
|
|
34
|
+
"numbering.expiring": "NumberingExpiringEventData",
|
|
35
|
+
"numbering.low": "NumberingLowEventData",
|
|
36
|
+
"payroll.accepted": "PayrollEventData",
|
|
37
|
+
"payroll.error": "PayrollEventData",
|
|
38
|
+
"payroll.rejected": "PayrollEventData",
|
|
39
|
+
"payroll_adjustment.accepted": "PayrollEventData",
|
|
40
|
+
"payroll_adjustment.error": "PayrollEventData",
|
|
41
|
+
"payroll_adjustment.rejected": "PayrollEventData",
|
|
42
|
+
"radian_event.accepted": "RadianEventData",
|
|
43
|
+
"radian_event.error": "RadianEventData",
|
|
44
|
+
"radian_event.rejected": "RadianEventData",
|
|
45
|
+
"support_document.accepted": "SupportDocumentEventData",
|
|
46
|
+
"support_document.error": "SupportDocumentEventData",
|
|
47
|
+
"support_document.rejected": "SupportDocumentEventData",
|
|
48
|
+
"support_document_adjustment.accepted": "SupportDocumentEventData",
|
|
49
|
+
"support_document_adjustment.error": "SupportDocumentEventData",
|
|
50
|
+
"support_document_adjustment.rejected": "SupportDocumentEventData",
|
|
51
|
+
"webhook.test": "TestEventData"
|
|
52
|
+
}
|