├── .clippy.toml ├── .devcontainer ├── .env ├── Dockerfile ├── devcontainer.json ├── docker-compose.yml ├── entrypoint.sh ├── onCreateCommand.sh ├── postCreateCommand.sh └── postStartCommand.sh ├── .dockerignore ├── .github ├── FUNDING.yml ├── actions │ └── install-dependencies │ │ └── action.yml ├── dependabot.yml └── workflows │ ├── ci-image-build.yaml │ ├── ci-repo.yml │ ├── image-build.yaml │ └── pr-checks.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── Cargo.lock ├── Cargo.toml ├── Dockerfile.server ├── Dockerfile.ui ├── LICENSE ├── README.md ├── entrypoint.ui.sh ├── openapi.yaml ├── rust-toolchain.toml ├── server ├── .gitignore ├── api │ ├── Cargo.toml │ └── src │ │ ├── db.rs │ │ ├── lib.rs │ │ └── system │ │ ├── credentials.rs │ │ ├── document.rs │ │ ├── error.rs │ │ └── mod.rs ├── cli │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── common │ ├── Cargo.toml │ └── src │ │ ├── config.rs │ │ └── lib.rs ├── deploy │ └── compose │ │ ├── compose.yaml │ │ └── scripts │ │ ├── keycloak │ │ └── setup.sh │ │ └── minio │ │ └── setup.sh ├── entity │ ├── Cargo.toml │ └── src │ │ ├── credentials.rs │ │ ├── delivery.rs │ │ ├── document.rs │ │ ├── keystore.rs │ │ ├── keystore_config.rs │ │ ├── lib.rs │ │ ├── mod.rs │ │ ├── prelude.rs │ │ └── send_rule.rs ├── migration │ ├── Cargo.toml │ ├── README.md │ └── src │ │ ├── lib.rs │ │ ├── m20240101_104121_create_document.rs │ │ ├── m20240113_213636_create_keystore.rs │ │ ├── m20240113_213657_create_keystore_config.rs │ │ ├── m20240114_154538_create_credentials.rs │ │ ├── m20240117_142858_create_send_rule.rs │ │ ├── m20240717_214515_create_delivery.rs │ │ └── main.rs ├── server │ ├── Cargo.toml │ └── src │ │ ├── dto.rs │ │ ├── lib.rs │ │ ├── openapi.rs │ │ └── server │ │ ├── credentials.rs │ │ ├── document.rs │ │ ├── health.rs │ │ └── mod.rs ├── signature │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── storage │ ├── Cargo.toml │ └── src │ │ ├── config.rs │ │ └── lib.rs ├── ui │ ├── .gitignore │ ├── biome.json │ ├── branding │ │ ├── favicon.ico │ │ ├── images │ │ │ ├── logo.png │ │ │ ├── logo192.png │ │ │ ├── logo512.png │ │ │ └── masthead-logo.svg │ │ ├── manifest.json │ │ └── strings.json │ ├── client │ │ ├── index.html │ │ ├── package.json │ │ ├── public │ │ │ └── .gitkeep │ │ ├── rsbuild.config.ts │ │ ├── src │ │ │ ├── app │ │ │ │ ├── Constants.ts │ │ │ │ ├── assets │ │ │ │ │ ├── avatar.svg │ │ │ │ │ ├── patternfly_avatar.jpg │ │ │ │ │ └── pfbg-icon.svg │ │ │ │ ├── common │ │ │ │ │ └── types.ts │ │ │ │ ├── components │ │ │ │ │ ├── Notifications.tsx │ │ │ │ │ ├── NotificationsContext.tsx │ │ │ │ │ ├── NotificationsProvider.tsx │ │ │ │ │ └── PageDrawerContext.tsx │ │ │ │ └── layout │ │ │ │ │ ├── about.tsx │ │ │ │ │ ├── default-layout.tsx │ │ │ │ │ ├── header.tsx │ │ │ │ │ ├── index.ts │ │ │ │ │ └── sidebar.tsx │ │ │ ├── assets │ │ │ │ └── favicon.svg │ │ │ ├── client │ │ │ │ ├── helpers.ts │ │ │ │ ├── models.ts │ │ │ │ └── rest.ts │ │ │ ├── env.d.ts │ │ │ ├── index.tsx │ │ │ ├── queries │ │ │ │ └── credentials.ts │ │ │ ├── routeTree.gen.ts │ │ │ └── routes │ │ │ │ ├── __root.tsx │ │ │ │ ├── about.tsx │ │ │ │ ├── credentials.tsx │ │ │ │ └── index.tsx │ │ └── tsconfig.json │ ├── common │ │ ├── package.json │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── branding-strings-stub.json │ │ │ ├── branding.ts │ │ │ ├── environment.ts │ │ │ ├── index.ts │ │ │ └── proxies.ts │ │ └── tsconfig.json │ ├── crate │ │ ├── Cargo.toml │ │ ├── build.rs │ │ └── src │ │ │ └── lib.rs │ ├── package-lock.json │ ├── package.json │ └── server │ │ ├── package.json │ │ ├── rollup.config.js │ │ └── src │ │ └── index.ts └── xtask │ ├── Cargo.toml │ └── src │ ├── main.rs │ └── openapi.rs ├── xbuilder ├── Cargo.toml ├── README.md ├── build.rs ├── resources │ └── templates │ │ ├── renderer │ │ ├── creditNote.xml │ │ ├── debitNote.xml │ │ ├── despatchAdvice.xml │ │ ├── invoice.xml │ │ ├── perception.xml │ │ ├── retention.xml │ │ ├── summaryDocuments.xml │ │ └── voidedDocuments.xml │ │ └── ubl │ │ ├── common │ │ └── signature.xml │ │ ├── standard │ │ ├── guia │ │ │ └── despatch-advice.ftl │ │ └── include │ │ │ ├── address.xml │ │ │ ├── contact.xml │ │ │ ├── customer.xml │ │ │ ├── document-line.xml │ │ │ ├── documentos-relacionados.xml │ │ │ ├── general-data.xml │ │ │ ├── guias.xml │ │ │ ├── monetary-total.xml │ │ │ ├── namespaces.xml │ │ │ ├── note │ │ │ └── invoice-reference.xml │ │ │ ├── payment-terms.xml │ │ │ ├── supplier.xml │ │ │ ├── tax-total.xml │ │ │ └── ubl-extensions.xml │ │ └── sunat │ │ └── include │ │ ├── agent-party.xml │ │ ├── receiver-party.xml │ │ └── supplier.xml ├── src │ ├── catalogs.rs │ ├── enricher │ │ ├── bounds │ │ │ ├── detalle │ │ │ │ ├── cantidad.rs │ │ │ │ ├── icb.rs │ │ │ │ ├── icb_aplica.rs │ │ │ │ ├── icb_tasa.rs │ │ │ │ ├── igv.rs │ │ │ │ ├── igv_base_imponible.rs │ │ │ │ ├── igv_tasa.rs │ │ │ │ ├── igv_tipo.rs │ │ │ │ ├── isc.rs │ │ │ │ ├── isc_base_imponible.rs │ │ │ │ ├── isc_tasa.rs │ │ │ │ ├── isc_tipo.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── precio.rs │ │ │ │ ├── precio_con_impuestos.rs │ │ │ │ ├── precio_referencia.rs │ │ │ │ ├── precio_referencia_tipo.rs │ │ │ │ ├── total_impuestos.rs │ │ │ │ └── unidad_medida.rs │ │ │ ├── fecha_emision.rs │ │ │ ├── firmante.rs │ │ │ ├── icb.rs │ │ │ ├── igv.rs │ │ │ ├── invoice │ │ │ │ ├── anticipos.rs │ │ │ │ ├── descuentos.rs │ │ │ │ ├── detraccion.rs │ │ │ │ ├── direccion_entrega.rs │ │ │ │ ├── forma_de_pago.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── percepcion.rs │ │ │ │ ├── tipo_comprobante.rs │ │ │ │ ├── tipo_operacion.rs │ │ │ │ └── total_importe.rs │ │ │ ├── ivap.rs │ │ │ ├── leyendas.rs │ │ │ ├── mod.rs │ │ │ ├── moneda.rs │ │ │ ├── note │ │ │ │ ├── creditnote │ │ │ │ │ ├── mod.rs │ │ │ │ │ └── tipo_nota.rs │ │ │ │ ├── debitnote │ │ │ │ │ ├── mod.rs │ │ │ │ │ └── tipo_nota.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── tipo_comprobante_afectado.rs │ │ │ │ └── total_importe.rs │ │ │ ├── proveedor.rs │ │ │ ├── serie_numero.rs │ │ │ └── total_impuestos.rs │ │ ├── fill.rs │ │ ├── mod.rs │ │ ├── process.rs │ │ ├── rules │ │ │ ├── mod.rs │ │ │ ├── phase1fill │ │ │ │ ├── detalle │ │ │ │ │ ├── detalles.rs │ │ │ │ │ ├── icb_tasa.rs │ │ │ │ │ ├── igv_tasa.rs │ │ │ │ │ ├── igv_tipo.rs │ │ │ │ │ ├── isc_tasa.rs │ │ │ │ │ ├── isc_tipo.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── precio_referencia_tipo.rs │ │ │ │ │ └── unidad_medida.rs │ │ │ │ ├── fecha_emision.rs │ │ │ │ ├── firmante.rs │ │ │ │ ├── icb_tasa.rs │ │ │ │ ├── igv_tasa.rs │ │ │ │ ├── invoice │ │ │ │ │ ├── anticipos.rs │ │ │ │ │ ├── descuentos.rs │ │ │ │ │ ├── forma_de_pago.rs │ │ │ │ │ ├── leyenda.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── tipo_comprobante.rs │ │ │ │ │ └── tipo_operacion.rs │ │ │ │ ├── ivap_tasa.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── moneda.rs │ │ │ │ ├── note │ │ │ │ │ ├── creditnote │ │ │ │ │ │ ├── mod.rs │ │ │ │ │ │ └── tipo_nota.rs │ │ │ │ │ ├── debitnote │ │ │ │ │ │ ├── mod.rs │ │ │ │ │ │ └── tipo_nota.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ └── tipo_comprobante_afectado.rs │ │ │ │ └── proveedor.rs │ │ │ ├── phase2process │ │ │ │ ├── detalle │ │ │ │ │ ├── detalles.rs │ │ │ │ │ ├── icb.rs │ │ │ │ │ ├── icb_aplica.rs │ │ │ │ │ ├── igv.rs │ │ │ │ │ ├── igv_base_imponible.rs │ │ │ │ │ ├── isc.rs │ │ │ │ │ ├── isc_base_imponible.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── precio.rs │ │ │ │ │ ├── precio_con_impuestos.rs │ │ │ │ │ ├── precio_referencia.rs │ │ │ │ │ └── total_impuestos.rs │ │ │ │ └── mod.rs │ │ │ └── phase3summary │ │ │ │ ├── invoice │ │ │ │ ├── detraccion.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── percepcion.rs │ │ │ │ ├── total_importe.rs │ │ │ │ └── total_impuestos.rs │ │ │ │ ├── leyenda.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── note │ │ │ │ ├── mod.rs │ │ │ │ ├── total_importe.rs │ │ │ │ └── total_impuestos.rs │ │ │ │ └── utils.rs │ │ └── summary.rs │ ├── lib.rs │ ├── models │ │ ├── common.rs │ │ ├── credit_note.rs │ │ ├── debit_note.rs │ │ ├── invoice.rs │ │ └── mod.rs │ ├── prelude.rs │ └── renderer │ │ └── mod.rs └── tests │ ├── common │ └── mod.rs │ ├── credit_note.rs │ ├── credit_note_isc.rs │ ├── credit_note_orden_de_compra.rs │ ├── debit_note.rs │ ├── invoice.rs │ ├── invoice_anticipos.rs │ ├── invoice_descuentos.rs │ ├── invoice_detraccion.rs │ ├── invoice_direccion_entrega.rs │ ├── invoice_documento_relacionado.rs │ ├── invoice_fecha_vencimiento.rs │ ├── invoice_forma_pago.rs │ ├── invoice_guias.rs │ ├── invoice_igv_tipo.rs │ ├── invoice_isc.rs │ ├── invoice_issue30.rs │ ├── invoice_moneda.rs │ ├── invoice_orden_compra.rs │ ├── invoice_percepcion.rs │ └── resources │ ├── certificates │ ├── LLAMA-PE-CERTIFICADO-DEMO-10467793549.pfx │ ├── private.key │ └── public.cer │ ├── e2e │ ├── homologacion │ │ ├── Group1Test │ │ │ ├── factura1Con3Items.xml │ │ │ ├── factura2Con2Items.xml │ │ │ ├── factura3Con1Items.xml │ │ │ ├── factura4Con5Items.xml │ │ │ ├── factura5Con4Items.xml │ │ │ ├── notaDeCreditoDeFactura2.xml │ │ │ ├── notaDeCreditoDeFactura3.xml │ │ │ ├── notaDeCreditoDeFactura4.xml │ │ │ ├── notaDeDebitoDeFactura2.xml │ │ │ ├── notaDeDebitoDeFactura3.xml │ │ │ └── notaDeDebitoDeFactura4.xml │ │ ├── Group2ExoneradoTest │ │ │ ├── factura1Con1Items.xml │ │ │ ├── factura2Con4Items.xml │ │ │ ├── factura3Con7Items.xml │ │ │ ├── factura4Con5Items.xml │ │ │ ├── factura5Con6Items.xml │ │ │ ├── notaDeCreditoDeFactura1.xml │ │ │ ├── notaDeCreditoDeFactura3.xml │ │ │ ├── notaDeCreditoDeFactura5.xml │ │ │ ├── notaDeDebitoDeFactura1.xml │ │ │ ├── notaDeDebitoDeFactura3.xml │ │ │ └── notaDeDebitoDeFactura5.xml │ │ ├── Group2InafectoTest │ │ │ ├── factura1Con1Items.xml │ │ │ ├── factura2Con4Items.xml │ │ │ ├── factura3Con7Items.xml │ │ │ ├── factura4Con5Items.xml │ │ │ ├── factura5Con6Items.xml │ │ │ ├── notaDeCreditoDeFactura1.xml │ │ │ ├── notaDeCreditoDeFactura3.xml │ │ │ ├── notaDeCreditoDeFactura5.xml │ │ │ ├── notaDeDebitoDeFactura1.xml │ │ │ ├── notaDeDebitoDeFactura3.xml │ │ │ └── notaDeDebitoDeFactura5.xml │ │ ├── Group3Test │ │ │ ├── factura1Con7Items.xml │ │ │ ├── factura2Con2Items.xml │ │ │ ├── factura3Con5Items.xml │ │ │ ├── factura4Con4Items.xml │ │ │ └── factura5Con3Items.xml │ │ ├── Group4Test │ │ │ ├── factura1Con2Items.xml │ │ │ ├── factura2Con2Items.xml │ │ │ ├── factura3Con4Items.xml │ │ │ ├── factura4Con3Items.xml │ │ │ ├── factura5Con5Items.xml │ │ │ ├── notaDeCreditoDeFactura2.xml │ │ │ ├── notaDeCreditoDeFactura3.xml │ │ │ ├── notaDeCreditoDeFactura5.xml │ │ │ ├── notaDeDebitoDeFactura2.xml │ │ │ ├── notaDeDebitoDeFactura3.xml │ │ │ └── notaDeDebitoDeFactura5.xml │ │ ├── Group5Test │ │ │ ├── factura1Con5Items.xml │ │ │ ├── notaDeCreditoDeFactura1.xml │ │ │ └── notaDeDebitoDeFactura2.xml │ │ ├── Group6Test │ │ │ ├── factura1Con5Items.xml │ │ │ ├── notaDeCreditoDeFactura1.xml │ │ │ └── notaDeDebitoDeFactura1.xml │ │ └── Group7Test │ │ │ ├── factura1Con5Items.xml │ │ │ ├── notaDeCreditoDeFactura1.xml │ │ │ └── notaDeDebitoDeFactura2.xml │ └── renderer │ │ ├── creditnote │ │ ├── CreditNoteIscTest │ │ │ ├── isc_aplicacionAlMontoFijo.xml │ │ │ ├── isc_sistemaAlValor.xml │ │ │ └── isc_sistemaDePreciosDeVentalAlPublico.xml │ │ ├── CreditNoteOrdenDeCompraTest │ │ │ └── ordenDeCompra.xml │ │ └── CreditNoteTest │ │ │ └── MinData_RUC.xml │ │ ├── debitnote │ │ └── DebitNoteTest │ │ │ └── MinData_RUC.xml │ │ ├── despatchadvice │ │ └── DespatchAdviceTest │ │ │ └── minData.xml │ │ ├── invoice │ │ ├── InvoiceAnticiposTest │ │ │ └── minAnticipos.xml │ │ ├── InvoiceDescuentosTest │ │ │ ├── descuentoGlobal.xml │ │ │ ├── descuentoGlobal_tipo02.xml │ │ │ └── descuentoGlobal_tipo03.xml │ │ ├── InvoiceDetraccionTest │ │ │ └── detraccion.xml │ │ ├── InvoiceDireccionEntregaTest │ │ │ ├── direccionEntregaFull.xml │ │ │ └── direccionEntregaMin.xml │ │ ├── InvoiceDocumentoRelacionadoTest │ │ │ └── documentoRelacionado.xml │ │ ├── InvoiceFechaVencimientoTest │ │ │ └── conFechaVencimiento.xml │ │ ├── InvoiceFormaPagoTest │ │ │ ├── conFormaPago.xml │ │ │ └── sinFormaPago.xml │ │ ├── InvoiceGuiasTest │ │ │ └── guiaSerieT.xml │ │ ├── InvoiceIscTest │ │ │ ├── isc_aplicacionAlMontoFijo.xml │ │ │ ├── isc_mixedTipoIgv.xml │ │ │ ├── isc_precioConImpuestos.xml │ │ │ ├── isc_sistemaAlValor.xml │ │ │ └── isc_sistemaDePreciosDeVentalAlPublico.xml │ │ ├── InvoiceIssue30Test │ │ │ ├── with-precioUnitario-ICB.xml │ │ │ ├── with-precioUnitario-conImpuestos-ICB.xml │ │ │ ├── with-precioUnitario.xml │ │ │ └── with-precioUnitarioConImpuestos.xml │ │ ├── InvoiceMonedaTest │ │ │ └── customMoneda.xml │ │ ├── InvoiceOrdeDeCompraTest │ │ │ └── ordenDeCompra.xml │ │ ├── InvoicePercepcionTest │ │ │ └── percepcion.xml │ │ ├── InvoiceTest │ │ │ ├── customClienteDireccionAndContacto.xml │ │ │ ├── customCodigoLocal.xml │ │ │ ├── customFechaEmision.xml │ │ │ ├── customFirmante.xml │ │ │ ├── customProveedorDireccionAndContacto.xml │ │ │ ├── customUnidadMedida.xml │ │ │ └── icb.xml │ │ └── InvoiceTipoIgvTest │ │ │ ├── invoice_pr_EXONERADO_OPERACION_ONEROSA.xml │ │ │ ├── invoice_pr_EXONERADO_TRANSFERENCIA_GRATUITA.xml │ │ │ ├── invoice_pr_EXPORTACION.xml │ │ │ ├── invoice_pr_GRAVADO_BONIFICACIONES.xml │ │ │ ├── invoice_pr_GRAVADO_IVAP.xml │ │ │ ├── invoice_pr_GRAVADO_OPERACION_ONEROSA.xml │ │ │ ├── invoice_pr_GRAVADO_RETIRO.xml │ │ │ ├── invoice_pr_GRAVADO_RETIRO_POR_DONACION.xml │ │ │ ├── invoice_pr_GRAVADO_RETIRO_POR_ENTREGA_A_TRABAJADORES.xml │ │ │ ├── invoice_pr_GRAVADO_RETIRO_POR_PREMIO.xml │ │ │ ├── invoice_pr_GRAVADO_RETIRO_POR_PUBLICIDAD.xml │ │ │ ├── invoice_pr_INAFECTO_OPERACION_ONEROSA.xml │ │ │ ├── invoice_pr_INAFECTO_RETIRO.xml │ │ │ ├── invoice_pr_INAFECTO_RETIRO_POR_BONIFICACION.xml │ │ │ ├── invoice_pr_INAFECTO_RETIRO_POR_CONVENIO_COLECTIVO.xml │ │ │ ├── invoice_pr_INAFECTO_RETIRO_POR_MUESTRAS_MEDICAS.xml │ │ │ ├── invoice_pr_INAFECTO_RETIRO_POR_PREMIO.xml │ │ │ ├── invoice_pr_INAFECTO_RETIRO_POR_PUBLICIDAD.xml │ │ │ ├── invoice_pr_icb_EXONERADO_OPERACION_ONEROSA.xml │ │ │ ├── invoice_pr_icb_EXONERADO_TRANSFERENCIA_GRATUITA.xml │ │ │ ├── invoice_pr_icb_EXPORTACION.xml │ │ │ ├── invoice_pr_icb_GRAVADO_BONIFICACIONES.xml │ │ │ ├── invoice_pr_icb_GRAVADO_IVAP.xml │ │ │ ├── invoice_pr_icb_GRAVADO_OPERACION_ONEROSA.xml │ │ │ ├── invoice_pr_icb_GRAVADO_RETIRO.xml │ │ │ ├── invoice_pr_icb_GRAVADO_RETIRO_POR_DONACION.xml │ │ │ ├── invoice_pr_icb_GRAVADO_RETIRO_POR_ENTREGA_A_TRABAJADORES.xml │ │ │ ├── invoice_pr_icb_GRAVADO_RETIRO_POR_PREMIO.xml │ │ │ ├── invoice_pr_icb_GRAVADO_RETIRO_POR_PUBLICIDAD.xml │ │ │ ├── invoice_pr_icb_INAFECTO_OPERACION_ONEROSA.xml │ │ │ ├── invoice_pr_icb_INAFECTO_RETIRO.xml │ │ │ ├── invoice_pr_icb_INAFECTO_RETIRO_POR_BONIFICACION.xml │ │ │ ├── invoice_pr_icb_INAFECTO_RETIRO_POR_CONVENIO_COLECTIVO.xml │ │ │ ├── invoice_pr_icb_INAFECTO_RETIRO_POR_MUESTRAS_MEDICAS.xml │ │ │ ├── invoice_pr_icb_INAFECTO_RETIRO_POR_PREMIO.xml │ │ │ ├── invoice_pr_icb_INAFECTO_RETIRO_POR_PUBLICIDAD.xml │ │ │ ├── invoice_pr_icb_isc_EXONERADO_OPERACION_ONEROSA.xml │ │ │ ├── invoice_pr_icb_isc_EXONERADO_TRANSFERENCIA_GRATUITA.xml │ │ │ ├── invoice_pr_icb_isc_EXPORTACION.xml │ │ │ ├── invoice_pr_icb_isc_GRAVADO_BONIFICACIONES.xml │ │ │ ├── invoice_pr_icb_isc_GRAVADO_IVAP.xml │ │ │ ├── invoice_pr_icb_isc_GRAVADO_OPERACION_ONEROSA.xml │ │ │ ├── invoice_pr_icb_isc_GRAVADO_RETIRO.xml │ │ │ ├── invoice_pr_icb_isc_GRAVADO_RETIRO_POR_DONACION.xml │ │ │ ├── invoice_pr_icb_isc_GRAVADO_RETIRO_POR_ENTREGA_A_TRABAJADORES.xml │ │ │ ├── invoice_pr_icb_isc_GRAVADO_RETIRO_POR_PREMIO.xml │ │ │ ├── invoice_pr_icb_isc_GRAVADO_RETIRO_POR_PUBLICIDAD.xml │ │ │ ├── invoice_pr_icb_isc_INAFECTO_OPERACION_ONEROSA.xml │ │ │ ├── invoice_pr_icb_isc_INAFECTO_RETIRO.xml │ │ │ ├── invoice_pr_icb_isc_INAFECTO_RETIRO_POR_BONIFICACION.xml │ │ │ ├── invoice_pr_icb_isc_INAFECTO_RETIRO_POR_CONVENIO_COLECTIVO.xml │ │ │ ├── invoice_pr_icb_isc_INAFECTO_RETIRO_POR_MUESTRAS_MEDICAS.xml │ │ │ ├── invoice_pr_icb_isc_INAFECTO_RETIRO_POR_PREMIO.xml │ │ │ ├── invoice_pr_icb_isc_INAFECTO_RETIRO_POR_PUBLICIDAD.xml │ │ │ ├── invoice_pu_EXONERADO_OPERACION_ONEROSA.xml │ │ │ ├── invoice_pu_EXONERADO_TRANSFERENCIA_GRATUITA.xml │ │ │ ├── invoice_pu_EXPORTACION.xml │ │ │ ├── invoice_pu_GRAVADO_BONIFICACIONES.xml │ │ │ ├── invoice_pu_GRAVADO_IVAP.xml │ │ │ ├── invoice_pu_GRAVADO_OPERACION_ONEROSA.xml │ │ │ ├── invoice_pu_GRAVADO_RETIRO.xml │ │ │ ├── invoice_pu_GRAVADO_RETIRO_POR_DONACION.xml │ │ │ ├── invoice_pu_GRAVADO_RETIRO_POR_ENTREGA_A_TRABAJADORES.xml │ │ │ ├── invoice_pu_GRAVADO_RETIRO_POR_PREMIO.xml │ │ │ ├── invoice_pu_GRAVADO_RETIRO_POR_PUBLICIDAD.xml │ │ │ ├── invoice_pu_INAFECTO_OPERACION_ONEROSA.xml │ │ │ ├── invoice_pu_INAFECTO_RETIRO.xml │ │ │ ├── invoice_pu_INAFECTO_RETIRO_POR_BONIFICACION.xml │ │ │ ├── invoice_pu_INAFECTO_RETIRO_POR_CONVENIO_COLECTIVO.xml │ │ │ ├── invoice_pu_INAFECTO_RETIRO_POR_MUESTRAS_MEDICAS.xml │ │ │ ├── invoice_pu_INAFECTO_RETIRO_POR_PREMIO.xml │ │ │ └── invoice_pu_INAFECTO_RETIRO_POR_PUBLICIDAD.xml │ │ ├── perception │ │ └── PerceptionTest │ │ │ └── perception_simple.xml │ │ ├── retention │ │ └── RetentionTest │ │ │ └── retention_simple.xml │ │ ├── summarydocuments │ │ └── SummaryDocumentsTest │ │ │ └── summaryDocuments.xml │ │ └── voideddocument │ │ └── VoidedDocumentsTest │ │ ├── voidedDocument.xml │ │ ├── voidedDocument_autoGeneratedFechaEmision.xml │ │ └── voidedDocument_autoGeneratedTipoComprobante.xml │ └── xsd │ ├── 2.0 │ ├── common │ │ ├── CCTS_CCT_SchemaModule-2.0.xsd │ │ ├── CodeList_CurrencyCode_ISO_7_04.xsd │ │ ├── CodeList_LanguageCode_ISO_7_04.xsd │ │ ├── CodeList_MIMEMediaTypeCode_IANA_7_04.xsd │ │ ├── CodeList_UnitCode_UNECE_7_04.xsd │ │ ├── UBL-CommonAggregateComponents-2.0.xsd │ │ ├── UBL-CommonBasicComponents-2.0.xsd │ │ ├── UBL-CommonExtensionComponents-2.0.xsd │ │ ├── UBL-CoreComponentParameters-2.0.xsd │ │ ├── UBL-ExtensionContentDatatype-2.0.xsd │ │ ├── UBL-QualifiedDatatypes-2.0.xsd │ │ ├── UBLPE-SunatAggregateComponents-1.0.xsd │ │ ├── UBLPE-SunatAggregateComponents-1.1.xsd │ │ ├── UnqualifiedDataTypeSchemaModule-2.0.xsd │ │ └── xmldsig-core-schema.xsd │ ├── maindoc │ │ ├── UBLPE-ApplicationResponse-1.0.xsd │ │ ├── UBLPE-CreditNote-1.0.xsd │ │ ├── UBLPE-DebitNote-1.0.xsd │ │ ├── UBLPE-Invoice-1.0.xsd │ │ ├── UBLPE-Perception-1.0.xsd │ │ ├── UBLPE-Retention-1.0.xsd │ │ ├── UBLPE-SummaryDocuments-1.0.xsd │ │ └── UBLPE-VoidedDocuments-1.0.xsd │ ├── message.xml │ ├── message.xsl │ ├── validateAdditionalInformationSchema-beta.xsl │ └── validateAdditionalInformationSchema.xsl │ └── 2.1 │ ├── common │ ├── CCTS_CCT_SchemaModule-2.1.xsd │ ├── UBL-CommonAggregateComponents-2.1.xsd │ ├── UBL-CommonBasicComponents-2.1.xsd │ ├── UBL-CommonExtensionComponents-2.1.xsd │ ├── UBL-CommonSignatureComponents-2.1.xsd │ ├── UBL-CoreComponentParameters-2.1.xsd │ ├── UBL-ExtensionContentDataType-2.1.xsd │ ├── UBL-QualifiedDataTypes-2.1.xsd │ ├── UBL-SignatureAggregateComponents-2.1.xsd │ ├── UBL-SignatureBasicComponents-2.1.xsd │ ├── UBL-UnqualifiedDataTypes-2.1.xsd │ ├── UBL-XAdESv132-2.1.xsd │ ├── UBL-XAdESv141-2.1.xsd │ └── UBL-xmldsig-core-schema-2.1.xsd │ └── maindoc │ ├── UBL-ApplicationResponse-2.1.xsd │ ├── UBL-AttachedDocument-2.1.xsd │ ├── UBL-AwardedNotification-2.1.xsd │ ├── UBL-BillOfLading-2.1.xsd │ ├── UBL-CallForTenders-2.1.xsd │ ├── UBL-Catalogue-2.1.xsd │ ├── UBL-CatalogueDeletion-2.1.xsd │ ├── UBL-CatalogueItemSpecificationUpdate-2.1.xsd │ ├── UBL-CataloguePricingUpdate-2.1.xsd │ ├── UBL-CatalogueRequest-2.1.xsd │ ├── UBL-CertificateOfOrigin-2.1.xsd │ ├── UBL-ContractAwardNotice-2.1.xsd │ ├── UBL-ContractNotice-2.1.xsd │ ├── UBL-CreditNote-2.1.xsd │ ├── UBL-DebitNote-2.1.xsd │ ├── UBL-DespatchAdvice-2.1.xsd │ ├── UBL-DocumentStatus-2.1.xsd │ ├── UBL-DocumentStatusRequest-2.1.xsd │ ├── UBL-ExceptionCriteria-2.1.xsd │ ├── UBL-ExceptionNotification-2.1.xsd │ ├── UBL-Forecast-2.1.xsd │ ├── UBL-ForecastRevision-2.1.xsd │ ├── UBL-ForwardingInstructions-2.1.xsd │ ├── UBL-FreightInvoice-2.1.xsd │ ├── UBL-FulfilmentCancellation-2.1.xsd │ ├── UBL-GoodsItemItinerary-2.1.xsd │ ├── UBL-GuaranteeCertificate-2.1.xsd │ ├── UBL-InstructionForReturns-2.1.xsd │ ├── UBL-InventoryReport-2.1.xsd │ ├── UBL-Invoice-2.1.xsd │ ├── UBL-ItemInformationRequest-2.1.xsd │ ├── UBL-Order-2.1.xsd │ ├── UBL-OrderCancellation-2.1.xsd │ ├── UBL-OrderChange-2.1.xsd │ ├── UBL-OrderResponse-2.1.xsd │ ├── UBL-OrderResponseSimple-2.1.xsd │ ├── UBL-PackingList-2.1.xsd │ ├── UBL-PriorInformationNotice-2.1.xsd │ ├── UBL-ProductActivity-2.1.xsd │ ├── UBL-Quotation-2.1.xsd │ ├── UBL-ReceiptAdvice-2.1.xsd │ ├── UBL-Reminder-2.1.xsd │ ├── UBL-RemittanceAdvice-2.1.xsd │ ├── UBL-RequestForQuotation-2.1.xsd │ ├── UBL-RetailEvent-2.1.xsd │ ├── UBL-SelfBilledCreditNote-2.1.xsd │ ├── UBL-SelfBilledInvoice-2.1.xsd │ ├── UBL-Statement-2.1.xsd │ ├── UBL-StockAvailabilityReport-2.1.xsd │ ├── UBL-Tender-2.1.xsd │ ├── UBL-TenderReceipt-2.1.xsd │ ├── UBL-TendererQualification-2.1.xsd │ ├── UBL-TradeItemLocationProfile-2.1.xsd │ ├── UBL-TransportExecutionPlan-2.1.xsd │ ├── UBL-TransportExecutionPlanRequest-2.1.xsd │ ├── UBL-TransportProgressStatus-2.1.xsd │ ├── UBL-TransportProgressStatusRequest-2.1.xsd │ ├── UBL-TransportServiceDescription-2.1.xsd │ ├── UBL-TransportServiceDescriptionRequest-2.1.xsd │ ├── UBL-TransportationStatus-2.1.xsd │ ├── UBL-TransportationStatusRequest-2.1.xsd │ ├── UBL-UnawardedNotification-2.1.xsd │ ├── UBL-UtilityStatement-2.1.xsd │ └── UBL-Waybill-2.1.xsd ├── xhandler ├── Cargo.toml └── src │ ├── lib.rs │ └── prelude.rs ├── xsender ├── Cargo.toml ├── README.md ├── build.rs ├── resources │ ├── templates │ │ ├── get_status_crd.xml │ │ ├── send_bill.xml │ │ ├── send_summary.xml │ │ ├── validate_cdp_criterios.xml │ │ ├── validate_file.xml │ │ └── verify_ticket.xml │ └── test │ │ ├── 150101-F001-11.xml │ │ ├── F001-1.xml │ │ ├── R-12345678901-01-F001-00000587.xml │ │ ├── R-20220557805-01-F001-22Openubl.xml │ │ ├── RA-20200328-1.xml │ │ ├── bill_service_response_fault.xml │ │ ├── bill_service_response_ok.xml │ │ ├── bill_service_response_ticket.xml │ │ └── get_status_response_ok.xml ├── src │ ├── analyzer.rs │ ├── client_sunat.rs │ ├── constants.rs │ ├── file_sender.rs │ ├── lib.rs │ ├── models.rs │ ├── prelude.rs │ ├── soap │ │ ├── cdr.rs │ │ ├── envelope.rs │ │ ├── mod.rs │ │ ├── send_file_response.rs │ │ └── verify_ticket_response.rs │ ├── ubl_file.rs │ └── zip_manager.rs └── tests │ ├── resources │ └── e2e │ │ ├── 12345678912-01-F001-1.xml │ │ └── 12345678912-RA-20200328-1.xml │ └── send_files.rs └── xsigner ├── Cargo.toml ├── README.md ├── resources └── test │ ├── LLAMA-PE-CERTIFICADO-DEMO-10467793549.pfx │ ├── invoice_no_template.xml │ ├── private.key │ └── public.cer └── src └── lib.rs /.clippy.toml: -------------------------------------------------------------------------------- 1 | allow-unwrap-in-tests = true 2 | allow-expect-in-tests = true 3 | -------------------------------------------------------------------------------- /.devcontainer/.env: -------------------------------------------------------------------------------- 1 | USER_UID=115091 2 | -------------------------------------------------------------------------------- /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM quay.io/fedora/fedora:41 2 | 3 | ARG USERNAME=vscode 4 | ARG USER_UID=1000 5 | ARG USER_GID=$USER_UID 6 | 7 | COPY entrypoint.sh /entrypoint.sh 8 | 9 | RUN dnf -y update && \ 10 | dnf install -y @development-tools && \ 11 | dnf install -y curl wget 12 | 13 | RUN groupadd --gid $USER_GID $USERNAME && \ 14 | useradd --uid $USER_UID --gid $USER_GID -m $USERNAME && \ 15 | echo $USERNAME:10000:5000 > /etc/subuid && echo $USERNAME:10000:5000 > /etc/subgid && \ 16 | # Allow user to execute 'sudo' without password 17 | usermod -aG wheel $USERNAME && \ 18 | echo "%wheel ALL=(ALL) NOPASSWD:ALL" | tee -a /etc/sudoers > /dev/null 19 | 20 | # set permissions 21 | RUN chown $USERNAME:$USERNAME -R /home/$USERNAME 22 | 23 | RUN usermod -aG wheel $USERNAME && \ 24 | # Allow user to execute 'sudo' without password 25 | echo "%wheel ALL=(ALL) NOPASSWD:ALL" | tee -a /etc/sudoers > /dev/null 26 | 27 | ENV _CONTAINERS_USERNS_CONFIGURED="" 28 | 29 | ENTRYPOINT [ "/entrypoint.sh" ] 30 | USER $USERNAME 31 | CMD ["tail", "-f", "/dev/null"] 32 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "xhandler-rust", 3 | "dockerComposeFile": "docker-compose.yml", 4 | "overrideCommand": true, 5 | "shutdownAction": "stopCompose", 6 | "service": "xhandler-rust", 7 | "remoteUser": "vscode", 8 | "workspaceFolder": "/workspace", 9 | "onCreateCommand": "bash .devcontainer/onCreateCommand.sh", 10 | "postCreateCommand": "bash .devcontainer/postCreateCommand.sh", 11 | "postStartCommand": "bash .devcontainer/postStartCommand.sh", 12 | "forwardPorts": [ 13 | 8080, 14 | 5173 15 | ], 16 | "customizations": { 17 | "jetbrains": { 18 | "backend": "RustRover" 19 | }, 20 | "vscode": { 21 | "extensions": [ 22 | "k--kato.intellij-idea-keybindings", 23 | "vadimcn.vscode-lldb", 24 | "rust-lang.rust-analyzer", 25 | "tamasfe.even-better-toml", 26 | "dsznajder.es7-react-js-snippets", 27 | "biomejs.biome", 28 | "GitHub.vscode-github-actions" 29 | ], 30 | "settings": { 31 | "biome.searchInPath": false, 32 | "biome.lspBin": "server/ui/node_modules/@biomejs/cli-linux-x64/biome" 33 | } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /.devcontainer/docker-compose.yml: -------------------------------------------------------------------------------- 1 | volumes: 2 | cargo-cache: 3 | npm-cache: 4 | 5 | services: 6 | xhandler-rust: 7 | # https://github.com/microsoft/vscode-remote-release/issues/10215 8 | image: localhost/xhandler-rust_devcontainer_xhandler-rust:latest 9 | build: 10 | context: . 11 | dockerfile: Dockerfile 12 | args: 13 | USER_UID: ${USER_UID} 14 | privileged: true 15 | userns_mode: "keep-id" 16 | command: tail -f /dev/null 17 | volumes: 18 | - ..:/workspace:cached 19 | - cargo-cache:/home/vscode/.cargo 20 | - npm-cache:/home/vscode/.npm 21 | -------------------------------------------------------------------------------- /.devcontainer/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Ensure $HOME exists when starting 4 | if [ ! -d "${HOME}" ]; then 5 | mkdir -p "${HOME}" 6 | fi 7 | 8 | # Setup $PS1 for a consistent and reasonable prompt 9 | if [ -w "${HOME}" ] && [ ! -f "${HOME}"/.bashrc ]; then 10 | echo "PS1='\s-\v \w \$ '" > "${HOME}"/.bashrc 11 | fi 12 | 13 | # Add current (arbitrary) user to /etc/passwd and /etc/group 14 | if ! whoami > /dev/null 2>&1; then 15 | if [ -w /etc/passwd ]; then 16 | echo "update passwd file" 17 | echo "${USER_NAME:-user}:x:$(id -u):0:${USER_NAME:-user} user:${HOME}:/bin/bash" >> /etc/passwd 18 | echo "${USER_NAME:-user}:x:$(id -u):" >> /etc/group 19 | fi 20 | fi 21 | -------------------------------------------------------------------------------- /.devcontainer/onCreateCommand.sh: -------------------------------------------------------------------------------- 1 | sudo dnf install -y @development-tools 2 | sudo dnf install -y @c-development 3 | sudo dnf install -y libxml2-devel openssl-devel gcc gcc-c++ cmake perl 4 | 5 | ## Install Rust 6 | sudo dnf install -y rustup 7 | 8 | ## Install NVM 9 | curl -s https://raw.githubusercontent.com/devcontainers/features/refs/heads/main/src/node/install.sh | sudo VERSION=22 bash 10 | 11 | ## Configure Rust 12 | rustup-init -y 13 | . "$HOME/.cargo/env" 14 | rustup update 15 | -------------------------------------------------------------------------------- /.devcontainer/postCreateCommand.sh: -------------------------------------------------------------------------------- 1 | # cd /workspace/server/ui && npm ci --ignore-scripts 2 | # corepack enable pnpm -------------------------------------------------------------------------------- /.devcontainer/postStartCommand.sh: -------------------------------------------------------------------------------- 1 | # Git autocomplete 2 | echo "source /usr/share/bash-completion/completions/git" >> ~/.bashrc 3 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | **/node_modules/ 2 | **/dist/ 3 | **/target/ 4 | 5 | **/.idea/ 6 | **/.openubl/ -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | 2 | github: [carlosthe19916] 3 | -------------------------------------------------------------------------------- /.github/actions/install-dependencies/action.yml: -------------------------------------------------------------------------------- 1 | name: Install XHandler dependencies 2 | description: Install XHandler dependencies. 3 | runs: 4 | using: "composite" 5 | steps: 6 | - name: Install dependencies 7 | shell: bash 8 | run: | 9 | sudo apt-get -y install pkg-config libssl-dev libxml2-dev libclang-dev 10 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | 4 | # Maintain dependencies for GitHub Actions 5 | - package-ecosystem: "github-actions" 6 | directory: "/" 7 | schedule: 8 | interval: "weekly" 9 | commit-message: 10 | prefix: ":ghost: " 11 | 12 | # Maintain dependencies for Cargo.toml 13 | - package-ecosystem: "cargo" 14 | directory: "/" 15 | schedule: 16 | interval: "weekly" 17 | commit-message: 18 | prefix: ":ghost: " 19 | 20 | # Maintain dependencies for NPM 21 | - package-ecosystem: "npm" 22 | directory: "/server/ui" 23 | schedule: 24 | interval: "weekly" 25 | commit-message: 26 | prefix: ":ghost: " 27 | allow: 28 | - dependency-name: "@patternfly/*" 29 | dependency-type: "direct" 30 | -------------------------------------------------------------------------------- /.github/workflows/ci-repo.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches-ignore: 6 | - "dependabot/**" 7 | paths-ignore: 8 | - 'README.md' 9 | pull_request: { } 10 | 11 | env: 12 | CI: true 13 | 14 | jobs: 15 | cargo-lint: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v4 19 | - uses: Swatinem/rust-cache@v2 20 | - uses: actions/setup-node@v4 21 | with: 22 | node-version: 22 23 | cache: npm 24 | cache-dependency-path: server/ui/package-lock.json 25 | - name: Install dependencies 26 | uses: ./.github/actions/install-dependencies 27 | - name: Install clippy 28 | run: rustup component add clippy 29 | - run: cargo fmt --check 30 | - run: cargo check 31 | - run: cargo clippy --all-targets --all-features -- -D warnings 32 | 33 | cargo-test: 34 | runs-on: ubuntu-latest 35 | steps: 36 | - uses: actions/checkout@v4 37 | - uses: Swatinem/rust-cache@v2 38 | - uses: actions/setup-node@v4 39 | with: 40 | node-version: 22 41 | cache: npm 42 | cache-dependency-path: server/ui/package-lock.json 43 | - name: Install dependencies 44 | uses: ./.github/actions/install-dependencies 45 | - run: cargo test 46 | 47 | npm-check: 48 | runs-on: ubuntu-latest 49 | steps: 50 | - uses: actions/checkout@v4 51 | - uses: actions/setup-node@v4 52 | with: 53 | node-version: 22 54 | cache: npm 55 | cache-dependency-path: server/ui/package-lock.json 56 | - working-directory: server/ui 57 | run: npm ci 58 | - working-directory: server/ui 59 | run: npm run check 60 | - working-directory: server/ui 61 | run: npm run build 62 | -------------------------------------------------------------------------------- /.github/workflows/image-build.yaml: -------------------------------------------------------------------------------- 1 | name: Multiple Architecture Image Build 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - "main" 8 | - "release-*" 9 | tags: 10 | - "v*" 11 | 12 | concurrency: 13 | group: build-${{ github.ref }} 14 | cancel-in-progress: true 15 | 16 | jobs: 17 | server-image-build: 18 | uses: project-openubl/release-tools/.github/workflows/build-push-images.yaml@main 19 | with: 20 | registry: "ghcr.io" 21 | image_name: "${{ github.repository_owner }}/openubl-server" 22 | containerfile: "./Dockerfile.server" 23 | architectures: '[ "amd64", "arm64" ]' 24 | secrets: 25 | registry_username: ${{ github.actor }} 26 | registry_password: ${{ secrets.GITHUB_TOKEN }} 27 | 28 | ui-image-build: 29 | uses: project-openubl/release-tools/.github/workflows/build-push-images.yaml@main 30 | with: 31 | registry: "ghcr.io" 32 | image_name: "${{ github.repository_owner }}/openubl-ui" 33 | containerfile: "./Dockerfile.ui" 34 | architectures: '[ "amd64", "arm64" ]' 35 | extra-args: "--ulimit nofile=4096:4096" 36 | secrets: 37 | registry_username: ${{ github.actor }} 38 | registry_password: ${{ secrets.GITHUB_TOKEN }} 39 | -------------------------------------------------------------------------------- /.github/workflows/pr-checks.yml: -------------------------------------------------------------------------------- 1 | name: PR Checks 2 | 3 | on: 4 | pull_request_target: 5 | types: [opened, edited, reopened, synchronize] 6 | 7 | jobs: 8 | verify: 9 | runs-on: ubuntu-latest 10 | name: Verify PR contents 11 | steps: 12 | - name: Check Title 13 | id: verifier 14 | uses: project-openubl/release-tools/cmd/verify-pr@main 15 | with: 16 | github_token: ${{ secrets.GITHUB_TOKEN }} 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.versionsBackup 2 | 3 | # Intellij 4 | ################### 5 | .idea 6 | *.iml 7 | 8 | # Eclipse # 9 | ########### 10 | .project 11 | .settings 12 | .classpath 13 | 14 | # NetBeans # 15 | ############ 16 | nbactions.xml 17 | nb-configuration.xml 18 | catalog.xml 19 | nbproject 20 | 21 | # Compiled source # 22 | ################### 23 | *.com 24 | *.class 25 | *.dll 26 | *.exe 27 | *.o 28 | *.so 29 | 30 | # Packages # 31 | ############ 32 | # it's better to unpack these files and commit the raw source 33 | # git has its own built in compression methods 34 | *.7z 35 | *.dmg 36 | *.gz 37 | *.iso 38 | *.jar 39 | *.rar 40 | *.tar 41 | *.zip 42 | 43 | # Logs and databases # 44 | ###################### 45 | *.log 46 | 47 | # Maven # 48 | ######### 49 | target 50 | 51 | # Maven shade 52 | ############# 53 | *dependency-reduced-pom.xml 54 | 55 | /lsp/ 56 | 57 | 58 | # Added by cargo 59 | 60 | /target 61 | 62 | .openubl/ 63 | .pnpm-store/ 64 | -------------------------------------------------------------------------------- /Dockerfile.server: -------------------------------------------------------------------------------- 1 | ###################################################################### 2 | # UI 3 | ###################################################################### 4 | FROM registry.access.redhat.com/ubi9/nodejs-22:latest AS ui-source 5 | USER 1001 6 | COPY --chown=1001 . . 7 | RUN cd server/ui/ && \ 8 | npm install -g npm@9 && \ 9 | npm clean-install --ignore-scripts && npm run build && npm run dist && \ 10 | rm -rf node_modules 11 | 12 | ###################################################################### 13 | # Build server 14 | ###################################################################### 15 | FROM registry.access.redhat.com/ubi9/ubi:latest AS server-builder 16 | 17 | # Dependencies 18 | RUN dnf install -y libxml2-devel openssl-devel gcc 19 | 20 | RUN mkdir /stage/ && \ 21 | dnf install --installroot /stage/ --setop install_weak_deps=false --nodocs -y zlib openssl && \ 22 | dnf clean all --installroot /stage/ 23 | 24 | # Setup Rust 25 | RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y 26 | ENV PATH=${PATH}:/root/.cargo/bin 27 | RUN rustup target add $(uname -m)-unknown-linux-gnu 28 | 29 | # Build source code 30 | COPY --from=ui-source /opt/app-root/src/ /code/openubl/ 31 | RUN cd /code/openubl/ && \ 32 | cargo build --no-default-features --release --target=$(uname -m)-unknown-linux-gnu && \ 33 | find /code/openubl/target/ -name "server" -exec cp -av {} /stage/usr/local/bin \; 34 | 35 | ###################################################################### 36 | # Builder runner 37 | ###################################################################### 38 | FROM registry.access.redhat.com/ubi9/ubi-micro:latest AS server-runner 39 | COPY --from=server-builder /stage/ . 40 | ENTRYPOINT ["/usr/local/bin/server"] -------------------------------------------------------------------------------- /Dockerfile.ui: -------------------------------------------------------------------------------- 1 | # Builder image 2 | FROM registry.access.redhat.com/ubi9/nodejs-22:latest AS builder 3 | 4 | USER 1001 5 | COPY --chown=1001 ./server/ui . 6 | COPY --chown=1001 ./entrypoint.ui.sh ./entrypoint.sh 7 | RUN npm install -g npm@9 8 | RUN npm clean-install --ignore-scripts && npm run build && npm run dist 9 | 10 | # Runner image 11 | FROM registry.access.redhat.com/ubi9/nodejs-22-minimal:latest 12 | 13 | # Add ps package to allow liveness probe for k8s cluster 14 | # Add tar package to allow copying files with kubectl scp 15 | USER 0 16 | RUN microdnf -y install tar procps-ng && microdnf clean all 17 | 18 | USER 1001 19 | 20 | LABEL name="openubl/openubl-ui" \ 21 | description="Openubl - User Interface" \ 22 | help="For more information visit https://project-openubl.github.io/" \ 23 | license="Apache License 2.0" \ 24 | maintainer="carlosthe19916@gmail.com" \ 25 | summary="Openubl - User Interface" \ 26 | url="https://ghcr.io/project-openubl/openubl-ui" \ 27 | usage="podman run -p 80 -v project-openubl/openubl-ui:latest" \ 28 | io.k8s.display-name="openubl-ui" \ 29 | io.k8s.description="Openubl - User Interface" \ 30 | io.openshift.expose-services="80:http" \ 31 | io.openshift.tags="operator,openubl,ui,nodejs22" \ 32 | io.openshift.min-cpu="100m" \ 33 | io.openshift.min-memory="350Mi" 34 | 35 | COPY --from=builder /opt/app-root/src/dist /opt/app-root/dist/ 36 | 37 | ENV DEBUG=1 38 | 39 | WORKDIR /opt/app-root/dist 40 | ENTRYPOINT ["./entrypoint.sh"] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![License](https://img.shields.io/github/license/project-openubl/xbuilder?logo=apache)](https://www.apache.org/licenses/LICENSE-2.0) 2 | ![CI](https://github.com/project-openubl/xbuilder/workflows/CI/badge.svg) 3 | 4 | [![Project Chat](https://img.shields.io/badge/zulip-join_chat-brightgreen.svg?style=for-the-badge&logo=zulip)](https://projectopenubl.zulipchat.com/) 5 | 6 | ## Libraries 7 | 8 | XMLs basados en UBL y SUNAT 9 | 10 | - [x] Crear 11 | - [x] Firmar 12 | - [x] Enviar 13 | 14 | ## Server 15 | 16 | ```shell 17 | cargo run --bin server 18 | ``` 19 | 20 | - The server is running at http://localhost:8080 21 | - You can see Swagger UI at http://localhost:8080/swagger-ui 22 | 23 | ## Server UI 24 | 25 | ```shell 26 | npm run dev --prefix server/ui 27 | ``` 28 | 29 | - The UI running at http://localhost:3000 30 | 31 | ## License 32 | 33 | - [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) 34 | -------------------------------------------------------------------------------- /entrypoint.ui.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | if [[ -z "$OPENUBL_API_URL" ]]; then 6 | echo "You must provide OPENUBL_API_URL environment variable" 1>&2 7 | exit 1 8 | fi 9 | 10 | if [[ -z "${NODE_EXTRA_CA_CERTS}" ]]; then 11 | # Nothing to do 12 | echo "No NODE_EXTRA_CA_CERTS found" 13 | else 14 | # Copy the Kube API and service CA bundle to /opt/app-root/src/ca.crt if they exist 15 | 16 | # Add Kube API CA 17 | if [ -f "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" ]; then 18 | cp /var/run/secrets/kubernetes.io/serviceaccount/ca.crt ${NODE_EXTRA_CA_CERTS} 19 | fi 20 | 21 | # Add service serving CA 22 | if [ -f "/var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt" ]; then 23 | cat /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt >>${NODE_EXTRA_CA_CERTS} 24 | fi 25 | 26 | # Add custom ingress CA if it exists 27 | if [ -f "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem" ]; then 28 | cat /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem >>${NODE_EXTRA_CA_CERTS} 29 | fi 30 | fi 31 | 32 | exec node --enable-source-maps server/dist/index.js -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "1.83.0" 3 | components = [ "rustfmt", "clippy" ] 4 | -------------------------------------------------------------------------------- /server/.gitignore: -------------------------------------------------------------------------------- 1 | *.versionsBackup 2 | 3 | # Intellij 4 | ################### 5 | .idea 6 | *.iml 7 | 8 | # Eclipse # 9 | ########### 10 | .project 11 | .settings 12 | .classpath 13 | 14 | # NetBeans # 15 | ############ 16 | nbactions.xml 17 | nb-configuration.xml 18 | catalog.xml 19 | nbproject 20 | 21 | # Compiled source # 22 | ################### 23 | *.com 24 | *.class 25 | *.dll 26 | *.exe 27 | *.o 28 | *.so 29 | 30 | # Packages # 31 | ############ 32 | # it's better to unpack these files and commit the raw source 33 | # git has its own built in compression methods 34 | *.7z 35 | *.dmg 36 | *.gz 37 | *.iso 38 | *.jar 39 | *.rar 40 | *.tar 41 | *.zip 42 | 43 | # Logs and databases # 44 | ###################### 45 | *.log 46 | 47 | # Maven # 48 | ######### 49 | target 50 | 51 | # Maven shade 52 | ############# 53 | *dependency-reduced-pom.xml 54 | 55 | /lsp/ 56 | 57 | 58 | # Added by cargo 59 | 60 | /target 61 | 62 | .openubl/ -------------------------------------------------------------------------------- /server/api/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "openubl-api" 3 | version.workspace = true 4 | edition.workspace = true 5 | license.workspace = true 6 | 7 | [dependencies] 8 | openubl-entity = { workspace = true } 9 | openubl-common = { workspace = true } 10 | openubl-migration = { workspace = true } 11 | openubl-storage = { workspace = true } 12 | 13 | xhandler = { workspace = true } 14 | 15 | sea-orm = { workspace = true, features = [ 16 | "sea-query-binder", 17 | "sqlx-sqlite", 18 | "sqlx-postgres", 19 | "runtime-tokio-rustls", 20 | "macros", 21 | ] } 22 | sea-query = { workspace = true } 23 | async-trait = { workspace = true } 24 | anyhow = { workspace = true } 25 | thiserror = { workspace = true } 26 | serde_json = { workspace = true } 27 | serde = { workspace = true, features = ["derive"] } 28 | -------------------------------------------------------------------------------- /server/api/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod db; 2 | pub mod system; 3 | -------------------------------------------------------------------------------- /server/api/src/system/error.rs: -------------------------------------------------------------------------------- 1 | use sea_orm::DbErr; 2 | 3 | #[derive(Debug, thiserror::Error)] 4 | pub enum Error { 5 | #[error(transparent)] 6 | Json(#[from] serde_json::Error), 7 | 8 | #[error(transparent)] 9 | Database(#[from] DbErr), 10 | 11 | #[error(transparent)] 12 | Any(#[from] anyhow::Error), 13 | } 14 | -------------------------------------------------------------------------------- /server/cli/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "openubl-cli" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [[bin]] 7 | name = "server" 8 | path = "src/main.rs" 9 | 10 | [dependencies] 11 | openubl-server = { workspace = true } 12 | 13 | clap = { workspace = true, features = ["derive", "env"] } 14 | anyhow = { workspace = true } 15 | actix-web = { workspace = true } 16 | log = { workspace = true } 17 | tokio = { workspace = true, features = ["full"] } 18 | -------------------------------------------------------------------------------- /server/common/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "openubl-common" 3 | version.workspace = true 4 | edition.workspace = true 5 | license.workspace = true 6 | 7 | [dependencies] 8 | clap = { workspace = true, features = ["derive", "env"] } 9 | -------------------------------------------------------------------------------- /server/common/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod config; 2 | -------------------------------------------------------------------------------- /server/deploy/compose/scripts/keycloak/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -l 2 | KEYCLOAK_HOME="/opt/keycloak" 3 | 4 | while ! $KEYCLOAK_HOME/bin/kcadm.sh config credentials config --server "$KEYCLOAK_SERVER_URL" --realm master --user "$KEYCLOAK_ADMIN" --password "$KEYCLOAK_ADMIN_PASSWORD" &> /dev/null; do 5 | echo "Waiting for Keycloak to start up..." 6 | sleep 3 7 | done 8 | 9 | # Create realm 10 | $KEYCLOAK_HOME/bin/kcadm.sh create realms -s realm=openubl -s enabled=true -o 11 | 12 | # Create clients 13 | $KEYCLOAK_HOME/bin/kcadm.sh create clients -r openubl -f - << EOF 14 | { 15 | "clientId": "openubl-api", 16 | "secret": "secret" 17 | } 18 | EOF 19 | 20 | $KEYCLOAK_HOME/bin/kcadm.sh create clients -r openubl -f - << EOF 21 | { 22 | "clientId": "openubl-ui", 23 | "publicClient": true, 24 | "redirectUris": ["*"], 25 | "webOrigins": ["*"] 26 | } 27 | EOF 28 | 29 | # Create user 30 | $KEYCLOAK_HOME/bin/kcadm.sh create users -r=openubl -s username=carlos -s enabled=true 31 | $KEYCLOAK_HOME/bin/kcadm.sh set-password -r=openubl --username carlos --new-password carlos -------------------------------------------------------------------------------- /server/deploy/compose/scripts/minio/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -l 2 | 3 | # Connect to minio 4 | /usr/bin/mc config host add myminio http://minio:9000 admin password; 5 | 6 | # Create buckets 7 | /usr/bin/mc mb myminio/openubl || true; 8 | 9 | # Config events 10 | /usr/bin/mc event add myminio/openubl arn:minio:sqs::OPENUBL:nats --event "put,delete"; 11 | 12 | # Restart service 13 | /usr/bin/mc admin service restart myminio; -------------------------------------------------------------------------------- /server/entity/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "openubl-entity" 3 | version.workspace = true 4 | edition.workspace = true 5 | license.workspace = true 6 | publish = false 7 | 8 | [dependencies] 9 | sea-orm = { workspace = true, features = [ 10 | "sqlx-sqlite", 11 | "sqlx-postgres", 12 | "runtime-tokio-rustls", 13 | "macros", 14 | ] } 15 | -------------------------------------------------------------------------------- /server/entity/src/credentials.rs: -------------------------------------------------------------------------------- 1 | //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10 2 | 3 | use sea_orm::entity::prelude::*; 4 | 5 | #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] 6 | #[sea_orm(table_name = "credentials")] 7 | pub struct Model { 8 | #[sea_orm(primary_key)] 9 | pub id: i32, 10 | pub name: String, 11 | pub description: Option, 12 | pub username_sol: String, 13 | pub password_sol: String, 14 | pub client_id: String, 15 | pub client_secret: String, 16 | pub url_invoice: String, 17 | pub url_despatch: String, 18 | pub url_perception_retention: String, 19 | } 20 | 21 | #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] 22 | pub enum Relation { 23 | #[sea_orm(has_many = "super::send_rule::Entity")] 24 | SendRule, 25 | } 26 | 27 | impl Related for Entity { 28 | fn to() -> RelationDef { 29 | Relation::SendRule.def() 30 | } 31 | } 32 | 33 | impl ActiveModelBehavior for ActiveModel {} 34 | -------------------------------------------------------------------------------- /server/entity/src/document.rs: -------------------------------------------------------------------------------- 1 | //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10 2 | 3 | use sea_orm::entity::prelude::*; 4 | 5 | #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] 6 | #[sea_orm(table_name = "ubl_document")] 7 | pub struct Model { 8 | #[sea_orm(primary_key)] 9 | pub id: i32, 10 | pub file_id: String, 11 | pub supplier_id: String, 12 | pub identifier: String, 13 | pub r#type: String, 14 | pub voided_document_code: Option, 15 | pub digest_value: Option, 16 | pub sha256: String, 17 | } 18 | 19 | #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] 20 | pub enum Relation {} 21 | 22 | impl ActiveModelBehavior for ActiveModel {} 23 | -------------------------------------------------------------------------------- /server/entity/src/keystore.rs: -------------------------------------------------------------------------------- 1 | //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10 2 | 3 | use sea_orm::entity::prelude::*; 4 | 5 | #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] 6 | #[sea_orm(table_name = "keystore")] 7 | pub struct Model { 8 | #[sea_orm(primary_key)] 9 | pub id: i32, 10 | pub name: String, 11 | } 12 | 13 | #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] 14 | pub enum Relation { 15 | #[sea_orm(has_many = "super::keystore_config::Entity")] 16 | KeystoreConfig, 17 | } 18 | 19 | impl Related for Entity { 20 | fn to() -> RelationDef { 21 | Relation::KeystoreConfig.def() 22 | } 23 | } 24 | 25 | impl ActiveModelBehavior for ActiveModel {} 26 | -------------------------------------------------------------------------------- /server/entity/src/keystore_config.rs: -------------------------------------------------------------------------------- 1 | //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10 2 | 3 | use sea_orm::entity::prelude::*; 4 | 5 | #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] 6 | #[sea_orm(table_name = "keystore_config")] 7 | pub struct Model { 8 | #[sea_orm(primary_key)] 9 | pub id: i32, 10 | pub name: String, 11 | pub val: String, 12 | pub keystore_id: i32, 13 | } 14 | 15 | #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] 16 | pub enum Relation { 17 | #[sea_orm( 18 | belongs_to = "super::keystore::Entity", 19 | from = "Column::KeystoreId", 20 | to = "super::keystore::Column::Id", 21 | on_update = "NoAction", 22 | on_delete = "Cascade" 23 | )] 24 | Keystore, 25 | } 26 | 27 | impl Related for Entity { 28 | fn to() -> RelationDef { 29 | Relation::Keystore.def() 30 | } 31 | } 32 | 33 | impl ActiveModelBehavior for ActiveModel {} 34 | -------------------------------------------------------------------------------- /server/entity/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10 2 | 3 | pub mod prelude; 4 | 5 | pub mod credentials; 6 | pub mod delivery; 7 | pub mod document; 8 | pub mod keystore; 9 | pub mod keystore_config; 10 | pub mod send_rule; 11 | -------------------------------------------------------------------------------- /server/entity/src/mod.rs: -------------------------------------------------------------------------------- 1 | //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 2 | 3 | pub mod prelude; 4 | -------------------------------------------------------------------------------- /server/entity/src/prelude.rs: -------------------------------------------------------------------------------- 1 | //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 2 | -------------------------------------------------------------------------------- /server/entity/src/send_rule.rs: -------------------------------------------------------------------------------- 1 | //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10 2 | 3 | use sea_orm::entity::prelude::*; 4 | 5 | #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] 6 | #[sea_orm(table_name = "send_rule")] 7 | pub struct Model { 8 | #[sea_orm(primary_key)] 9 | pub id: i32, 10 | pub supplier_id: String, 11 | pub credentials_id: i32, 12 | } 13 | 14 | #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] 15 | pub enum Relation { 16 | #[sea_orm( 17 | belongs_to = "super::credentials::Entity", 18 | from = "Column::CredentialsId", 19 | to = "super::credentials::Column::Id", 20 | on_update = "NoAction", 21 | on_delete = "Cascade" 22 | )] 23 | Credentials, 24 | } 25 | 26 | impl Related for Entity { 27 | fn to() -> RelationDef { 28 | Relation::Credentials.def() 29 | } 30 | } 31 | 32 | impl ActiveModelBehavior for ActiveModel {} 33 | -------------------------------------------------------------------------------- /server/migration/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "openubl-migration" 3 | version.workspace = true 4 | edition.workspace = true 5 | license.workspace = true 6 | publish = false 7 | 8 | [lib] 9 | name = "migration" 10 | path = "src/lib.rs" 11 | 12 | [dependencies] 13 | async-std = { workspace = true, features = ["attributes", "tokio1"] } 14 | 15 | [dependencies.sea-orm-migration] 16 | version = "1.0.0" 17 | features = [ 18 | # Enable at least one `ASYNC_RUNTIME` and `DATABASE_DRIVER` feature if you want to run migration via CLI. 19 | # View the list of supported features at https://www.sea-ql.org/SeaORM/docs/install-and-config/database-and-async-runtime. 20 | # e.g. 21 | "runtime-tokio-rustls", # `ASYNC_RUNTIME` feature 22 | "sqlx-sqlite", # `DATABASE_DRIVER` feature 23 | "sqlx-postgres", # `DATABASE_DRIVER` feature 24 | ] 25 | -------------------------------------------------------------------------------- /server/migration/README.md: -------------------------------------------------------------------------------- 1 | # CLI 2 | 3 | Generate migration and apply it: 4 | 5 | ```shell 6 | sea-orm-cli migrate generate NAME_OF_MIGRATION 7 | sea-orm-cli migrate -u postgres://user:password@localhost/openubl 8 | ``` 9 | 10 | Generate entity files of database `openubl` to `entity/src` 11 | 12 | ```shell 13 | sea-orm-cli generate entity -u postgres://user:password@localhost/openubl -o entity/src 14 | ``` 15 | 16 | # Running Migrator CLI 17 | 18 | - Generate a new migration file 19 | ```sh 20 | cargo run -- generate MIGRATION_NAME 21 | ``` 22 | - Apply all pending migrations 23 | ```sh 24 | cargo run 25 | ``` 26 | ```sh 27 | cargo run -- up 28 | ``` 29 | - Apply first 10 pending migrations 30 | ```sh 31 | cargo run -- up -n 10 32 | ``` 33 | - Rollback last applied migrations 34 | ```sh 35 | cargo run -- down 36 | ``` 37 | - Rollback last 10 applied migrations 38 | ```sh 39 | cargo run -- down -n 10 40 | ``` 41 | - Drop all tables from the database, then reapply all migrations 42 | ```sh 43 | cargo run -- fresh 44 | ``` 45 | - Rollback all applied migrations, then reapply all migrations 46 | ```sh 47 | cargo run -- refresh 48 | ``` 49 | - Rollback all applied migrations 50 | ```sh 51 | cargo run -- reset 52 | ``` 53 | - Check the status of all migrations 54 | ```sh 55 | cargo run -- status 56 | ``` 57 | -------------------------------------------------------------------------------- /server/migration/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use sea_orm_migration::prelude::*; 2 | 3 | mod m20240101_104121_create_document; 4 | mod m20240113_213636_create_keystore; 5 | mod m20240113_213657_create_keystore_config; 6 | mod m20240114_154538_create_credentials; 7 | mod m20240117_142858_create_send_rule; 8 | mod m20240717_214515_create_delivery; 9 | pub struct Migrator; 10 | 11 | #[async_trait::async_trait] 12 | impl MigratorTrait for Migrator { 13 | fn migrations() -> Vec> { 14 | vec![ 15 | Box::new(m20240101_104121_create_document::Migration), 16 | Box::new(m20240113_213636_create_keystore::Migration), 17 | Box::new(m20240113_213657_create_keystore_config::Migration), 18 | Box::new(m20240114_154538_create_credentials::Migration), 19 | Box::new(m20240117_142858_create_send_rule::Migration), 20 | Box::new(m20240717_214515_create_delivery::Migration), 21 | ] 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /server/migration/src/m20240101_104121_create_document.rs: -------------------------------------------------------------------------------- 1 | use sea_orm_migration::prelude::*; 2 | 3 | #[derive(DeriveMigrationName)] 4 | pub struct Migration; 5 | 6 | #[async_trait::async_trait] 7 | impl MigrationTrait for Migration { 8 | async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { 9 | manager 10 | .create_table( 11 | Table::create() 12 | .table(Document::Table) 13 | .if_not_exists() 14 | .col( 15 | ColumnDef::new(Document::Id) 16 | .integer() 17 | .not_null() 18 | .auto_increment() 19 | .primary_key(), 20 | ) 21 | .col(ColumnDef::new(Document::FileId).string().not_null()) 22 | .col(ColumnDef::new(Document::SupplierId).string().not_null()) 23 | .col(ColumnDef::new(Document::Identifier).string().not_null()) 24 | .col(ColumnDef::new(Document::Type).string().not_null()) 25 | .col(ColumnDef::new(Document::VoidedDocumentCode).string()) 26 | .col(ColumnDef::new(Document::DigestValue).string()) 27 | .col(ColumnDef::new(Document::Sha256).string().not_null()) 28 | .to_owned(), 29 | ) 30 | .await 31 | } 32 | 33 | async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { 34 | manager 35 | .drop_table(Table::drop().table(Document::Table).to_owned()) 36 | .await 37 | } 38 | } 39 | 40 | #[derive(DeriveIden)] 41 | pub enum Document { 42 | Table, 43 | Id, 44 | FileId, 45 | Type, 46 | Identifier, 47 | SupplierId, 48 | VoidedDocumentCode, 49 | DigestValue, 50 | Sha256, 51 | } 52 | -------------------------------------------------------------------------------- /server/migration/src/m20240113_213636_create_keystore.rs: -------------------------------------------------------------------------------- 1 | use sea_orm_migration::prelude::*; 2 | 3 | #[derive(DeriveMigrationName)] 4 | pub struct Migration; 5 | 6 | #[async_trait::async_trait] 7 | impl MigrationTrait for Migration { 8 | async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { 9 | manager 10 | .create_table( 11 | Table::create() 12 | .table(Keystore::Table) 13 | .if_not_exists() 14 | .col( 15 | ColumnDef::new(Keystore::Id) 16 | .integer() 17 | .not_null() 18 | .auto_increment() 19 | .primary_key(), 20 | ) 21 | .col(ColumnDef::new(Keystore::Name).string().not_null()) 22 | .to_owned(), 23 | ) 24 | .await 25 | } 26 | 27 | async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { 28 | manager 29 | .drop_table(Table::drop().table(Keystore::Table).to_owned()) 30 | .await 31 | } 32 | } 33 | 34 | #[derive(DeriveIden)] 35 | pub enum Keystore { 36 | Table, 37 | Id, 38 | Name, 39 | } 40 | -------------------------------------------------------------------------------- /server/migration/src/m20240117_142858_create_send_rule.rs: -------------------------------------------------------------------------------- 1 | use crate::m20240114_154538_create_credentials::Credentials; 2 | use sea_orm_migration::prelude::*; 3 | 4 | #[derive(DeriveMigrationName)] 5 | pub struct Migration; 6 | 7 | #[async_trait::async_trait] 8 | impl MigrationTrait for Migration { 9 | async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { 10 | manager 11 | .create_table( 12 | Table::create() 13 | .table(SendRule::Table) 14 | .if_not_exists() 15 | .col( 16 | ColumnDef::new(SendRule::Id) 17 | .integer() 18 | .not_null() 19 | .auto_increment() 20 | .primary_key(), 21 | ) 22 | .col(ColumnDef::new(SendRule::SupplierId).string().not_null()) 23 | .col(ColumnDef::new(SendRule::CredentialsId).integer().not_null()) 24 | .foreign_key( 25 | ForeignKey::create() 26 | .from_col(SendRule::CredentialsId) 27 | .to(Credentials::Table, Credentials::Id) 28 | .on_delete(ForeignKeyAction::Cascade), 29 | ) 30 | .to_owned(), 31 | ) 32 | .await 33 | } 34 | 35 | async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { 36 | manager 37 | .drop_table(Table::drop().table(SendRule::Table).to_owned()) 38 | .await 39 | } 40 | } 41 | 42 | #[derive(DeriveIden)] 43 | enum SendRule { 44 | Table, 45 | Id, 46 | SupplierId, 47 | CredentialsId, 48 | } 49 | -------------------------------------------------------------------------------- /server/migration/src/main.rs: -------------------------------------------------------------------------------- 1 | use sea_orm_migration::prelude::*; 2 | 3 | #[async_std::main] 4 | async fn main() { 5 | cli::run_cli(migration::Migrator).await; 6 | } 7 | -------------------------------------------------------------------------------- /server/server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "openubl-server" 3 | description = "Web service for managing UBL files from SUNAT" 4 | version.workspace = true 5 | edition.workspace = true 6 | license.workspace = true 7 | 8 | [dependencies] 9 | openubl-api = { workspace = true } 10 | openubl-common = { workspace = true } 11 | openubl-entity = { workspace = true } 12 | openubl-storage = { workspace = true } 13 | openubl-ui = { workspace = true } 14 | 15 | xhandler = { workspace = true } 16 | 17 | actix-web = { workspace = true } 18 | serde = { workspace = true, features = ["derive"] } 19 | sea-orm = { workspace = true, features = [ 20 | "sqlx-sqlite", 21 | "sqlx-postgres", 22 | "runtime-tokio-rustls", 23 | "macros", 24 | ] } 25 | clap = { workspace = true, features = ["derive", "env"] } 26 | anyhow = { workspace = true } 27 | env_logger = { workspace = true } 28 | thiserror = { workspace = true } 29 | utoipa = { workspace = true, features = ["actix_extras", "yaml"] } 30 | utoipa-actix-web = { workspace = true } 31 | utoipa-swagger-ui = { workspace = true, features = ["actix-web"] } 32 | actix-web-httpauth = { workspace = true } 33 | actix-4-jwt-auth = { workspace = true } 34 | actix-multipart = { workspace = true } 35 | actix-web-static-files = { workspace = true } 36 | minio = { workspace = true } 37 | -------------------------------------------------------------------------------- /server/server/src/openapi.rs: -------------------------------------------------------------------------------- 1 | use actix_web::App; 2 | use utoipa::{ 3 | openapi::{Info, License}, 4 | OpenApi, 5 | }; 6 | use utoipa_actix_web::AppExt; 7 | 8 | use crate::{configure_api, configure_q}; 9 | 10 | #[derive(OpenApi)] 11 | #[openapi()] 12 | pub struct ApiDoc; 13 | 14 | pub fn default_openapi_info() -> Info { 15 | let mut info = Info::new("Openubl", env!("CARGO_PKG_VERSION")); 16 | info.description = Some("Enviar archivos XML a la SUNAT API".into()); 17 | info.license = { 18 | let mut license = License::new("Apache License, Version 2.0"); 19 | license.identifier = Some("Apache-2.0".into()); 20 | Some(license) 21 | }; 22 | info 23 | } 24 | 25 | pub async fn create_openapi() -> anyhow::Result { 26 | let (_, mut openapi) = App::new() 27 | .into_utoipa_app() 28 | .service(utoipa_actix_web::scope("/q").configure(configure_q)) 29 | .service(utoipa_actix_web::scope("/api").configure(configure_api)) 30 | .split_for_parts(); 31 | 32 | openapi.info = default_openapi_info(); 33 | 34 | Ok(openapi) 35 | } 36 | -------------------------------------------------------------------------------- /server/server/src/server/health.rs: -------------------------------------------------------------------------------- 1 | use actix_web::{get, web, Responder}; 2 | 3 | use crate::server::Error; 4 | use crate::AppState; 5 | 6 | #[utoipa::path( 7 | responses( 8 | (status = 200, description = "Liveness"), 9 | ), 10 | )] 11 | #[get("/health/live")] 12 | pub async fn liveness(_: web::Data) -> Result { 13 | Ok("Live") 14 | } 15 | 16 | #[utoipa::path( 17 | responses( 18 | (status = 200, description = "Readiness"), 19 | ), 20 | )] 21 | #[get("/health/read")] 22 | pub async fn readiness(_: web::Data) -> Result { 23 | Ok("Read") 24 | } 25 | -------------------------------------------------------------------------------- /server/signature/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "openubl-signature" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | anyhow = { workspace = true } 10 | thiserror = { workspace = true } 11 | -------------------------------------------------------------------------------- /server/signature/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /server/storage/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "openubl-storage" 3 | version.workspace = true 4 | edition.workspace = true 5 | license.workspace = true 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | clap = { workspace = true, features = ["derive", "env"] } 11 | minio = { workspace = true } 12 | serde = { workspace = true, features = ["derive"] } 13 | anyhow = { workspace = true } 14 | uuid = { workspace = true, features = ["v4"] } 15 | thiserror = { workspace = true } 16 | zip = { workspace = true } 17 | tempfile = { workspace = true } 18 | reqwest = { workspace = true } 19 | aws-sdk-s3 = { workspace = true } 20 | tokio = { workspace = true } 21 | aws-config = { workspace = true } 22 | aws-smithy-runtime = { workspace = true } 23 | aws-smithy-runtime-api = { workspace = true } 24 | -------------------------------------------------------------------------------- /server/storage/src/config.rs: -------------------------------------------------------------------------------- 1 | #[derive(clap::Subcommand, Debug)] 2 | pub enum Storage { 3 | Local(LocalStorage), 4 | Minio(MinioStorage), 5 | S3(S3Storage), 6 | } 7 | 8 | #[derive(clap::Args, Debug)] 9 | pub struct LocalStorage { 10 | #[arg( 11 | id = "storage-local-dir", 12 | long, 13 | env = "STORAGE_LOCAL_DIR", 14 | default_value = "storage" 15 | )] 16 | pub local_dir: String, 17 | } 18 | 19 | #[derive(clap::Args, Debug)] 20 | pub struct MinioStorage { 21 | #[arg(id = "storage-minio-host", long, env = "STORAGE_MINIO_HOST")] 22 | pub host: String, 23 | 24 | #[arg( 25 | id = "storage-minio-bucket", 26 | long, 27 | env = "STORAGE_MINIO_BUCKET", 28 | default_value = "openubl" 29 | )] 30 | pub bucket: String, 31 | 32 | #[arg( 33 | id = "storage-minio-access-key", 34 | long, 35 | env = "STORAGE_MINIO_ACCESS_KEY" 36 | )] 37 | pub access_key: String, 38 | 39 | #[arg( 40 | id = "storage-minio-secret-key", 41 | long, 42 | env = "STORAGE_MINIO_SECRET_KEY" 43 | )] 44 | pub secret_key: String, 45 | } 46 | 47 | #[derive(clap::Args, Debug)] 48 | pub struct S3Storage { 49 | #[arg(id = "storage-s3-region", long, env = "STORAGE_S3_REGION")] 50 | pub region: String, 51 | 52 | #[arg( 53 | id = "storage-s3-bucket", 54 | long, 55 | env = "STORAGE_S3_BUCKET", 56 | default_value = "openubl" 57 | )] 58 | pub bucket: String, 59 | 60 | #[arg(id = "storage-s3-access-key", long, env = "STORAGE_S3_ACCESS_KEY")] 61 | pub access_key: String, 62 | 63 | #[arg(id = "storage-s3-secret-key", long, env = "STORAGE_S3_SECRET_KEY")] 64 | pub secret_key: String, 65 | } 66 | -------------------------------------------------------------------------------- /server/ui/.gitignore: -------------------------------------------------------------------------------- 1 | # Local 2 | .DS_Store 3 | *.local 4 | *.log* 5 | 6 | # Dist 7 | node_modules 8 | dist/ 9 | 10 | # IDE 11 | .vscode/* 12 | !.vscode/extensions.json 13 | .idea 14 | -------------------------------------------------------------------------------- /server/ui/biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://biomejs.dev/schemas/1.8.0/schema.json", 3 | "organizeImports": { 4 | "enabled": true 5 | }, 6 | "vcs": { 7 | "enabled": true, 8 | "clientKind": "git", 9 | "useIgnoreFile": true 10 | }, 11 | "formatter": { 12 | "indentStyle": "space" 13 | }, 14 | "javascript": { 15 | "formatter": { 16 | "quoteStyle": "double" 17 | } 18 | }, 19 | "css": { 20 | "parser": { 21 | "cssModules": true 22 | } 23 | }, 24 | "linter": { 25 | "enabled": true, 26 | "rules": { 27 | "recommended": true 28 | } 29 | }, 30 | "files": { 31 | "ignore": ["**/routeTree.gen.ts"] 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /server/ui/branding/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-openubl/xhandler-rust/2c4a2947117d7d011f4423ec746153f892cead9e/server/ui/branding/favicon.ico -------------------------------------------------------------------------------- /server/ui/branding/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-openubl/xhandler-rust/2c4a2947117d7d011f4423ec746153f892cead9e/server/ui/branding/images/logo.png -------------------------------------------------------------------------------- /server/ui/branding/images/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-openubl/xhandler-rust/2c4a2947117d7d011f4423ec746153f892cead9e/server/ui/branding/images/logo192.png -------------------------------------------------------------------------------- /server/ui/branding/images/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-openubl/xhandler-rust/2c4a2947117d7d011f4423ec746153f892cead9e/server/ui/branding/images/logo512.png -------------------------------------------------------------------------------- /server/ui/branding/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "openubl-ui", 3 | "name": "Openubl UI", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } -------------------------------------------------------------------------------- /server/ui/branding/strings.json: -------------------------------------------------------------------------------- 1 | { 2 | "application": { 3 | "title": "Openubl", 4 | "name": "Openubl UI", 5 | "description": "Openubl UI" 6 | }, 7 | "about": { 8 | "displayName": "Openubl", 9 | "imageSrc": "<%= brandingRoot %>/images/masthead-logo.svg", 10 | "documentationUrl": "https://project-openubl.github.io/" 11 | }, 12 | "masthead": { 13 | "leftBrand": { 14 | "src": "<%= brandingRoot %>/images/masthead-logo.svg", 15 | "alt": "brand", 16 | "height": "40px" 17 | }, 18 | "leftTitle": null, 19 | "rightBrand": null 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /server/ui/client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= branding.application.title %> 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | 20 |
21 | 22 | -------------------------------------------------------------------------------- /server/ui/client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@openubl-ui/client", 3 | "version": "1.0.0", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "clean": "rimraf ./dist", 8 | "clean:all": "rimraf ./dist ./node_modules", 9 | "build": "rsbuild build", 10 | "check": "biome check .", 11 | "check:write": "biome check --write .", 12 | "dev": "rsbuild dev", 13 | "start": "rsbuild preview" 14 | }, 15 | "dependencies": { 16 | "@patternfly/patternfly": "^6.1.0", 17 | "@patternfly/react-core": "^6.1.0", 18 | "@patternfly/react-table": "^6.1.0", 19 | "@patternfly/react-tokens": "^6.1.0", 20 | "@tanstack/react-query": "^5.66.11", 21 | "@tanstack/react-query-devtools": "^5.66.11", 22 | "@tanstack/react-router": "^1.112.0", 23 | "@tanstack/react-table": "^8.21.2", 24 | "@tanstack/router-devtools": "^1.112.0", 25 | "axios": "^1.8.1", 26 | "react": "^18.3.1", 27 | "react-dom": "^18.3.1" 28 | }, 29 | "devDependencies": { 30 | "@rsbuild/core": "^1.2.8", 31 | "@rsbuild/plugin-react": "^1.1.0", 32 | "@tanstack/router-plugin": "^1.99.0", 33 | "@types/react": "^18.3.18", 34 | "@types/react-dom": "^18.3.5", 35 | "raw-loader": "^4.0.2" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /server/ui/client/public/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-openubl/xhandler-rust/2c4a2947117d7d011f4423ec746153f892cead9e/server/ui/client/public/.gitkeep -------------------------------------------------------------------------------- /server/ui/client/src/app/Constants.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-openubl/xhandler-rust/2c4a2947117d7d011f4423ec746153f892cead9e/server/ui/client/src/app/Constants.ts -------------------------------------------------------------------------------- /server/ui/client/src/app/assets/avatar.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /server/ui/client/src/app/assets/patternfly_avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-openubl/xhandler-rust/2c4a2947117d7d011f4423ec746153f892cead9e/server/ui/client/src/app/assets/patternfly_avatar.jpg -------------------------------------------------------------------------------- /server/ui/client/src/app/assets/pfbg-icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /server/ui/client/src/app/common/types.ts: -------------------------------------------------------------------------------- 1 | export interface Page { 2 | page: number; 3 | perPage: number; 4 | } 5 | 6 | export interface SortBy { 7 | index: number; 8 | direction: "asc" | "desc"; 9 | } 10 | -------------------------------------------------------------------------------- /server/ui/client/src/app/components/Notifications.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import { 4 | Alert, 5 | AlertActionCloseButton, 6 | AlertGroup, 7 | } from "@patternfly/react-core"; 8 | import { NotificationsContext } from "./NotificationsContext"; 9 | 10 | export const Notifications: React.FunctionComponent = () => { 11 | const appContext = React.useContext(NotificationsContext); 12 | return ( 13 | 14 | {appContext.notifications.map((notification) => { 15 | return ( 16 | { 24 | appContext.dismissNotification(notification.title); 25 | }} 26 | /> 27 | ), 28 | })} 29 | timeout={notification.timeout ?? 4000} 30 | > 31 | {notification.message} 32 | 33 | ); 34 | })} 35 | 36 | ); 37 | }; 38 | -------------------------------------------------------------------------------- /server/ui/client/src/app/components/NotificationsContext.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import type { AlertProps } from "@patternfly/react-core"; 4 | 5 | export interface INotification { 6 | title: string; 7 | variant: AlertProps["variant"]; 8 | message?: React.ReactNode; 9 | hideCloseButton?: boolean; 10 | timeout?: number | boolean; 11 | } 12 | 13 | export interface INotificationsProvider { 14 | children: React.ReactNode; 15 | } 16 | 17 | interface INotificationsContext { 18 | pushNotification: (notification: INotification) => void; 19 | dismissNotification: (key: string) => void; 20 | notifications: INotification[]; 21 | } 22 | 23 | const appContextDefaultValue = {} as INotificationsContext; 24 | 25 | export const NotificationsContext = React.createContext( 26 | appContextDefaultValue, 27 | ); 28 | -------------------------------------------------------------------------------- /server/ui/client/src/app/components/NotificationsProvider.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import { 4 | type INotification, 5 | type INotificationsProvider, 6 | NotificationsContext, 7 | } from "./NotificationsContext.tsx"; 8 | 9 | const notificationDefault: Pick = { 10 | hideCloseButton: false, 11 | }; 12 | 13 | export const NotificationsProvider: React.FunctionComponent< 14 | INotificationsProvider 15 | > = ({ children }: INotificationsProvider) => { 16 | const [notifications, setNotifications] = React.useState([]); 17 | 18 | const pushNotification = ( 19 | notification: INotification, 20 | clearNotificationDelay?: number, 21 | ) => { 22 | setNotifications([ 23 | ...notifications, 24 | { ...notificationDefault, ...notification }, 25 | ]); 26 | setTimeout(() => setNotifications([]), clearNotificationDelay ?? 10000); 27 | }; 28 | 29 | const dismissNotification = (title: string) => { 30 | const remainingNotifications = notifications.filter( 31 | (n) => n.title !== title, 32 | ); 33 | setNotifications(remainingNotifications); 34 | }; 35 | 36 | return ( 37 | 44 | {children} 45 | 46 | ); 47 | }; 48 | -------------------------------------------------------------------------------- /server/ui/client/src/app/layout/default-layout.tsx: -------------------------------------------------------------------------------- 1 | import type React from "react"; 2 | 3 | import { Page, SkipToContent } from "@patternfly/react-core"; 4 | 5 | import { Notifications } from "@app/components/Notifications"; 6 | import { PageContentWithDrawerProvider } from "@app/components/PageDrawerContext"; 7 | 8 | import { HeaderApp } from "./header"; 9 | import { SidebarApp } from "./sidebar"; 10 | 11 | interface DefaultLayoutProps { 12 | children?: React.ReactNode; 13 | } 14 | 15 | export const DefaultLayout: React.FC = ({ children }) => { 16 | const pageId = "main-content-page-layout-horizontal-nav"; 17 | const PageSkipToContent = ( 18 | Skip to content 19 | ); 20 | 21 | return ( 22 | } 24 | sidebar={} 25 | isManagedSidebar 26 | skipToContent={PageSkipToContent} 27 | mainContainerId={pageId} 28 | > 29 | 30 | {children} 31 | 32 | 33 | 34 | ); 35 | }; 36 | -------------------------------------------------------------------------------- /server/ui/client/src/app/layout/index.ts: -------------------------------------------------------------------------------- 1 | export { DefaultLayout } from "./default-layout"; 2 | -------------------------------------------------------------------------------- /server/ui/client/src/assets/favicon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /server/ui/client/src/client/helpers.ts: -------------------------------------------------------------------------------- 1 | export type WithUiId = T & { _ui_unique_id: string }; 2 | 3 | /** Mark an object as "New" therefore does not have an `id` field. */ 4 | export type New = Omit; 5 | 6 | export interface HubFilter { 7 | field: string; 8 | operator?: "=" | "!=" | "~" | ">" | ">=" | "<" | "<="; 9 | value: 10 | | string 11 | | number 12 | | { 13 | list: (string | number)[]; 14 | operator?: "AND" | "OR"; 15 | }; 16 | } 17 | 18 | export interface HubRequestParams { 19 | filters?: HubFilter[]; 20 | sort?: { 21 | field: string; 22 | direction: "asc" | "desc"; 23 | }; 24 | page?: { 25 | pageNumber: number; // 1-indexed 26 | itemsPerPage: number; 27 | }; 28 | } 29 | 30 | export interface HubPaginatedResult { 31 | data: T[]; 32 | total: number; 33 | params: HubRequestParams; 34 | } 35 | 36 | // Common 37 | 38 | export type VulnerabilityStatus = 39 | | "fixed" 40 | | "not_affected" 41 | | "known_not_affected" 42 | | "affected"; 43 | -------------------------------------------------------------------------------- /server/ui/client/src/client/models.ts: -------------------------------------------------------------------------------- 1 | export interface Credentials { 2 | id: number; 3 | name: string; 4 | description?: string; 5 | supplier_ids_applied_to: string[]; 6 | soap?: { 7 | username_sol: string; 8 | password_sol: string; 9 | url_invoice: string; 10 | url_perception_retention: string; 11 | }; 12 | rest?: { 13 | client_id: string; 14 | client_secret: string; 15 | url_despatch: string; 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /server/ui/client/src/client/rest.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | import type { New } from "./helpers.ts"; 4 | import type { Credentials } from "./models"; 5 | 6 | const API = "/api"; 7 | 8 | export const CREDENTIALS = `${API}/credentials`; 9 | 10 | export const getCredentials = () => { 11 | return axios 12 | .get(CREDENTIALS) 13 | .then((response) => response.data); 14 | }; 15 | 16 | export const getCredentialsById = (id: number | string) => { 17 | return axios 18 | .get(`${CREDENTIALS}/${id}`) 19 | .then((response) => response.data); 20 | }; 21 | 22 | export const createCredentials = (obj: New) => 23 | axios.post(`${CREDENTIALS}/credentials`, obj); 24 | 25 | export const updateCredentials = (obj: Credentials) => 26 | axios.put(`${CREDENTIALS}/${obj.id}`, obj); 27 | 28 | export const deleteCredentials = (id: number | string) => 29 | axios.delete(`${CREDENTIALS}/${id}`); 30 | -------------------------------------------------------------------------------- /server/ui/client/src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /server/ui/client/src/index.tsx: -------------------------------------------------------------------------------- 1 | import { createRoot } from "react-dom/client"; 2 | 3 | import { RouterProvider, createRouter } from "@tanstack/react-router"; 4 | import { routeTree } from "./routeTree.gen"; 5 | 6 | // Set up a Router instance 7 | const router = createRouter({ 8 | routeTree, 9 | defaultPreload: "intent", 10 | }); 11 | 12 | // Register things for typesafety 13 | declare module "@tanstack/react-router" { 14 | interface Register { 15 | router: typeof router; 16 | } 17 | } 18 | 19 | const rootElement = document.getElementById("root"); 20 | 21 | if (rootElement && !rootElement.innerHTML) { 22 | const root = createRoot(rootElement); 23 | root.render(); 24 | } 25 | -------------------------------------------------------------------------------- /server/ui/client/src/routes/__root.tsx: -------------------------------------------------------------------------------- 1 | import React, { Suspense } from "react"; 2 | 3 | import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; 4 | import { Outlet, createRootRoute } from "@tanstack/react-router"; 5 | 6 | import { NotificationsProvider } from "@app/components/NotificationsProvider.tsx"; 7 | import { DefaultLayout } from "@app/layout"; 8 | 9 | import "@patternfly/patternfly/patternfly-addons.css"; 10 | import "@patternfly/patternfly/patternfly.css"; 11 | 12 | const TanStackQueryDevtools = 13 | process.env.NODE_ENV === "production" 14 | ? () => null 15 | : React.lazy(() => 16 | import("@tanstack/react-query-devtools").then((res) => ({ 17 | default: res.ReactQueryDevtools, 18 | })), 19 | ); 20 | 21 | const TanStackRouterDevtools = 22 | process.env.NODE_ENV === "production" 23 | ? () => null 24 | : React.lazy(() => 25 | import("@tanstack/router-devtools").then((res) => ({ 26 | default: res.TanStackRouterDevtools, 27 | })), 28 | ); 29 | 30 | const queryClient = new QueryClient(); 31 | 32 | export const Route = createRootRoute({ 33 | component: RootComponent, 34 | }); 35 | 36 | function RootComponent() { 37 | return ( 38 | <> 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | ); 54 | } 55 | -------------------------------------------------------------------------------- /server/ui/client/src/routes/about.tsx: -------------------------------------------------------------------------------- 1 | import { createFileRoute } from "@tanstack/react-router"; 2 | 3 | export const Route = createFileRoute("/about")({ 4 | component: AboutComponent, 5 | }); 6 | 7 | function AboutComponent() { 8 | return ( 9 |
10 |

About

11 |
12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /server/ui/client/src/routes/index.tsx: -------------------------------------------------------------------------------- 1 | import { Content, PageSection } from "@patternfly/react-core"; 2 | import { createFileRoute } from "@tanstack/react-router"; 3 | 4 | export const Route = createFileRoute("/")({ 5 | component: HomeComponent, 6 | }); 7 | 8 | function HomeComponent() { 9 | return ( 10 | <> 11 | 12 | 13 |

Openubl

14 |

Fully hosted and managed service

15 |
16 |
17 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /server/ui/client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["DOM", "ES2020"], 4 | "jsx": "react-jsx", 5 | "target": "ES2020", 6 | "noEmit": true, 7 | "skipLibCheck": true, 8 | "useDefineForClassFields": true, 9 | 10 | /* modules */ 11 | "module": "ESNext", 12 | "isolatedModules": true, 13 | "resolveJsonModule": true, 14 | "moduleResolution": "Bundler", 15 | "allowImportingTsExtensions": true, 16 | 17 | /* type checking */ 18 | "strict": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | 22 | "paths": { 23 | "@client/*": ["./src/client/*"], 24 | "@queries/*": ["./src/queries/*"], 25 | "@app/*": ["./src/app/*"] 26 | } 27 | }, 28 | "include": ["src"] 29 | } 30 | -------------------------------------------------------------------------------- /server/ui/common/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@openubl-ui/common", 3 | "description": "ESM module for code common to client and server", 4 | "version": "0.1.0", 5 | "license": "Apache-2.0", 6 | "private": true, 7 | "type": "module", 8 | "exports": { 9 | ".": { 10 | "types": "./dist/index.d.ts", 11 | "import": "./dist/index.mjs", 12 | "require": "./dist/index.cjs" 13 | }, 14 | "./package.json": "./package.json" 15 | }, 16 | "types": "./dist", 17 | "main": "./dist/index.cjs", 18 | "module": "./dist/index.mjs", 19 | "scripts": { 20 | "clean": "rimraf ./dist", 21 | "clean:all": "rimraf ./dist ./node_modules", 22 | "check": "biome check .", 23 | "check:write": "biome check --write .", 24 | "prebuild": "npm run clean", 25 | "build": "NODE_ENV=production rollup -c", 26 | "dev": "NODE_ENV=development rollup -c --watch" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /server/ui/common/rollup.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 3 | import { readFileSync } from "node:fs"; 4 | import path from "node:path"; 5 | import { fileURLToPath } from "node:url"; 6 | import util from "node:util"; 7 | 8 | import nodeResolve from "@rollup/plugin-node-resolve"; 9 | import typescript from "@rollup/plugin-typescript"; 10 | import virtual from "@rollup/plugin-virtual"; 11 | import ejs from "ejs"; 12 | import copy from "rollup-plugin-copy"; 13 | 14 | const __dirname = fileURLToPath(new URL(".", import.meta.url)); 15 | const pathTo = (...relativePath) => path.resolve(__dirname, ...relativePath); 16 | 17 | const baseBrandingPath = process.env.BRANDING ?? "./branding"; 18 | const brandingPath = pathTo("../", baseBrandingPath); 19 | const jsonStrings = JSON.parse( 20 | readFileSync(path.resolve(brandingPath, "./strings.json"), "utf8"), 21 | ); 22 | const stringModule = ejs.render( 23 | ` 24 | export const strings = ${util.inspect(jsonStrings)}; 25 | export default strings; 26 | `, 27 | { 28 | brandingRoot: "branding", 29 | }, 30 | ); 31 | 32 | console.log("Using branding assets from:", brandingPath); 33 | 34 | const config = { 35 | input: "src/index.ts", 36 | 37 | output: [ 38 | { 39 | file: "dist/index.mjs", 40 | format: "esm", 41 | sourcemap: true, 42 | }, 43 | { 44 | file: "dist/index.cjs", 45 | format: "cjs", 46 | sourcemap: true, 47 | }, 48 | ], 49 | 50 | watch: { 51 | clearScreen: false, 52 | }, 53 | 54 | plugins: [ 55 | copy({ 56 | targets: [{ src: `${brandingPath}/**/*`, dest: "dist/branding" }], 57 | }), 58 | nodeResolve(), 59 | typescript(), 60 | virtual({ 61 | "@branding/strings.json": stringModule, 62 | }), 63 | ], 64 | }; 65 | 66 | export default config; 67 | -------------------------------------------------------------------------------- /server/ui/common/src/branding-strings-stub.json: -------------------------------------------------------------------------------- 1 | { 2 | "application": { 3 | "title": "Stub to allow package build to work", 4 | "name": "", 5 | "description": "" 6 | }, 7 | "about": { 8 | "displayName": "", 9 | "image": "", 10 | "documentationUrl": "" 11 | }, 12 | "masthead": { 13 | "leftBrand": { 14 | "src": "", 15 | "alt": "", 16 | "height": "" 17 | }, 18 | "leftTitle": null, 19 | "rightBrand": null 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /server/ui/common/src/branding.ts: -------------------------------------------------------------------------------- 1 | export interface MastheadBrand { 2 | src: string; 3 | alt: string; 4 | height: string; 5 | } 6 | 7 | export interface MastheadTitle { 8 | text: string; 9 | heading?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6"; 10 | size?: "md" | "lg" | "xl" | "2xl" | "3xl" | "4xl"; 11 | } 12 | 13 | export interface BrandingStrings { 14 | application: { 15 | title: string; 16 | name?: string; 17 | description?: string; 18 | }; 19 | 20 | about: { 21 | displayName: string; 22 | imageSrc?: string; 23 | documentationUrl?: string; 24 | }; 25 | 26 | masthead: { 27 | leftBrand?: MastheadBrand; 28 | leftTitle?: MastheadTitle; 29 | rightBrand?: MastheadBrand; 30 | }; 31 | } 32 | 33 | // Note: Typescript will look at the `paths` definition to resolve this import 34 | // to a stub JSON file. In the next rollup build step, that import will 35 | // be replaced by the rollup virtual plugin with a dynamically generated 36 | // JSON import with the actual branding information. 37 | import * as stringsJson from "@branding/strings.json"; 38 | 39 | export const brandingStrings = 40 | stringsJson.default as unknown as BrandingStrings; 41 | 42 | /** 43 | * Return the `node_modules/` resolved path for the branding assets. 44 | */ 45 | import { createRequire } from "node:module"; 46 | const require = createRequire(import.meta.url); 47 | 48 | export const brandingAssetPath = () => 49 | `${require 50 | .resolve("@openubl-ui/common/package.json") 51 | .replace(/(.)\/package.json$/, "$1")}/dist/branding`; 52 | -------------------------------------------------------------------------------- /server/ui/common/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./environment.js"; 2 | export * from "./proxies.js"; 3 | export * from "./branding.js"; 4 | 5 | /** 6 | * Return a base64 encoded JSON string containing the given `env` object. 7 | */ 8 | export const encodeEnv = (env: object, exclude?: string[]): string => { 9 | const filtered = exclude 10 | ? Object.fromEntries( 11 | Object.entries(env).filter(([key]) => !exclude.includes(key)), 12 | ) 13 | : env; 14 | 15 | return btoa(JSON.stringify(filtered)); 16 | }; 17 | 18 | /** 19 | * Return an objects from a base64 encoded JSON string. 20 | */ 21 | export const decodeEnv = (env: string): object => 22 | !env ? {} : JSON.parse(atob(env)); 23 | 24 | // TODO: Include `index.html.ejs` to `index.html` template file processing... 25 | -------------------------------------------------------------------------------- /server/ui/common/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "include": ["src/**/*"], 4 | "compilerOptions": { 5 | "strict": true, 6 | "target": "es2020", 7 | "module": "node16", 8 | "moduleResolution": "node16", 9 | "outDir": "./dist", 10 | "declaration": true, 11 | "declarationMap": true, 12 | "sourceMap": true, 13 | "inlineSources": true, 14 | "resolveJsonModule": true, 15 | "paths": { 16 | "@branding/strings.json": ["./src/branding-strings-stub.json"] 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /server/ui/crate/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "openubl-ui" 3 | version.workspace = true 4 | edition.workspace = true 5 | license.workspace = true 6 | 7 | [dependencies] 8 | anyhow = { workspace = true } 9 | base64 = { workspace = true } 10 | serde = { workspace = true, features = ["derive"] } 11 | serde_json = { workspace = true } 12 | static-files = { workspace = true } 13 | tera = { workspace = true } 14 | 15 | [build-dependencies] 16 | static-files = { workspace = true } 17 | -------------------------------------------------------------------------------- /server/ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@openubl-ui/root", 3 | "version": "1.0.0", 4 | "license": "Apache-2.0", 5 | "private": true, 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/project-openubl/xhandler-rust.git" 9 | }, 10 | "scripts": { 11 | "clean": "rimraf ./dist && npm run clean -ws --if-present", 12 | "clean:all": "npm run clean:all -ws --if-present && rimraf ./dist ./node_modules", 13 | "dist": "rimraf ./dist && copyfiles -e 'node_modules/**' entrypoint.sh '**/package.json' '*/dist/**/*' ./dist", 14 | "check": "npm run check -ws --if-present", 15 | "check:write": "npm run check:write -ws --if-present", 16 | "build": "npm run build -ws --if-present", 17 | "dev:common": "npm run dev -w common", 18 | "dev:server": "npm run dev -w server", 19 | "dev:client": "npm run dev -w client", 20 | "dev": "concurrently -n common,client -c 'white.bold.inverse,green.bold.inverse,blue.bold.inverse' 'npm:dev:common' 'npm:dev:client'", 21 | "start": "npm run build -w common -w client && npm run start -w server" 22 | }, 23 | "workspaces": ["common", "client", "server"], 24 | "dependencies": { 25 | "@types/express": "^4.17.21", 26 | "http-proxy-middleware": "^2.0.6" 27 | }, 28 | "devDependencies": { 29 | "@biomejs/biome": "^1.9.4", 30 | "@rollup/plugin-commonjs": "^28.0.2", 31 | "@rollup/plugin-json": "^6.1.0", 32 | "@rollup/plugin-node-resolve": "^16.0.0", 33 | "@rollup/plugin-run": "^3.1.0", 34 | "@rollup/plugin-typescript": "^12.1.2", 35 | "@rollup/plugin-virtual": "^3.0.2", 36 | "@types/node": "^22.8.1", 37 | "concurrently": "^9.1.2", 38 | "copyfiles": "^2.4.1", 39 | "rimraf": "^6.0.1", 40 | "rollup": "^3.29.5", 41 | "rollup-plugin-copy": "^3.5.0", 42 | "typescript": "^5.7.3" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /server/ui/server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@openubl-ui/server", 3 | "version": "0.1.0", 4 | "license": "Apache-2.0", 5 | "private": true, 6 | "type": "module", 7 | "scripts": { 8 | "clean": "rimraf ./dist", 9 | "clean:all": "rimraf ./dist ./node_modules", 10 | "check": "biome check .", 11 | "check:write": "biome check --write .", 12 | "prebuild": "npm run clean", 13 | "build": "NODE_ENV=production rollup -c", 14 | "dev": "NODE_ENV=development ROLLUP_RUN=true rollup -c -w", 15 | "start": "npm run build && node --enable-source-maps dist/index.js" 16 | }, 17 | "dependencies": { 18 | "cookie-parser": "^1.4.6", 19 | "ejs": "^3.1.10", 20 | "express": "^4.19.2", 21 | "http-terminator": "^3.2.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /server/ui/server/rollup.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 3 | import commonjs from "@rollup/plugin-commonjs"; 4 | import json from "@rollup/plugin-json"; 5 | import nodeResolve from "@rollup/plugin-node-resolve"; 6 | import run from "@rollup/plugin-run"; 7 | 8 | const buildAndRun = process.env?.ROLLUP_RUN === "true"; 9 | 10 | export default { 11 | input: "src/index.ts", 12 | output: { 13 | file: "dist/index.js", 14 | format: "esm", 15 | sourcemap: true, 16 | }, 17 | watch: { 18 | clearScreen: false, 19 | }, 20 | 21 | plugins: [ 22 | nodeResolve({ 23 | preferBuiltins: true, 24 | }), 25 | commonjs(), 26 | json(), 27 | buildAndRun && 28 | run({ 29 | execArgv: ["-r", "source-map-support/register"], 30 | }), 31 | ], 32 | }; 33 | -------------------------------------------------------------------------------- /server/xtask/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "openubl-xtask" 3 | version.workspace = true 4 | edition.workspace = true 5 | license.workspace = true 6 | 7 | [[bin]] 8 | name = "xtask" 9 | path = "src/main.rs" 10 | 11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 12 | 13 | [dependencies] 14 | openubl-server = { workspace = true } 15 | 16 | anyhow = { workspace = true } 17 | clap = { workspace = true, features = ["derive", "env"] } 18 | tokio = { workspace = true, features = ["full"] } 19 | -------------------------------------------------------------------------------- /server/xtask/src/main.rs: -------------------------------------------------------------------------------- 1 | use clap::{Parser, Subcommand}; 2 | 3 | mod openapi; 4 | 5 | #[derive(Debug, Parser)] 6 | pub struct Xtask { 7 | #[command(subcommand)] 8 | command: Command, 9 | } 10 | 11 | impl Xtask { 12 | pub async fn run(self) -> anyhow::Result<()> { 13 | match self.command { 14 | Command::Openapi(command) => command.run().await, 15 | } 16 | } 17 | } 18 | 19 | #[derive(Debug, Subcommand)] 20 | pub enum Command { 21 | /// Used to generate and/or validate the openapi spec 22 | Openapi(openapi::Openapi), 23 | } 24 | 25 | #[tokio::main] 26 | async fn main() -> anyhow::Result<()> { 27 | Xtask::parse().run().await 28 | } 29 | -------------------------------------------------------------------------------- /xbuilder/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xbuilder" 3 | description = "Creates XML files based on UBL under the standards of Peru." 4 | version.workspace = true 5 | edition.workspace = true 6 | license.workspace = true 7 | 8 | [dependencies] 9 | chrono = { workspace = true, features = ["serde"] } 10 | regex = { workspace = true } 11 | log = { workspace = true } 12 | tera = { workspace = true } 13 | static-files = { workspace = true } 14 | lazy_static = { workspace = true } 15 | serde = { workspace = true, features = ["derive"] } 16 | rust_decimal = { workspace = true } 17 | rust_decimal_macros = { workspace = true } 18 | anyhow = { workspace = true } 19 | 20 | [dev-dependencies] 21 | libxml = { workspace = true } 22 | 23 | serial_test = { workspace = true } 24 | tokio = { workspace = true, features = ["macros"] } 25 | 26 | xsender = { path = "../xsender" } 27 | xsigner = { path = "../xsigner" } 28 | 29 | [build-dependencies] 30 | static-files = { workspace = true } 31 | -------------------------------------------------------------------------------- /xbuilder/build.rs: -------------------------------------------------------------------------------- 1 | use static_files::resource_dir; 2 | 3 | fn main() -> std::io::Result<()> { 4 | resource_dir("./resources/templates").build() 5 | } 6 | -------------------------------------------------------------------------------- /xbuilder/resources/templates/renderer/creditNote.xml: -------------------------------------------------------------------------------- 1 | {%- import "ubl/standard/include/document-line.xml" as macros_document_line -%} 2 | {%- import "ubl/standard/include/address.xml" as macros_address -%} 3 | 4 | 7 | {%- include "ubl/standard/include/ubl-extensions.xml" -%} 8 | {%- include "ubl/standard/include/general-data.xml" -%} 9 | {%- if leyendas %} 10 | {%- for key,value in leyendas %} 11 | 12 | {%- endfor %} 13 | {%- endif %} 14 | {{moneda}} 15 | {%- include "ubl/standard/include/note/invoice-reference.xml" -%} 16 | {%- include "ubl/standard/include/guias.xml" -%} 17 | {%- include "ubl/standard/include/documentos-relacionados.xml" -%} 18 | {%- include "ubl/common/signature.xml" %} 19 | {% include "ubl/standard/include/supplier.xml" %} 20 | {% include "ubl/standard/include/customer.xml" -%} 21 | {% include "ubl/standard/include/tax-total.xml" %} 22 | 23 | {%- include "ubl/standard/include/monetary-total.xml" %} 24 | 25 | {%- for it in detalles %} 26 | 27 | {{loop.index}} 28 | {{it.cantidad}}{{ macros_document_line::line(item=it) }} 29 | 30 | {%- endfor %} 31 | 32 | -------------------------------------------------------------------------------- /xbuilder/resources/templates/renderer/debitNote.xml: -------------------------------------------------------------------------------- 1 | {%- import "ubl/standard/include/document-line.xml" as macros_document_line -%} 2 | {%- import "ubl/standard/include/address.xml" as macros_address -%} 3 | 4 | 7 | {%- include "ubl/standard/include/ubl-extensions.xml" -%} 8 | {%- include "ubl/standard/include/general-data.xml" -%} 9 | {%- if leyendas %} 10 | {%- for key,value in leyendas %} 11 | 12 | {%- endfor %} 13 | {%- endif %} 14 | {{moneda}} 15 | {%- include "ubl/standard/include/note/invoice-reference.xml" -%} 16 | {%- include "ubl/standard/include/guias.xml" -%} 17 | {%- include "ubl/standard/include/documentos-relacionados.xml" -%} 18 | {%- include "ubl/common/signature.xml" %} 19 | {% include "ubl/standard/include/supplier.xml" %} 20 | {% include "ubl/standard/include/customer.xml" -%} 21 | {% include "ubl/standard/include/tax-total.xml" %} 22 | 23 | {%- include "ubl/standard/include/monetary-total.xml" %} 24 | 25 | {%- for it in detalles %} 26 | 27 | {{loop.index}} 28 | {{it.cantidad}}{{ macros_document_line::line(item=it) }} 29 | 30 | {%- endfor %} 31 | 32 | -------------------------------------------------------------------------------- /xbuilder/resources/templates/renderer/voidedDocuments.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | {% include "ubl/standard/include/ubl-extensions.xml" %} 7 | 2.0 8 | 1.0 9 | RA-{fechaEmision.format('yyyyMMdd')}-{numero} 10 | {fechaEmisionComprobantes} 11 | {fechaEmision} 12 | {% include "ubl/common/signature.xml" %} 13 | {% include "ubl/sunat/include/supplier.xml" %} 14 | {%- for it in comprobantes %} 15 | 16 | {{loop.index}} 17 | {it.tipo_comprobante} 18 | {it.serie} 19 | {it.numero} 20 | {it.descripcionSustento} 21 | 22 | {%- endfor %} 23 | 24 | -------------------------------------------------------------------------------- /xbuilder/resources/templates/ubl/common/signature.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{firmante.ruc}} 4 | 5 | 6 | {{firmante.ruc}} 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | #PROJECT-OPENUBL-SIGN 15 | 16 | 17 | -------------------------------------------------------------------------------- /xbuilder/resources/templates/ubl/standard/include/address.xml: -------------------------------------------------------------------------------- 1 | {%- macro address(direccion) -%} 2 | {%- if direccion.ubigeo %} 3 | {{direccion.ubigeo}} 4 | {%- endif %} 5 | {%- if direccion.codigo_local %} 6 | {{direccion.codigo_local}} 7 | {%- endif %} 8 | {%- if direccion.urbanizacion %} 9 | {{direccion.urbanizacion}} 10 | {%- endif %} 11 | {%- if direccion.provincia %} 12 | {{direccion.provincia}} 13 | {%- endif %} 14 | {%- if direccion.departamento %} 15 | {{direccion.departamento}} 16 | {%- endif %} 17 | {%- if direccion.distrito %} 18 | {{direccion.distrito}} 19 | {%- endif %} 20 | {%- if direccion.direccion %} 21 | 22 | 23 | 24 | {%- endif %} 25 | {%- if direccion.codigo_pais %} 26 | 27 | {{direccion.codigo_pais}} 28 | 29 | {%- endif %} 30 | {%- endmacro address -%} -------------------------------------------------------------------------------- /xbuilder/resources/templates/ubl/standard/include/contact.xml: -------------------------------------------------------------------------------- 1 | {%- macro contact(contacto) -%} 2 | 3 | {%- if contacto.telefono %} 4 | {{contacto.telefono}} 5 | {%- endif %} 6 | {%- if contacto.email %} 7 | {{contacto.email}} 8 | {%- endif %} 9 | 10 | {%- endmacro contact -%} -------------------------------------------------------------------------------- /xbuilder/resources/templates/ubl/standard/include/customer.xml: -------------------------------------------------------------------------------- 1 | {%- import "ubl/standard/include/address.xml" as macros_address -%} 2 | {%- import "ubl/standard/include/contact.xml" as macros_contact -%} 3 | 4 | 5 | 6 | 7 | {{cliente.numero_documento_identidad}} 8 | 9 | 10 | 11 | {%- if cliente.direccion %} 12 | {{ macros_address::address(direccion=cliente.direccion) }} 13 | 14 | {%- endif %} 15 | 16 | {%- if cliente.contacto %} 17 | {{ macros_contact::contact(contacto=cliente.contacto) }} 18 | {%- endif %} 19 | 20 | -------------------------------------------------------------------------------- /xbuilder/resources/templates/ubl/standard/include/documentos-relacionados.xml: -------------------------------------------------------------------------------- 1 | 2 | {%- for it in documentos_relacionados %} 3 | 4 | {{it.serie_numero}} 5 | {{it.tipo_documento}} 6 | 7 | {%- endfor %} -------------------------------------------------------------------------------- /xbuilder/resources/templates/ubl/standard/include/general-data.xml: -------------------------------------------------------------------------------- 1 | 2 | 2.1 3 | 2.0 4 | {{serie_numero}} 5 | {{fecha_emision}} 6 | {%- if hora_emision %} 7 | {{hora_emision}} 8 | {%- endif %} -------------------------------------------------------------------------------- /xbuilder/resources/templates/ubl/standard/include/guias.xml: -------------------------------------------------------------------------------- 1 | 2 | {%- for it in guias %} 3 | 4 | {{it.serie_numero}} 5 | {{it.tipo_documento}} 6 | 7 | {%- endfor %} -------------------------------------------------------------------------------- /xbuilder/resources/templates/ubl/standard/include/monetary-total.xml: -------------------------------------------------------------------------------- 1 | 2 | {{total_importe.importe_sin_impuestos | round_decimal}} 3 | {{total_importe.importe | round_decimal}} 4 | {{total_importe.importe | round_decimal}} -------------------------------------------------------------------------------- /xbuilder/resources/templates/ubl/standard/include/namespaces.xml: -------------------------------------------------------------------------------- 1 | 2 | xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" 3 | xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2" 4 | xmlns:ccts="urn:un:unece:uncefact:documentation:2" 5 | xmlns:cec="urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2" 6 | xmlns:ds="http://www.w3.org/2000/09/xmldsig#" 7 | xmlns:ext="urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2" 8 | xmlns:qdt="urn:oasis:names:specification:ubl:schema:xsd:QualifiedDatatypes-2" 9 | xmlns:sac="urn:sunat:names:specification:ubl:peru:schema:xsd:SunatAggregateComponents-1" 10 | xmlns:udt="urn:un:unece:uncefact:data:specification:UnqualifiedDataTypesSchemaModule:2" 11 | xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 12 | -------------------------------------------------------------------------------- /xbuilder/resources/templates/ubl/standard/include/note/invoice-reference.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{comprobante_afectado_serie_numero}} 4 | {{comprobante_afectado_tipo}} 5 | 6 | 7 | {%- if orden_de_compra %} 8 | 9 | {{orden_de_compra}} 10 | 11 | {%- endif %} 12 | 13 | 14 | {{comprobante_afectado_serie_numero}} 15 | {{comprobante_afectado_tipo}} 16 | 17 | -------------------------------------------------------------------------------- /xbuilder/resources/templates/ubl/standard/include/payment-terms.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | FormaPago 4 | {{forma_de_pago.tipo}} 5 | {%- if forma_de_pago.tipo is credito %} 6 | {{forma_de_pago.total | round_decimal}} 7 | {%- endif %} 8 | 9 | {%- for it in forma_de_pago.cuotas %} 10 | 11 | FormaPago 12 | Cuota{{loop.index | format03d}} 13 | {{it.importe | round_decimal}} 14 | {{it.fecha_pago}} 15 | 16 | {%- endfor %} -------------------------------------------------------------------------------- /xbuilder/resources/templates/ubl/standard/include/supplier.xml: -------------------------------------------------------------------------------- 1 | {%- import "ubl/standard/include/address.xml" as macros_address -%} 2 | {%- import "ubl/standard/include/contact.xml" as macros_contact -%} 3 | 4 | 5 | 6 | 7 | {{proveedor.ruc}} 8 | 9 | {%- if proveedor.nombre_comercial %} 10 | 11 | {{proveedor.nombre_comercial}} 12 | 13 | {%- endif %} 14 | 15 | 16 | {%- if proveedor.direccion %} 17 | {{ macros_address::address(direccion=proveedor.direccion) }} 18 | 19 | {%- endif %} 20 | 21 | {%- if proveedor.contacto %} 22 | {{ macros_contact::contact(contacto=proveedor.contacto) }} 23 | {%- endif %} 24 | 25 | -------------------------------------------------------------------------------- /xbuilder/resources/templates/ubl/standard/include/ubl-extensions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /xbuilder/resources/templates/ubl/sunat/include/agent-party.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | {proveedor.ruc} 4 | 5 | {%- if proveedor.nombreComercial %} 6 | 7 | 8 | 9 | {%- endif %} 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /xbuilder/resources/templates/ubl/sunat/include/receiver-party.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | {cliente.numeroDocumentoIdentidad} 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /xbuilder/resources/templates/ubl/sunat/include/supplier.xml: -------------------------------------------------------------------------------- 1 | 2 | {proveedor.ruc} 3 | 6 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/detalle/cantidad.rs: -------------------------------------------------------------------------------- 1 | use rust_decimal::Decimal; 2 | 3 | use crate::models::common::Detalle; 4 | 5 | pub trait DetalleCantidadGetter { 6 | fn get_cantidad(&self) -> Decimal; 7 | } 8 | 9 | impl DetalleCantidadGetter for Detalle { 10 | fn get_cantidad(&self) -> Decimal { 11 | self.cantidad 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/detalle/icb.rs: -------------------------------------------------------------------------------- 1 | use rust_decimal::Decimal; 2 | 3 | use crate::models::common::Detalle; 4 | 5 | pub trait DetalleIcbGetter { 6 | fn get_icb(&self) -> &Option; 7 | } 8 | 9 | pub trait DetalleIcbSetter { 10 | fn set_icb(&mut self, val: Decimal); 11 | } 12 | 13 | impl DetalleIcbGetter for Detalle { 14 | fn get_icb(&self) -> &Option { 15 | &self.icb 16 | } 17 | } 18 | 19 | impl DetalleIcbSetter for Detalle { 20 | fn set_icb(&mut self, val: Decimal) { 21 | self.icb = Some(val); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/detalle/icb_aplica.rs: -------------------------------------------------------------------------------- 1 | use crate::models::common::Detalle; 2 | 3 | pub trait DetalleICBAplicaGetter { 4 | fn get_icb_aplica(&self) -> bool; 5 | } 6 | 7 | pub trait DetalleIcbAplicaSetter { 8 | fn set_icb_aplica(&mut self, val: bool); 9 | } 10 | 11 | impl DetalleICBAplicaGetter for Detalle { 12 | fn get_icb_aplica(&self) -> bool { 13 | self.icb_aplica 14 | } 15 | } 16 | 17 | impl DetalleIcbAplicaSetter for Detalle { 18 | fn set_icb_aplica(&mut self, val: bool) { 19 | self.icb_aplica = val; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/detalle/icb_tasa.rs: -------------------------------------------------------------------------------- 1 | use rust_decimal::Decimal; 2 | 3 | use crate::models::common::Detalle; 4 | 5 | pub trait DetalleIcbTasaGetter { 6 | fn get_icb_tasa(&self) -> &Option; 7 | } 8 | 9 | pub trait DetalleIcbTasaSetter { 10 | fn set_icb_tasa(&mut self, val: Decimal); 11 | } 12 | 13 | impl DetalleIcbTasaGetter for Detalle { 14 | fn get_icb_tasa(&self) -> &Option { 15 | &self.icb_tasa 16 | } 17 | } 18 | 19 | impl DetalleIcbTasaSetter for Detalle { 20 | fn set_icb_tasa(&mut self, val: Decimal) { 21 | self.icb_tasa = Some(val); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/detalle/igv.rs: -------------------------------------------------------------------------------- 1 | use rust_decimal::Decimal; 2 | 3 | use crate::models::common::Detalle; 4 | 5 | pub trait DetalleIgvGetter { 6 | fn get_igv(&self) -> &Option; 7 | } 8 | 9 | pub trait DetalleIgvSetter { 10 | fn set_igv(&mut self, val: Decimal); 11 | } 12 | 13 | impl DetalleIgvGetter for Detalle { 14 | fn get_igv(&self) -> &Option { 15 | &self.igv 16 | } 17 | } 18 | 19 | impl DetalleIgvSetter for Detalle { 20 | fn set_igv(&mut self, val: Decimal) { 21 | self.igv = Some(val); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/detalle/igv_base_imponible.rs: -------------------------------------------------------------------------------- 1 | use rust_decimal::Decimal; 2 | 3 | use crate::models::common::Detalle; 4 | 5 | pub trait DetalleIgvBaseImponibleGetter { 6 | fn get_igv_base_imponible(&self) -> &Option; 7 | } 8 | 9 | pub trait DetalleIGVBaseImponibleSetter { 10 | fn set_igv_base_imponible(&mut self, val: Decimal); 11 | } 12 | 13 | impl DetalleIgvBaseImponibleGetter for Detalle { 14 | fn get_igv_base_imponible(&self) -> &Option { 15 | &self.igv_base_imponible 16 | } 17 | } 18 | 19 | impl DetalleIGVBaseImponibleSetter for Detalle { 20 | fn set_igv_base_imponible(&mut self, val: Decimal) { 21 | self.igv_base_imponible = Some(val); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/detalle/igv_tasa.rs: -------------------------------------------------------------------------------- 1 | use rust_decimal::Decimal; 2 | 3 | use crate::models::common::Detalle; 4 | 5 | pub trait DetalleIgvTasaGetter { 6 | fn get_igv_tasa(&self) -> &Option; 7 | } 8 | 9 | pub trait DetalleIgvTasaSetter { 10 | fn set_igv_tasa(&mut self, val: Decimal); 11 | } 12 | 13 | impl DetalleIgvTasaGetter for Detalle { 14 | fn get_igv_tasa(&self) -> &Option { 15 | &self.igv_tasa 16 | } 17 | } 18 | 19 | impl DetalleIgvTasaSetter for Detalle { 20 | fn set_igv_tasa(&mut self, val: Decimal) { 21 | self.igv_tasa = Some(val); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/detalle/igv_tipo.rs: -------------------------------------------------------------------------------- 1 | use crate::models::common::Detalle; 2 | 3 | pub trait DetalleIgvTipoGetter { 4 | fn get_igv_tipo(&self) -> &Option<&'static str>; 5 | } 6 | 7 | pub trait DetalleIgvTipoSetter { 8 | fn set_igv_tipo(&mut self, val: &'static str); 9 | } 10 | 11 | impl DetalleIgvTipoGetter for Detalle { 12 | fn get_igv_tipo(&self) -> &Option<&'static str> { 13 | &self.igv_tipo 14 | } 15 | } 16 | 17 | impl DetalleIgvTipoSetter for Detalle { 18 | fn set_igv_tipo(&mut self, val: &'static str) { 19 | self.igv_tipo = Some(val); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/detalle/isc.rs: -------------------------------------------------------------------------------- 1 | use rust_decimal::Decimal; 2 | 3 | use crate::models::common::Detalle; 4 | 5 | pub trait DetalleIscGetter { 6 | fn get_isc(&self) -> &Option; 7 | } 8 | 9 | pub trait DetalleISCSetter { 10 | fn set_isc(&mut self, val: Decimal); 11 | } 12 | 13 | impl DetalleIscGetter for Detalle { 14 | fn get_isc(&self) -> &Option { 15 | &self.isc 16 | } 17 | } 18 | 19 | impl DetalleISCSetter for Detalle { 20 | fn set_isc(&mut self, val: Decimal) { 21 | self.isc = Some(val); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/detalle/isc_base_imponible.rs: -------------------------------------------------------------------------------- 1 | use rust_decimal::Decimal; 2 | 3 | use crate::models::common::Detalle; 4 | 5 | pub trait DetalleIscBaseImponibleGetter { 6 | fn get_isc_base_imponible(&self) -> &Option; 7 | } 8 | 9 | pub trait DetalleISCBaseImponibleSetter { 10 | fn set_isc_base_imponible(&mut self, val: Decimal); 11 | } 12 | 13 | impl DetalleIscBaseImponibleGetter for Detalle { 14 | fn get_isc_base_imponible(&self) -> &Option { 15 | &self.isc_base_imponible 16 | } 17 | } 18 | 19 | impl DetalleISCBaseImponibleSetter for Detalle { 20 | fn set_isc_base_imponible(&mut self, val: Decimal) { 21 | self.isc_base_imponible = Some(val); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/detalle/isc_tasa.rs: -------------------------------------------------------------------------------- 1 | use rust_decimal::Decimal; 2 | 3 | use crate::models::common::Detalle; 4 | 5 | pub trait DetalleIscTasaGetter { 6 | fn get_isc_tasa(&self) -> &Option; 7 | } 8 | 9 | pub trait DetalleIscTasaSetter { 10 | fn set_isc_tasa(&mut self, val: Decimal); 11 | } 12 | 13 | impl DetalleIscTasaGetter for Detalle { 14 | fn get_isc_tasa(&self) -> &Option { 15 | &self.isc_tasa 16 | } 17 | } 18 | 19 | impl DetalleIscTasaSetter for Detalle { 20 | fn set_isc_tasa(&mut self, val: Decimal) { 21 | self.isc_tasa = Some(val); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/detalle/isc_tipo.rs: -------------------------------------------------------------------------------- 1 | use crate::models::common::Detalle; 2 | 3 | pub trait DetalleIscTipoGetter { 4 | fn get_isc_tipo(&self) -> &Option<&'static str>; 5 | } 6 | 7 | pub trait DetalleIscTipoSetter { 8 | fn set_isc_tipo(&mut self, val: &'static str); 9 | } 10 | 11 | impl DetalleIscTipoGetter for Detalle { 12 | fn get_isc_tipo(&self) -> &Option<&'static str> { 13 | &self.isc_tipo 14 | } 15 | } 16 | 17 | impl DetalleIscTipoSetter for Detalle { 18 | fn set_isc_tipo(&mut self, val: &'static str) { 19 | self.isc_tipo = Some(val); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/detalle/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::models::common::Detalle; 2 | use crate::models::credit_note::CreditNote; 3 | use crate::models::debit_note::DebitNote; 4 | use crate::models::invoice::Invoice; 5 | 6 | pub mod cantidad; 7 | pub mod icb; 8 | pub mod icb_aplica; 9 | pub mod icb_tasa; 10 | pub mod igv; 11 | pub mod igv_base_imponible; 12 | pub mod igv_tasa; 13 | pub mod igv_tipo; 14 | pub mod isc; 15 | pub mod isc_base_imponible; 16 | pub mod isc_tasa; 17 | pub mod isc_tipo; 18 | pub mod precio; 19 | pub mod precio_con_impuestos; 20 | pub mod precio_referencia; 21 | pub mod precio_referencia_tipo; 22 | pub mod total_impuestos; 23 | pub mod unidad_medida; 24 | 25 | pub trait DetallesGetter { 26 | fn get_detalles(&mut self) -> &mut Vec; 27 | } 28 | 29 | impl DetallesGetter for Invoice { 30 | fn get_detalles(&mut self) -> &mut Vec { 31 | &mut self.detalles 32 | } 33 | } 34 | 35 | impl DetallesGetter for CreditNote { 36 | fn get_detalles(&mut self) -> &mut Vec { 37 | &mut self.detalles 38 | } 39 | } 40 | 41 | impl DetallesGetter for DebitNote { 42 | fn get_detalles(&mut self) -> &mut Vec { 43 | &mut self.detalles 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/detalle/precio.rs: -------------------------------------------------------------------------------- 1 | use rust_decimal::Decimal; 2 | 3 | use crate::models::common::Detalle; 4 | 5 | pub trait DetallePrecioGetter { 6 | fn get_precio(&self) -> &Option; 7 | } 8 | 9 | pub trait DetallePrecioSetter { 10 | fn set_precio(&mut self, val: Decimal); 11 | } 12 | 13 | impl DetallePrecioGetter for Detalle { 14 | fn get_precio(&self) -> &Option { 15 | &self.precio 16 | } 17 | } 18 | 19 | impl DetallePrecioSetter for Detalle { 20 | fn set_precio(&mut self, val: Decimal) { 21 | self.precio = Some(val); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/detalle/precio_con_impuestos.rs: -------------------------------------------------------------------------------- 1 | use rust_decimal::Decimal; 2 | 3 | use crate::models::common::Detalle; 4 | 5 | pub trait DetallePrecioConImpuestosGetter { 6 | fn get_precio_con_impuestos(&self) -> &Option; 7 | } 8 | 9 | pub trait DetallePrecioConImpuestosSetter { 10 | fn set_precio_con_impuestos(&mut self, val: Decimal); 11 | } 12 | 13 | impl DetallePrecioConImpuestosGetter for Detalle { 14 | fn get_precio_con_impuestos(&self) -> &Option { 15 | &self.precio_con_impuestos 16 | } 17 | } 18 | 19 | impl DetallePrecioConImpuestosSetter for Detalle { 20 | fn set_precio_con_impuestos(&mut self, val: Decimal) { 21 | self.precio_con_impuestos = Some(val); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/detalle/precio_referencia.rs: -------------------------------------------------------------------------------- 1 | use rust_decimal::Decimal; 2 | 3 | use crate::models::common::Detalle; 4 | 5 | pub trait DetallePrecioReferenciaGetter { 6 | fn get_precio_referencia(&self) -> &Option; 7 | } 8 | 9 | pub trait DetallePrecioReferenciaSetter { 10 | fn set_precio_referencia(&mut self, val: Decimal); 11 | } 12 | 13 | impl DetallePrecioReferenciaGetter for Detalle { 14 | fn get_precio_referencia(&self) -> &Option { 15 | &self.precio_referencia 16 | } 17 | } 18 | 19 | impl DetallePrecioReferenciaSetter for Detalle { 20 | fn set_precio_referencia(&mut self, val: Decimal) { 21 | self.precio_referencia = Some(val); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/detalle/precio_referencia_tipo.rs: -------------------------------------------------------------------------------- 1 | use crate::models::common::Detalle; 2 | 3 | pub trait DetallePrecioReferenciaTipoGetter { 4 | fn get_precio_referencia_tipo(&self) -> &Option<&'static str>; 5 | } 6 | 7 | pub trait DetallePrecioReferenciaTipoSetter { 8 | fn set_precio_referencia_tipo(&mut self, val: &'static str); 9 | } 10 | 11 | impl DetallePrecioReferenciaTipoGetter for Detalle { 12 | fn get_precio_referencia_tipo(&self) -> &Option<&'static str> { 13 | &self.precio_referencia_tipo 14 | } 15 | } 16 | 17 | impl DetallePrecioReferenciaTipoSetter for Detalle { 18 | fn set_precio_referencia_tipo(&mut self, val: &'static str) { 19 | self.precio_referencia_tipo = Some(val); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/detalle/total_impuestos.rs: -------------------------------------------------------------------------------- 1 | use rust_decimal::Decimal; 2 | 3 | use crate::models::common::Detalle; 4 | 5 | pub trait DetalleTotalImpuestosGetter { 6 | fn get_total_impuestos(&self) -> &Option; 7 | } 8 | 9 | pub trait DetalleTotalImpuestosSetter { 10 | fn set_total_impuestos(&mut self, val: Decimal); 11 | } 12 | 13 | impl DetalleTotalImpuestosGetter for Detalle { 14 | fn get_total_impuestos(&self) -> &Option { 15 | &self.total_impuestos 16 | } 17 | } 18 | 19 | impl DetalleTotalImpuestosSetter for Detalle { 20 | fn set_total_impuestos(&mut self, val: Decimal) { 21 | self.total_impuestos = Some(val); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/detalle/unidad_medida.rs: -------------------------------------------------------------------------------- 1 | use crate::models::common::Detalle; 2 | 3 | pub trait DetalleUnidadMedidaGetter { 4 | fn get_unidad_medida(&self) -> &Option<&'static str>; 5 | } 6 | 7 | pub trait DetalleUnidadMedidaSetter { 8 | fn set_unidad_medida(&mut self, val: &'static str); 9 | } 10 | 11 | impl DetalleUnidadMedidaGetter for Detalle { 12 | fn get_unidad_medida(&self) -> &Option<&'static str> { 13 | &self.unidad_medida 14 | } 15 | } 16 | 17 | impl DetalleUnidadMedidaSetter for Detalle { 18 | fn set_unidad_medida(&mut self, val: &'static str) { 19 | self.unidad_medida = Some(val); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/fecha_emision.rs: -------------------------------------------------------------------------------- 1 | use chrono::NaiveDate; 2 | 3 | use crate::models::credit_note::CreditNote; 4 | use crate::models::debit_note::DebitNote; 5 | use crate::models::invoice::Invoice; 6 | 7 | pub trait FechaEmisionGetter { 8 | fn get_fecha_emision(&self) -> &Option; 9 | } 10 | 11 | pub trait FechaEmisionSetter { 12 | fn set_fecha_emision(&mut self, val: NaiveDate); 13 | } 14 | 15 | // 16 | 17 | impl FechaEmisionGetter for Invoice { 18 | fn get_fecha_emision(&self) -> &Option { 19 | &self.fecha_emision 20 | } 21 | } 22 | 23 | impl FechaEmisionGetter for CreditNote { 24 | fn get_fecha_emision(&self) -> &Option { 25 | &self.fecha_emision 26 | } 27 | } 28 | 29 | impl FechaEmisionGetter for DebitNote { 30 | fn get_fecha_emision(&self) -> &Option { 31 | &self.fecha_emision 32 | } 33 | } 34 | 35 | impl FechaEmisionSetter for Invoice { 36 | fn set_fecha_emision(&mut self, val: NaiveDate) { 37 | self.fecha_emision = Some(val); 38 | } 39 | } 40 | 41 | impl FechaEmisionSetter for CreditNote { 42 | fn set_fecha_emision(&mut self, val: NaiveDate) { 43 | self.fecha_emision = Some(val); 44 | } 45 | } 46 | 47 | impl FechaEmisionSetter for DebitNote { 48 | fn set_fecha_emision(&mut self, val: NaiveDate) { 49 | self.fecha_emision = Some(val); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/firmante.rs: -------------------------------------------------------------------------------- 1 | use crate::models::common::Firmante; 2 | use crate::models::credit_note::CreditNote; 3 | use crate::models::debit_note::DebitNote; 4 | use crate::models::invoice::Invoice; 5 | 6 | pub trait FirmanteGetter { 7 | fn get_firmante(&self) -> &Option; 8 | } 9 | 10 | pub trait FirmanteSetter { 11 | fn set_firmante(&mut self, val: Firmante); 12 | } 13 | 14 | impl FirmanteGetter for Invoice { 15 | fn get_firmante(&self) -> &Option { 16 | &self.firmante 17 | } 18 | } 19 | 20 | impl FirmanteGetter for CreditNote { 21 | fn get_firmante(&self) -> &Option { 22 | &self.firmante 23 | } 24 | } 25 | 26 | impl FirmanteGetter for DebitNote { 27 | fn get_firmante(&self) -> &Option { 28 | &self.firmante 29 | } 30 | } 31 | 32 | impl FirmanteSetter for Invoice { 33 | fn set_firmante(&mut self, val: Firmante) { 34 | self.firmante = Some(val); 35 | } 36 | } 37 | 38 | impl FirmanteSetter for CreditNote { 39 | fn set_firmante(&mut self, val: Firmante) { 40 | self.firmante = Some(val); 41 | } 42 | } 43 | 44 | impl FirmanteSetter for DebitNote { 45 | fn set_firmante(&mut self, val: Firmante) { 46 | self.firmante = Some(val); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/icb.rs: -------------------------------------------------------------------------------- 1 | use rust_decimal::Decimal; 2 | 3 | use crate::models::credit_note::CreditNote; 4 | use crate::models::debit_note::DebitNote; 5 | use crate::models::invoice::Invoice; 6 | 7 | pub trait IcbTasaGetter { 8 | fn get_icb_tasa(&self) -> &Option; 9 | } 10 | 11 | pub trait IcbTasaSetter { 12 | fn set_icb_tasa(&mut self, val: Decimal); 13 | } 14 | 15 | impl IcbTasaGetter for Invoice { 16 | fn get_icb_tasa(&self) -> &Option { 17 | &self.icb_tasa 18 | } 19 | } 20 | 21 | impl IcbTasaGetter for CreditNote { 22 | fn get_icb_tasa(&self) -> &Option { 23 | &self.icb_tasa 24 | } 25 | } 26 | 27 | impl IcbTasaGetter for DebitNote { 28 | fn get_icb_tasa(&self) -> &Option { 29 | &self.icb_tasa 30 | } 31 | } 32 | 33 | impl IcbTasaSetter for Invoice { 34 | fn set_icb_tasa(&mut self, val: Decimal) { 35 | self.icb_tasa = Some(val); 36 | } 37 | } 38 | 39 | impl IcbTasaSetter for CreditNote { 40 | fn set_icb_tasa(&mut self, val: Decimal) { 41 | self.icb_tasa = Some(val); 42 | } 43 | } 44 | 45 | impl IcbTasaSetter for DebitNote { 46 | fn set_icb_tasa(&mut self, val: Decimal) { 47 | self.icb_tasa = Some(val); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/igv.rs: -------------------------------------------------------------------------------- 1 | use rust_decimal::Decimal; 2 | 3 | use crate::models::credit_note::CreditNote; 4 | use crate::models::debit_note::DebitNote; 5 | use crate::models::invoice::Invoice; 6 | 7 | pub trait IgvTasaGetter { 8 | fn get_igv_tasa(&self) -> &Option; 9 | } 10 | 11 | pub trait IgvTasaSetter { 12 | fn set_igv_tasa(&mut self, val: Decimal); 13 | } 14 | 15 | impl IgvTasaGetter for Invoice { 16 | fn get_igv_tasa(&self) -> &Option { 17 | &self.igv_tasa 18 | } 19 | } 20 | 21 | impl IgvTasaGetter for CreditNote { 22 | fn get_igv_tasa(&self) -> &Option { 23 | &self.igv_tasa 24 | } 25 | } 26 | 27 | impl IgvTasaGetter for DebitNote { 28 | fn get_igv_tasa(&self) -> &Option { 29 | &self.igv_tasa 30 | } 31 | } 32 | 33 | impl IgvTasaSetter for Invoice { 34 | fn set_igv_tasa(&mut self, val: Decimal) { 35 | self.igv_tasa = Some(val); 36 | } 37 | } 38 | 39 | impl IgvTasaSetter for CreditNote { 40 | fn set_igv_tasa(&mut self, val: Decimal) { 41 | self.igv_tasa = Some(val); 42 | } 43 | } 44 | 45 | impl IgvTasaSetter for DebitNote { 46 | fn set_igv_tasa(&mut self, val: Decimal) { 47 | self.igv_tasa = Some(val); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/invoice/anticipos.rs: -------------------------------------------------------------------------------- 1 | use crate::models::common::Anticipo; 2 | use crate::models::invoice::Invoice; 3 | 4 | pub trait InvoiceAnticiposGetter { 5 | fn get_anticipos(&mut self) -> &mut Vec; 6 | } 7 | 8 | impl InvoiceAnticiposGetter for Invoice { 9 | fn get_anticipos(&mut self) -> &mut Vec { 10 | &mut self.anticipos 11 | } 12 | } 13 | 14 | // 15 | 16 | pub trait AnticipoGetter { 17 | fn get_tipo(&self) -> &Option<&'static str>; 18 | fn get_comprobante_tipo(&self) -> &Option<&'static str>; 19 | fn get_comprobante_serie_numero(&self) -> &'static str; 20 | } 21 | 22 | pub trait AnticipoSetter { 23 | fn set_tipo(&mut self, val: &'static str); 24 | fn set_comprobante_tipo(&mut self, val: &'static str); 25 | } 26 | 27 | impl AnticipoGetter for Anticipo { 28 | fn get_tipo(&self) -> &Option<&'static str> { 29 | &self.tipo 30 | } 31 | 32 | fn get_comprobante_tipo(&self) -> &Option<&'static str> { 33 | &self.comprobante_tipo 34 | } 35 | 36 | fn get_comprobante_serie_numero(&self) -> &'static str { 37 | self.comprobante_serie_numero 38 | } 39 | } 40 | 41 | impl AnticipoSetter for Anticipo { 42 | fn set_tipo(&mut self, val: &'static str) { 43 | self.tipo = Some(val); 44 | } 45 | 46 | fn set_comprobante_tipo(&mut self, val: &'static str) { 47 | self.comprobante_tipo = Some(val); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/invoice/descuentos.rs: -------------------------------------------------------------------------------- 1 | use rust_decimal::Decimal; 2 | 3 | use crate::models::common::Descuento; 4 | use crate::models::invoice::Invoice; 5 | 6 | pub trait InvoiceDescuentosGetter { 7 | fn get_descuentos(&mut self) -> &mut Vec; 8 | } 9 | 10 | impl InvoiceDescuentosGetter for Invoice { 11 | fn get_descuentos(&mut self) -> &mut Vec { 12 | &mut self.descuentos 13 | } 14 | } 15 | 16 | // 17 | 18 | pub trait DescuentoGetter { 19 | fn get_tipo(&self) -> &Option<&'static str>; 20 | fn get_monto(&self) -> Decimal; 21 | fn get_monto_base(&self) -> &Option; 22 | fn get_factor(&self) -> &Option; 23 | } 24 | 25 | pub trait DescuentoSetter { 26 | fn set_tipo(&mut self, val: &'static str); 27 | fn set_monto_base(&mut self, val: Decimal); 28 | fn set_factor(&mut self, val: Decimal); 29 | } 30 | 31 | impl DescuentoGetter for Descuento { 32 | fn get_tipo(&self) -> &Option<&'static str> { 33 | &self.tipo 34 | } 35 | 36 | fn get_monto(&self) -> Decimal { 37 | self.monto 38 | } 39 | 40 | fn get_monto_base(&self) -> &Option { 41 | &self.monto_base 42 | } 43 | 44 | fn get_factor(&self) -> &Option { 45 | &self.factor 46 | } 47 | } 48 | 49 | impl DescuentoSetter for Descuento { 50 | fn set_tipo(&mut self, val: &'static str) { 51 | self.tipo = Some(val); 52 | } 53 | 54 | fn set_monto_base(&mut self, val: Decimal) { 55 | self.monto_base = Some(val); 56 | } 57 | 58 | fn set_factor(&mut self, val: Decimal) { 59 | self.factor = Some(val); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/invoice/detraccion.rs: -------------------------------------------------------------------------------- 1 | use rust_decimal::Decimal; 2 | 3 | use crate::models::common::Detraccion; 4 | use crate::models::invoice::Invoice; 5 | 6 | pub trait InvoiceDetraccionGetter { 7 | fn get_detraccion(&mut self) -> &mut Option; 8 | } 9 | 10 | impl InvoiceDetraccionGetter for Invoice { 11 | fn get_detraccion(&mut self) -> &mut Option { 12 | &mut self.detraccion 13 | } 14 | } 15 | 16 | // Monto 17 | 18 | pub trait InvoiceDetraccionMontoGetter { 19 | fn get_monto(&self) -> &Option; 20 | } 21 | 22 | pub trait InvoiceDetraccionMontoSetter { 23 | fn set_monto(&mut self, val: Decimal); 24 | } 25 | 26 | impl InvoiceDetraccionMontoGetter for Detraccion { 27 | fn get_monto(&self) -> &Option { 28 | &self.monto 29 | } 30 | } 31 | 32 | impl InvoiceDetraccionMontoSetter for Detraccion { 33 | fn set_monto(&mut self, val: Decimal) { 34 | self.monto = Some(val); 35 | } 36 | } 37 | 38 | // Porcentaje 39 | 40 | // Monto 41 | 42 | pub trait InvoiceDetraccionPorcentajeGetter { 43 | fn get_porcentaje(&self) -> &Decimal; 44 | } 45 | 46 | impl InvoiceDetraccionPorcentajeGetter for Detraccion { 47 | fn get_porcentaje(&self) -> &Decimal { 48 | &self.porcentaje 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/invoice/direccion_entrega.rs: -------------------------------------------------------------------------------- 1 | use crate::models::common::Direccion; 2 | use crate::models::invoice::Invoice; 3 | 4 | pub trait InvoiceDireccionEntregaGetter { 5 | fn get_direccion_entrega(&self) -> &Option; 6 | } 7 | 8 | impl InvoiceDireccionEntregaGetter for Invoice { 9 | fn get_direccion_entrega(&self) -> &Option { 10 | &self.direccion_entrega 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/invoice/forma_de_pago.rs: -------------------------------------------------------------------------------- 1 | use crate::models::common::FormaDePago; 2 | use crate::models::invoice::Invoice; 3 | 4 | pub trait InvoiceFormaDePagoGetter { 5 | fn get_forma_de_pago(&self) -> &Option; 6 | } 7 | 8 | pub trait InvoiceFormaDePagoSetter { 9 | fn set_forma_de_pago(&mut self, val: FormaDePago); 10 | } 11 | 12 | impl InvoiceFormaDePagoGetter for Invoice { 13 | fn get_forma_de_pago(&self) -> &Option { 14 | &self.forma_de_pago 15 | } 16 | } 17 | 18 | impl InvoiceFormaDePagoSetter for Invoice { 19 | fn set_forma_de_pago(&mut self, val: FormaDePago) { 20 | self.forma_de_pago = Some(val); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/invoice/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod anticipos; 2 | pub mod descuentos; 3 | pub mod detraccion; 4 | pub mod direccion_entrega; 5 | pub mod forma_de_pago; 6 | pub mod percepcion; 7 | pub mod tipo_comprobante; 8 | pub mod tipo_operacion; 9 | pub mod total_importe; 10 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/invoice/tipo_comprobante.rs: -------------------------------------------------------------------------------- 1 | use crate::models::invoice::Invoice; 2 | 3 | pub trait InvoiceTipoComprobanteGetter { 4 | fn get_tipo_comprobante(&self) -> &Option<&'static str>; 5 | } 6 | 7 | pub trait InvoiceTipoComprobanteSetter { 8 | fn set_tipo_comprobante(&mut self, val: &'static str); 9 | } 10 | 11 | impl InvoiceTipoComprobanteGetter for Invoice { 12 | fn get_tipo_comprobante(&self) -> &Option<&'static str> { 13 | &self.tipo_comprobante 14 | } 15 | } 16 | 17 | impl InvoiceTipoComprobanteSetter for Invoice { 18 | fn set_tipo_comprobante(&mut self, val: &'static str) { 19 | self.tipo_comprobante = Some(val); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/invoice/tipo_operacion.rs: -------------------------------------------------------------------------------- 1 | use crate::models::invoice::Invoice; 2 | 3 | pub trait InvoiceTipoOperacionGetter { 4 | fn get_tipo_operacion(&self) -> &Option<&'static str>; 5 | } 6 | 7 | pub trait InvoiceTipoOperacionSetter { 8 | fn set_tipo_operacion(&mut self, val: &'static str); 9 | } 10 | 11 | impl InvoiceTipoOperacionGetter for Invoice { 12 | fn get_tipo_operacion(&self) -> &Option<&'static str> { 13 | &self.tipo_operacion 14 | } 15 | } 16 | 17 | impl InvoiceTipoOperacionSetter for Invoice { 18 | fn set_tipo_operacion(&mut self, val: &'static str) { 19 | self.tipo_operacion = Some(val); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/invoice/total_importe.rs: -------------------------------------------------------------------------------- 1 | use crate::models::common::TotalImporteInvoice; 2 | use crate::models::invoice::Invoice; 3 | 4 | pub trait InvoiceTotalImporteGetter { 5 | fn get_total_importe(&self) -> &Option; 6 | } 7 | 8 | pub trait InvoiceTotalImporteSetter { 9 | fn set_total_importe(&mut self, val: TotalImporteInvoice); 10 | } 11 | 12 | impl InvoiceTotalImporteGetter for Invoice { 13 | fn get_total_importe(&self) -> &Option { 14 | &self.total_importe 15 | } 16 | } 17 | 18 | impl InvoiceTotalImporteSetter for Invoice { 19 | fn set_total_importe(&mut self, val: TotalImporteInvoice) { 20 | self.total_importe = Some(val); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/ivap.rs: -------------------------------------------------------------------------------- 1 | use rust_decimal::Decimal; 2 | 3 | use crate::models::credit_note::CreditNote; 4 | use crate::models::debit_note::DebitNote; 5 | use crate::models::invoice::Invoice; 6 | 7 | pub trait IvapTasaGetter { 8 | fn get_ivap_tasa(&self) -> &Option; 9 | } 10 | 11 | pub trait IvapTasaSetter { 12 | fn set_ivap_tasa(&mut self, val: Decimal); 13 | } 14 | 15 | impl IvapTasaGetter for Invoice { 16 | fn get_ivap_tasa(&self) -> &Option { 17 | &self.ivap_tasa 18 | } 19 | } 20 | 21 | impl IvapTasaGetter for CreditNote { 22 | fn get_ivap_tasa(&self) -> &Option { 23 | &self.ivap_tasa 24 | } 25 | } 26 | 27 | impl IvapTasaGetter for DebitNote { 28 | fn get_ivap_tasa(&self) -> &Option { 29 | &self.ivap_tasa 30 | } 31 | } 32 | 33 | impl IvapTasaSetter for Invoice { 34 | fn set_ivap_tasa(&mut self, val: Decimal) { 35 | self.ivap_tasa = Some(val); 36 | } 37 | } 38 | 39 | impl IvapTasaSetter for CreditNote { 40 | fn set_ivap_tasa(&mut self, val: Decimal) { 41 | self.ivap_tasa = Some(val); 42 | } 43 | } 44 | 45 | impl IvapTasaSetter for DebitNote { 46 | fn set_ivap_tasa(&mut self, val: Decimal) { 47 | self.ivap_tasa = Some(val); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/leyendas.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use crate::models::credit_note::CreditNote; 4 | use crate::models::debit_note::DebitNote; 5 | use crate::models::invoice::Invoice; 6 | 7 | pub trait LeyendasGetter { 8 | fn get_leyendas(&self) -> &HashMap<&str, &str>; 9 | } 10 | 11 | pub trait LeyendasSetter { 12 | fn insert_leyendas(&mut self, k: &'static str, v: &'static str); 13 | } 14 | 15 | impl LeyendasGetter for Invoice { 16 | fn get_leyendas(&self) -> &HashMap<&str, &str> { 17 | &self.leyendas 18 | } 19 | } 20 | 21 | impl LeyendasGetter for CreditNote { 22 | fn get_leyendas(&self) -> &HashMap<&str, &str> { 23 | &self.leyendas 24 | } 25 | } 26 | 27 | impl LeyendasGetter for DebitNote { 28 | fn get_leyendas(&self) -> &HashMap<&str, &str> { 29 | &self.leyendas 30 | } 31 | } 32 | 33 | impl LeyendasSetter for Invoice { 34 | fn insert_leyendas(&mut self, k: &'static str, v: &'static str) { 35 | self.leyendas.insert(k, v); 36 | } 37 | } 38 | 39 | impl LeyendasSetter for CreditNote { 40 | fn insert_leyendas(&mut self, k: &'static str, v: &'static str) { 41 | self.leyendas.insert(k, v); 42 | } 43 | } 44 | 45 | impl LeyendasSetter for DebitNote { 46 | fn insert_leyendas(&mut self, k: &'static str, v: &'static str) { 47 | self.leyendas.insert(k, v); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod detalle; 2 | pub mod fecha_emision; 3 | pub mod firmante; 4 | pub mod icb; 5 | pub mod igv; 6 | pub mod invoice; 7 | pub mod ivap; 8 | pub mod leyendas; 9 | pub mod moneda; 10 | pub mod note; 11 | pub mod proveedor; 12 | pub mod serie_numero; 13 | pub mod total_impuestos; 14 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/moneda.rs: -------------------------------------------------------------------------------- 1 | use crate::models::credit_note::CreditNote; 2 | use crate::models::debit_note::DebitNote; 3 | use crate::models::invoice::Invoice; 4 | 5 | pub trait MonedaGetter { 6 | fn get_moneda(&self) -> &Option<&str>; 7 | } 8 | 9 | pub trait MonedaSetter { 10 | fn set_moneda(&mut self, val: &'static str); 11 | } 12 | 13 | impl MonedaGetter for Invoice { 14 | fn get_moneda(&self) -> &Option<&str> { 15 | &self.moneda 16 | } 17 | } 18 | 19 | impl MonedaGetter for CreditNote { 20 | fn get_moneda(&self) -> &Option<&str> { 21 | &self.moneda 22 | } 23 | } 24 | 25 | impl MonedaGetter for DebitNote { 26 | fn get_moneda(&self) -> &Option<&str> { 27 | &self.moneda 28 | } 29 | } 30 | 31 | impl MonedaSetter for Invoice { 32 | fn set_moneda(&mut self, val: &'static str) { 33 | self.moneda = Some(val); 34 | } 35 | } 36 | 37 | impl MonedaSetter for CreditNote { 38 | fn set_moneda(&mut self, val: &'static str) { 39 | self.moneda = Some(val); 40 | } 41 | } 42 | 43 | impl MonedaSetter for DebitNote { 44 | fn set_moneda(&mut self, val: &'static str) { 45 | self.moneda = Some(val); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/note/creditnote/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod tipo_nota; 2 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/note/creditnote/tipo_nota.rs: -------------------------------------------------------------------------------- 1 | use crate::models::credit_note::CreditNote; 2 | 3 | pub trait CreditNoteTipoGetter { 4 | fn get_tipo_nota(&self) -> &Option<&'static str>; 5 | } 6 | 7 | pub trait CreditNoteTipoSetter { 8 | fn set_tipo_nota(&mut self, val: &'static str); 9 | } 10 | 11 | impl CreditNoteTipoGetter for CreditNote { 12 | fn get_tipo_nota(&self) -> &Option<&'static str> { 13 | &self.tipo_nota 14 | } 15 | } 16 | 17 | impl CreditNoteTipoSetter for CreditNote { 18 | fn set_tipo_nota(&mut self, val: &'static str) { 19 | self.tipo_nota = Some(val); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/note/debitnote/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod tipo_nota; 2 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/note/debitnote/tipo_nota.rs: -------------------------------------------------------------------------------- 1 | use crate::models::debit_note::DebitNote; 2 | 3 | pub trait DebitNoteTipoGetter { 4 | fn get_tipo_nota(&self) -> &Option<&'static str>; 5 | } 6 | 7 | pub trait DebitNoteTipoSetter { 8 | fn set_tipo_nota(&mut self, val: &'static str); 9 | } 10 | 11 | impl DebitNoteTipoGetter for DebitNote { 12 | fn get_tipo_nota(&self) -> &Option<&'static str> { 13 | &self.tipo_nota 14 | } 15 | } 16 | 17 | impl DebitNoteTipoSetter for DebitNote { 18 | fn set_tipo_nota(&mut self, val: &'static str) { 19 | self.tipo_nota = Some(val); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/note/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod creditnote; 2 | pub mod debitnote; 3 | pub mod tipo_comprobante_afectado; 4 | pub mod total_importe; 5 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/note/tipo_comprobante_afectado.rs: -------------------------------------------------------------------------------- 1 | use crate::models::credit_note::CreditNote; 2 | use crate::models::debit_note::DebitNote; 3 | 4 | pub trait NoteTipoComprobanteAfectadoGetter { 5 | fn get_comprobante_afectado_tipo(&self) -> &Option<&'static str>; 6 | } 7 | 8 | pub trait NoteTipoComprobanteAfectadoSetter { 9 | fn set_comprobante_afectado_tipo(&mut self, val: &'static str); 10 | } 11 | 12 | impl NoteTipoComprobanteAfectadoGetter for CreditNote { 13 | fn get_comprobante_afectado_tipo(&self) -> &Option<&'static str> { 14 | &self.comprobante_afectado_tipo 15 | } 16 | } 17 | 18 | impl NoteTipoComprobanteAfectadoGetter for DebitNote { 19 | fn get_comprobante_afectado_tipo(&self) -> &Option<&'static str> { 20 | &self.comprobante_afectado_tipo 21 | } 22 | } 23 | 24 | impl NoteTipoComprobanteAfectadoSetter for CreditNote { 25 | fn set_comprobante_afectado_tipo(&mut self, val: &'static str) { 26 | self.comprobante_afectado_tipo = Some(val); 27 | } 28 | } 29 | 30 | impl NoteTipoComprobanteAfectadoSetter for DebitNote { 31 | fn set_comprobante_afectado_tipo(&mut self, val: &'static str) { 32 | self.comprobante_afectado_tipo = Some(val); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/note/total_importe.rs: -------------------------------------------------------------------------------- 1 | use crate::models::common::TotalImporteNote; 2 | use crate::models::credit_note::CreditNote; 3 | use crate::models::debit_note::DebitNote; 4 | 5 | pub trait NoteTotalImporteGetter { 6 | fn get_total_importe(&self) -> &Option; 7 | } 8 | 9 | pub trait NoteTotalImporteSetter { 10 | fn set_total_importe(&mut self, val: TotalImporteNote); 11 | } 12 | 13 | impl NoteTotalImporteGetter for CreditNote { 14 | fn get_total_importe(&self) -> &Option { 15 | &self.total_importe 16 | } 17 | } 18 | 19 | impl NoteTotalImporteGetter for DebitNote { 20 | fn get_total_importe(&self) -> &Option { 21 | &self.total_importe 22 | } 23 | } 24 | 25 | impl NoteTotalImporteSetter for CreditNote { 26 | fn set_total_importe(&mut self, val: TotalImporteNote) { 27 | self.total_importe = Some(val); 28 | } 29 | } 30 | 31 | impl NoteTotalImporteSetter for DebitNote { 32 | fn set_total_importe(&mut self, val: TotalImporteNote) { 33 | self.total_importe = Some(val); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/proveedor.rs: -------------------------------------------------------------------------------- 1 | use crate::models::common::{Direccion, Proveedor}; 2 | use crate::models::credit_note::CreditNote; 3 | use crate::models::debit_note::DebitNote; 4 | use crate::models::invoice::Invoice; 5 | 6 | pub trait ProveedorGetter { 7 | fn get_proveedor(&self) -> &Proveedor; 8 | } 9 | 10 | pub trait ProveedorSetter { 11 | fn set_proveedor_direccion(&mut self, val: Direccion); 12 | } 13 | 14 | impl ProveedorGetter for Invoice { 15 | fn get_proveedor(&self) -> &Proveedor { 16 | &self.proveedor 17 | } 18 | } 19 | 20 | impl ProveedorGetter for CreditNote { 21 | fn get_proveedor(&self) -> &Proveedor { 22 | &self.proveedor 23 | } 24 | } 25 | 26 | impl ProveedorGetter for DebitNote { 27 | fn get_proveedor(&self) -> &Proveedor { 28 | &self.proveedor 29 | } 30 | } 31 | 32 | impl ProveedorSetter for Invoice { 33 | fn set_proveedor_direccion(&mut self, val: Direccion) { 34 | self.proveedor.direccion = Some(val); 35 | } 36 | } 37 | 38 | impl ProveedorSetter for CreditNote { 39 | fn set_proveedor_direccion(&mut self, val: Direccion) { 40 | self.proveedor.direccion = Some(val); 41 | } 42 | } 43 | 44 | impl ProveedorSetter for DebitNote { 45 | fn set_proveedor_direccion(&mut self, val: Direccion) { 46 | self.proveedor.direccion = Some(val); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/serie_numero.rs: -------------------------------------------------------------------------------- 1 | use crate::models::credit_note::CreditNote; 2 | use crate::models::debit_note::DebitNote; 3 | use crate::models::invoice::Invoice; 4 | 5 | pub trait SerieNumeroGetter { 6 | fn get_serie_numero(&self) -> &str; 7 | } 8 | 9 | impl SerieNumeroGetter for Invoice { 10 | fn get_serie_numero(&self) -> &str { 11 | self.serie_numero 12 | } 13 | } 14 | 15 | impl SerieNumeroGetter for CreditNote { 16 | fn get_serie_numero(&self) -> &str { 17 | self.serie_numero 18 | } 19 | } 20 | 21 | impl SerieNumeroGetter for DebitNote { 22 | fn get_serie_numero(&self) -> &str { 23 | self.serie_numero 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/bounds/total_impuestos.rs: -------------------------------------------------------------------------------- 1 | use crate::models::common::TotalImpuestos; 2 | use crate::models::credit_note::CreditNote; 3 | use crate::models::debit_note::DebitNote; 4 | use crate::models::invoice::Invoice; 5 | 6 | pub trait TotalImpuestosGetter { 7 | fn get_total_impuestos(&self) -> &Option; 8 | } 9 | 10 | pub trait TotalImpuestosSetter { 11 | fn set_total_impuestos(&mut self, val: TotalImpuestos); 12 | } 13 | 14 | impl TotalImpuestosGetter for Invoice { 15 | fn get_total_impuestos(&self) -> &Option { 16 | &self.total_impuestos 17 | } 18 | } 19 | 20 | impl TotalImpuestosGetter for CreditNote { 21 | fn get_total_impuestos(&self) -> &Option { 22 | &self.total_impuestos 23 | } 24 | } 25 | 26 | impl TotalImpuestosGetter for DebitNote { 27 | fn get_total_impuestos(&self) -> &Option { 28 | &self.total_impuestos 29 | } 30 | } 31 | 32 | impl TotalImpuestosSetter for Invoice { 33 | fn set_total_impuestos(&mut self, val: TotalImpuestos) { 34 | self.total_impuestos = Some(val); 35 | } 36 | } 37 | 38 | impl TotalImpuestosSetter for CreditNote { 39 | fn set_total_impuestos(&mut self, val: TotalImpuestos) { 40 | self.total_impuestos = Some(val); 41 | } 42 | } 43 | 44 | impl TotalImpuestosSetter for DebitNote { 45 | fn set_total_impuestos(&mut self, val: TotalImpuestos) { 46 | self.total_impuestos = Some(val); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/mod.rs: -------------------------------------------------------------------------------- 1 | use chrono::NaiveDate; 2 | use rust_decimal::Decimal; 3 | 4 | use crate::enricher::fill::Fill; 5 | use crate::enricher::process::Process; 6 | use crate::enricher::summary::Summary; 7 | use crate::models::credit_note::CreditNote; 8 | use crate::models::debit_note::DebitNote; 9 | use crate::models::invoice::Invoice; 10 | 11 | mod bounds; 12 | mod fill; 13 | mod process; 14 | mod rules; 15 | mod summary; 16 | 17 | pub struct Defaults { 18 | pub icb_tasa: Decimal, 19 | pub igv_tasa: Decimal, 20 | pub ivap_tasa: Decimal, 21 | pub date: NaiveDate, 22 | } 23 | 24 | pub trait Enrich { 25 | fn enrich(&mut self, defaults: &Defaults); 26 | } 27 | 28 | impl Enrich for Invoice { 29 | fn enrich(&mut self, defaults: &Defaults) { 30 | Fill::fill(self, defaults); 31 | Process::process(self); 32 | Summary::summary(self); 33 | } 34 | } 35 | 36 | impl Enrich for CreditNote { 37 | fn enrich(&mut self, defaults: &Defaults) { 38 | Fill::fill(self, defaults); 39 | Process::process(self); 40 | Summary::summary(self); 41 | } 42 | } 43 | 44 | impl Enrich for DebitNote { 45 | fn enrich(&mut self, defaults: &Defaults) { 46 | Fill::fill(self, defaults); 47 | Process::process(self); 48 | Summary::summary(self); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/process.rs: -------------------------------------------------------------------------------- 1 | use crate::enricher::rules::phase2process::detalle::detalles::DetallesProcessRule; 2 | use crate::models::credit_note::CreditNote; 3 | use crate::models::debit_note::DebitNote; 4 | use crate::models::invoice::Invoice; 5 | 6 | pub trait Process { 7 | fn process(&mut self); 8 | } 9 | 10 | trait ProcessCommon { 11 | fn process_common(&mut self); 12 | } 13 | 14 | // trait ProcessInvoice { 15 | // fn process_invoice(&mut self); 16 | // } 17 | // 18 | // trait ProcessCreditNote { 19 | // fn process_creditnote(&mut self); 20 | // } 21 | // 22 | // trait ProcessDebitNote { 23 | // fn process_debitnote(&mut self); 24 | // } 25 | 26 | impl Process for Invoice { 27 | fn process(&mut self) { 28 | self.process_common(); 29 | } 30 | } 31 | 32 | impl Process for CreditNote { 33 | fn process(&mut self) { 34 | self.process_common(); 35 | } 36 | } 37 | 38 | impl Process for DebitNote { 39 | fn process(&mut self) { 40 | self.process_common(); 41 | } 42 | } 43 | 44 | impl ProcessCommon for T 45 | where 46 | T: DetallesProcessRule, 47 | { 48 | fn process_common(&mut self) { 49 | let mut changed = true; 50 | 51 | while changed { 52 | let results = [DetallesProcessRule::process(self).unwrap_or_default()]; 53 | 54 | changed = results.contains(&true); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/rules/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod phase1fill; 2 | pub mod phase2process; 3 | pub mod phase3summary; 4 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/rules/phase1fill/detalle/icb_tasa.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | use crate::enricher::bounds::detalle::icb_tasa::{DetalleIcbTasaGetter, DetalleIcbTasaSetter}; 4 | use crate::enricher::rules::phase1fill::detalle::detalles::DetalleDefaults; 5 | 6 | pub trait DetalleICBTasaFillRule { 7 | fn fill(&mut self, defaults: &DetalleDefaults) -> Result; 8 | } 9 | 10 | impl DetalleICBTasaFillRule for T 11 | where 12 | T: DetalleIcbTasaGetter + DetalleIcbTasaSetter, 13 | { 14 | fn fill(&mut self, defaults: &DetalleDefaults) -> Result { 15 | match &self.get_icb_tasa() { 16 | Some(..) => Ok(false), 17 | None => { 18 | self.set_icb_tasa(defaults.icb_tasa); 19 | Ok(true) 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/rules/phase1fill/detalle/igv_tasa.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use rust_decimal::Decimal; 3 | 4 | use crate::catalogs::{Catalog7, Catalog7Group, FromCode}; 5 | use crate::enricher::bounds::detalle::igv_tasa::{DetalleIgvTasaGetter, DetalleIgvTasaSetter}; 6 | use crate::enricher::bounds::detalle::igv_tipo::DetalleIgvTipoGetter; 7 | use crate::enricher::rules::phase1fill::detalle::detalles::DetalleDefaults; 8 | 9 | pub trait DetalleIGVTasaFillRule { 10 | fn fill(&mut self, defaults: &DetalleDefaults) -> Result; 11 | } 12 | 13 | impl DetalleIGVTasaFillRule for T 14 | where 15 | T: DetalleIgvTasaGetter + DetalleIgvTasaSetter + DetalleIgvTipoGetter, 16 | { 17 | fn fill(&mut self, defaults: &DetalleDefaults) -> Result { 18 | match (self.get_igv_tasa(), *self.get_igv_tipo()) { 19 | (None, Some(igv_tipo)) => { 20 | if let Ok(catalog) = Catalog7::from_code(igv_tipo) { 21 | let tasa = match catalog { 22 | Catalog7::GravadoIvap => defaults.ivap_tasa, 23 | _ => match catalog.group() { 24 | Catalog7Group::Gravado | Catalog7Group::Gratuita => defaults.igv_tasa, 25 | Catalog7Group::Exonerado 26 | | Catalog7Group::Inafecto 27 | | Catalog7Group::Exportacion => Decimal::ZERO, 28 | }, 29 | }; 30 | self.set_igv_tasa(tasa); 31 | Ok(true) 32 | } else { 33 | Ok(false) 34 | } 35 | } 36 | _ => Ok(false), 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/rules/phase1fill/detalle/igv_tipo.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | use crate::catalogs::{Catalog, Catalog7}; 4 | use crate::enricher::bounds::detalle::igv_tipo::{DetalleIgvTipoGetter, DetalleIgvTipoSetter}; 5 | use crate::enricher::rules::phase1fill::detalle::detalles::DetalleDefaults; 6 | 7 | pub trait DetalleIGVTipoFillRule { 8 | fn fill(&mut self, defaults: &DetalleDefaults) -> Result; 9 | } 10 | 11 | impl DetalleIGVTipoFillRule for T 12 | where 13 | T: DetalleIgvTipoGetter + DetalleIgvTipoSetter, 14 | { 15 | fn fill(&mut self, _: &DetalleDefaults) -> Result { 16 | match &self.get_igv_tipo() { 17 | Some(..) => Ok(false), 18 | None => { 19 | self.set_igv_tipo(Catalog7::GravadoOperacionOnerosa.code()); 20 | Ok(true) 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/rules/phase1fill/detalle/isc_tipo.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | use crate::catalogs::{Catalog, Catalog8}; 4 | use crate::enricher::bounds::detalle::isc_tipo::{DetalleIscTipoGetter, DetalleIscTipoSetter}; 5 | use crate::enricher::rules::phase1fill::detalle::detalles::DetalleDefaults; 6 | 7 | pub trait DetalleISCTipoFillRule { 8 | fn fill(&mut self, defaults: &DetalleDefaults) -> Result; 9 | } 10 | 11 | impl DetalleISCTipoFillRule for T 12 | where 13 | T: DetalleIscTipoGetter + DetalleIscTipoSetter, 14 | { 15 | fn fill(&mut self, _: &DetalleDefaults) -> Result { 16 | match &self.get_isc_tipo() { 17 | Some(..) => Ok(false), 18 | None => { 19 | self.set_isc_tipo(Catalog8::SistemaAlValor.code()); 20 | Ok(true) 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/rules/phase1fill/detalle/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod detalles; 2 | pub mod icb_tasa; 3 | pub mod igv_tasa; 4 | pub mod igv_tipo; 5 | pub mod isc_tasa; 6 | pub mod isc_tipo; 7 | pub mod precio_referencia_tipo; 8 | pub mod unidad_medida; 9 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/rules/phase1fill/detalle/precio_referencia_tipo.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | use crate::catalogs::{Catalog, Catalog16, Catalog7, FromCode}; 4 | use crate::enricher::bounds::detalle::igv_tipo::DetalleIgvTipoGetter; 5 | use crate::enricher::bounds::detalle::precio_referencia_tipo::{ 6 | DetallePrecioReferenciaTipoGetter, DetallePrecioReferenciaTipoSetter, 7 | }; 8 | use crate::enricher::rules::phase1fill::detalle::detalles::DetalleDefaults; 9 | 10 | pub trait DetallePrecioReferenciaTipoFillRule { 11 | fn fill(&mut self, defaults: &DetalleDefaults) -> Result; 12 | } 13 | 14 | impl DetallePrecioReferenciaTipoFillRule for T 15 | where 16 | T: DetallePrecioReferenciaTipoGetter + DetallePrecioReferenciaTipoSetter + DetalleIgvTipoGetter, 17 | { 18 | fn fill(&mut self, _: &DetalleDefaults) -> Result { 19 | match (self.get_precio_referencia_tipo(), *self.get_igv_tipo()) { 20 | (None, Some(igv_tipo)) => { 21 | if let Ok(catalog) = Catalog7::from_code(igv_tipo) { 22 | let catalog16 = if catalog.onerosa() { 23 | &Catalog16::PrecioUnitarioIncluyeIgv 24 | } else { 25 | &Catalog16::ValorReferencialUnitarioEnOperacionesNoOnerosas 26 | }; 27 | self.set_precio_referencia_tipo(catalog16.code()); 28 | Ok(true) 29 | } else { 30 | Ok(false) 31 | } 32 | } 33 | _ => Ok(false), 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/rules/phase1fill/detalle/unidad_medida.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | use crate::enricher::bounds::detalle::unidad_medida::{ 4 | DetalleUnidadMedidaGetter, DetalleUnidadMedidaSetter, 5 | }; 6 | use crate::enricher::rules::phase1fill::detalle::detalles::DetalleDefaults; 7 | 8 | pub trait DetalleUnidadMedidaFillRule { 9 | fn fill(&mut self, defaults: &DetalleDefaults) -> Result; 10 | } 11 | 12 | impl DetalleUnidadMedidaFillRule for T 13 | where 14 | T: DetalleUnidadMedidaGetter + DetalleUnidadMedidaSetter, 15 | { 16 | fn fill(&mut self, _: &DetalleDefaults) -> Result { 17 | match &self.get_unidad_medida() { 18 | Some(..) => Ok(false), 19 | None => { 20 | self.set_unidad_medida("NIU"); 21 | Ok(true) 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/rules/phase1fill/fecha_emision.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | use crate::enricher::bounds::fecha_emision::{FechaEmisionGetter, FechaEmisionSetter}; 4 | use crate::enricher::Defaults; 5 | 6 | pub trait FechaEmisionFillRule { 7 | fn fill(&mut self, defaults: &Defaults) -> Result; 8 | } 9 | 10 | impl FechaEmisionFillRule for T 11 | where 12 | T: FechaEmisionGetter + FechaEmisionSetter, 13 | { 14 | fn fill(&mut self, defaults: &Defaults) -> Result { 15 | match &self.get_fecha_emision() { 16 | Some(..) => Ok(false), 17 | None => { 18 | self.set_fecha_emision(defaults.date); 19 | Ok(true) 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/rules/phase1fill/firmante.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | use crate::enricher::bounds::firmante::{FirmanteGetter, FirmanteSetter}; 4 | use crate::enricher::bounds::proveedor::ProveedorGetter; 5 | use crate::models::common::Firmante; 6 | 7 | pub trait FirmanteFillRule { 8 | fn fill(&mut self) -> Result; 9 | } 10 | 11 | impl FirmanteFillRule for T 12 | where 13 | T: FirmanteGetter + FirmanteSetter + ProveedorGetter, 14 | { 15 | fn fill(&mut self) -> Result { 16 | match &self.get_firmante() { 17 | Some(..) => Ok(false), 18 | None => { 19 | let firmante = Firmante { 20 | ruc: self.get_proveedor().ruc, 21 | razon_social: self.get_proveedor().razon_social, 22 | }; 23 | self.set_firmante(firmante); 24 | Ok(true) 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/rules/phase1fill/icb_tasa.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | use crate::enricher::bounds::icb::{IcbTasaGetter, IcbTasaSetter}; 4 | use crate::enricher::Defaults; 5 | 6 | pub trait IcbTasaFillRule { 7 | fn fill(&mut self, defaults: &Defaults) -> Result; 8 | } 9 | 10 | impl IcbTasaFillRule for T 11 | where 12 | T: IcbTasaGetter + IcbTasaSetter, 13 | { 14 | fn fill(&mut self, defaults: &Defaults) -> Result { 15 | match &self.get_icb_tasa() { 16 | Some(..) => Ok(false), 17 | None => { 18 | self.set_icb_tasa(defaults.icb_tasa); 19 | Ok(true) 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/rules/phase1fill/igv_tasa.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | use crate::enricher::bounds::igv::{IgvTasaGetter, IgvTasaSetter}; 4 | use crate::enricher::Defaults; 5 | 6 | pub trait IgvTasaFillRule { 7 | fn fill(&mut self, defaults: &Defaults) -> Result; 8 | } 9 | 10 | impl IgvTasaFillRule for T 11 | where 12 | T: IgvTasaGetter + IgvTasaSetter, 13 | { 14 | fn fill(&mut self, defaults: &Defaults) -> Result { 15 | match &self.get_igv_tasa() { 16 | Some(..) => Ok(false), 17 | None => { 18 | self.set_igv_tasa(defaults.igv_tasa); 19 | Ok(true) 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/rules/phase1fill/invoice/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod anticipos; 2 | pub mod descuentos; 3 | pub mod forma_de_pago; 4 | pub mod leyenda; 5 | pub mod tipo_comprobante; 6 | pub mod tipo_operacion; 7 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/rules/phase1fill/invoice/tipo_comprobante.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use regex::Regex; 3 | 4 | use crate::catalogs::{Catalog, Catalog1}; 5 | use crate::enricher::bounds::invoice::tipo_comprobante::{ 6 | InvoiceTipoComprobanteGetter, InvoiceTipoComprobanteSetter, 7 | }; 8 | use crate::enricher::bounds::serie_numero::SerieNumeroGetter; 9 | use crate::{BOLETA_SERIE_REGEX, FACTURA_SERIE_REGEX}; 10 | 11 | pub trait InvoiceTipoComprobanteFillRule { 12 | fn fill(&mut self) -> Result; 13 | } 14 | 15 | impl InvoiceTipoComprobanteFillRule for T 16 | where 17 | T: InvoiceTipoComprobanteGetter + InvoiceTipoComprobanteSetter + SerieNumeroGetter, 18 | { 19 | fn fill(&mut self) -> Result { 20 | match &self.get_tipo_comprobante() { 21 | Some(..) => Ok(false), 22 | None => { 23 | if Regex::new(FACTURA_SERIE_REGEX)?.is_match(self.get_serie_numero()) { 24 | self.set_tipo_comprobante(Catalog1::Factura.code()); 25 | 26 | return Ok(true); 27 | } else if Regex::new(BOLETA_SERIE_REGEX)?.is_match(self.get_serie_numero()) { 28 | self.set_tipo_comprobante(Catalog1::Boleta.code()); 29 | 30 | return Ok(true); 31 | } 32 | 33 | Ok(false) 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/rules/phase1fill/invoice/tipo_operacion.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | use crate::catalogs::{Catalog, Catalog51}; 4 | use crate::enricher::bounds::invoice::detraccion::InvoiceDetraccionGetter; 5 | use crate::enricher::bounds::invoice::percepcion::InvoicePercepcionGetter; 6 | use crate::enricher::bounds::invoice::tipo_operacion::{ 7 | InvoiceTipoOperacionGetter, InvoiceTipoOperacionSetter, 8 | }; 9 | 10 | pub trait InvoiceTipoOperacionFillRule { 11 | fn fill(&mut self) -> Result; 12 | } 13 | 14 | impl InvoiceTipoOperacionFillRule for T 15 | where 16 | T: InvoiceTipoOperacionGetter 17 | + InvoiceTipoOperacionSetter 18 | + InvoiceDetraccionGetter 19 | + InvoicePercepcionGetter, 20 | { 21 | fn fill(&mut self) -> Result { 22 | match &self.get_tipo_operacion() { 23 | Some(..) => Ok(false), 24 | None => { 25 | if self.get_detraccion().is_some() { 26 | self.set_tipo_operacion(Catalog51::OperacionSujetaADetraccion.code()); 27 | } else if self.get_percepcion().is_some() { 28 | self.set_tipo_operacion(Catalog51::OperacionSujetaAPercepcion.code()); 29 | } else { 30 | self.set_tipo_operacion(Catalog51::VentaInterna.code()); 31 | } 32 | 33 | Ok(false) 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/rules/phase1fill/ivap_tasa.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use Ok; 3 | 4 | use crate::enricher::bounds::ivap::{IvapTasaGetter, IvapTasaSetter}; 5 | use crate::enricher::Defaults; 6 | 7 | pub trait IvapTasaFillRule { 8 | fn fill(&mut self, defaults: &Defaults) -> Result; 9 | } 10 | 11 | impl IvapTasaFillRule for T 12 | where 13 | T: IvapTasaGetter + IvapTasaSetter, 14 | { 15 | fn fill(&mut self, defaults: &Defaults) -> Result { 16 | match &self.get_ivap_tasa() { 17 | Some(..) => Ok(false), 18 | None => { 19 | self.set_ivap_tasa(defaults.ivap_tasa); 20 | Ok(true) 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/rules/phase1fill/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod detalle; 2 | pub mod fecha_emision; 3 | pub mod firmante; 4 | pub mod icb_tasa; 5 | pub mod igv_tasa; 6 | pub mod invoice; 7 | pub mod ivap_tasa; 8 | pub mod moneda; 9 | pub mod note; 10 | pub mod proveedor; 11 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/rules/phase1fill/moneda.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | use crate::enricher::bounds::moneda::{MonedaGetter, MonedaSetter}; 4 | 5 | pub trait MonedaFillRule { 6 | fn fill(&mut self) -> Result; 7 | } 8 | 9 | impl MonedaFillRule for T 10 | where 11 | T: MonedaGetter + MonedaSetter, 12 | { 13 | fn fill(&mut self) -> Result { 14 | match &self.get_moneda() { 15 | Some(..) => Ok(false), 16 | None => { 17 | self.set_moneda("PEN"); 18 | Ok(true) 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/rules/phase1fill/note/creditnote/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod tipo_nota; 2 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/rules/phase1fill/note/creditnote/tipo_nota.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | use crate::catalogs::{Catalog, Catalog9}; 4 | use crate::enricher::bounds::note::creditnote::tipo_nota::{ 5 | CreditNoteTipoGetter, CreditNoteTipoSetter, 6 | }; 7 | 8 | pub trait CreditNoteTipoFillRule { 9 | fn fill(&mut self) -> Result; 10 | } 11 | 12 | impl CreditNoteTipoFillRule for T 13 | where 14 | T: CreditNoteTipoGetter + CreditNoteTipoSetter, 15 | { 16 | fn fill(&mut self) -> Result { 17 | match &self.get_tipo_nota() { 18 | Some(..) => Ok(false), 19 | None => { 20 | self.set_tipo_nota(Catalog9::AnulacionDeLaOperacion.code()); 21 | Ok(true) 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/rules/phase1fill/note/debitnote/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod tipo_nota; 2 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/rules/phase1fill/note/debitnote/tipo_nota.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | use crate::catalogs::{Catalog, Catalog10}; 4 | use crate::enricher::bounds::note::debitnote::tipo_nota::{ 5 | DebitNoteTipoGetter, DebitNoteTipoSetter, 6 | }; 7 | 8 | pub trait DebitNoteTipoFillRule { 9 | fn fill(&mut self) -> Result; 10 | } 11 | 12 | impl DebitNoteTipoFillRule for T 13 | where 14 | T: DebitNoteTipoGetter + DebitNoteTipoSetter, 15 | { 16 | fn fill(&mut self) -> Result { 17 | match &self.get_tipo_nota() { 18 | Some(..) => Ok(false), 19 | None => { 20 | self.set_tipo_nota(Catalog10::AumentoEnElValor.code()); 21 | Ok(true) 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/rules/phase1fill/note/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod creditnote; 2 | pub mod debitnote; 3 | pub mod tipo_comprobante_afectado; 4 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/rules/phase1fill/note/tipo_comprobante_afectado.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use log::warn; 3 | use regex::Regex; 4 | 5 | use crate::catalogs::{Catalog, Catalog1}; 6 | use crate::enricher::bounds::note::tipo_comprobante_afectado::{ 7 | NoteTipoComprobanteAfectadoGetter, NoteTipoComprobanteAfectadoSetter, 8 | }; 9 | use crate::enricher::bounds::serie_numero::SerieNumeroGetter; 10 | use crate::{BOLETA_SERIE_REGEX, FACTURA_SERIE_REGEX}; 11 | 12 | pub trait NoteComprobanteAfectadoTipoFillRule { 13 | fn fill(&mut self) -> Result; 14 | } 15 | 16 | impl NoteComprobanteAfectadoTipoFillRule for T 17 | where 18 | T: NoteTipoComprobanteAfectadoGetter + NoteTipoComprobanteAfectadoSetter + SerieNumeroGetter, 19 | { 20 | fn fill(&mut self) -> Result { 21 | match &self.get_comprobante_afectado_tipo() { 22 | Some(..) => Ok(false), 23 | None => { 24 | if Regex::new(FACTURA_SERIE_REGEX)?.is_match(self.get_serie_numero()) { 25 | self.set_comprobante_afectado_tipo(Catalog1::Factura.code()); 26 | Ok(true) 27 | } else if Regex::new(BOLETA_SERIE_REGEX)?.is_match(self.get_serie_numero()) { 28 | self.set_comprobante_afectado_tipo(Catalog1::Boleta.code()); 29 | Ok(true) 30 | } else { 31 | warn!( 32 | "tipo_comprobante_afectado no pudo ser inferido a partir de serie-numero" 33 | ); 34 | Ok(false) 35 | } 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/rules/phase1fill/proveedor.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | use crate::enricher::bounds::proveedor::{ProveedorGetter, ProveedorSetter}; 4 | use crate::models::common::Direccion; 5 | 6 | pub trait ProveedorFillRule { 7 | fn fill(&mut self) -> Result; 8 | } 9 | 10 | impl ProveedorFillRule for T 11 | where 12 | T: ProveedorGetter + ProveedorSetter, 13 | { 14 | fn fill(&mut self) -> Result { 15 | match &self.get_proveedor().direccion { 16 | Some(direccion) => match direccion.codigo_local { 17 | None => { 18 | self.set_proveedor_direccion(Direccion { 19 | codigo_local: Some("0000"), 20 | ..*direccion 21 | }); 22 | Ok(true) 23 | } 24 | Some(_) => Ok(false), 25 | }, 26 | None => { 27 | self.set_proveedor_direccion(Direccion { 28 | codigo_pais: None, 29 | departamento: None, 30 | provincia: None, 31 | distrito: None, 32 | direccion: None, 33 | urbanizacion: None, 34 | ubigeo: None, 35 | codigo_local: Some("0000"), 36 | }); 37 | Ok(true) 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/rules/phase2process/detalle/icb.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use rust_decimal::Decimal; 3 | 4 | use crate::enricher::bounds::detalle::cantidad::DetalleCantidadGetter; 5 | use crate::enricher::bounds::detalle::icb::{DetalleIcbGetter, DetalleIcbSetter}; 6 | use crate::enricher::bounds::detalle::icb_aplica::DetalleICBAplicaGetter; 7 | use crate::enricher::bounds::detalle::icb_tasa::DetalleIcbTasaGetter; 8 | 9 | pub trait DetalleICBProcessRule { 10 | fn process(&mut self) -> Result; 11 | } 12 | 13 | impl DetalleICBProcessRule for T 14 | where 15 | T: DetalleIcbGetter 16 | + DetalleIcbSetter 17 | + DetalleICBAplicaGetter 18 | + DetalleCantidadGetter 19 | + DetalleIcbTasaGetter, 20 | { 21 | fn process(&mut self) -> Result { 22 | match &self.get_icb() { 23 | Some(..) => Ok(false), 24 | None => { 25 | if self.get_icb_aplica() { 26 | if let Some(icb_tasa) = self.get_icb_tasa() { 27 | let icb = self.get_cantidad() * *icb_tasa; 28 | self.set_icb(icb); 29 | Ok(true) 30 | } else { 31 | Ok(false) 32 | } 33 | } else { 34 | self.set_icb(Decimal::ZERO); 35 | Ok(true) 36 | } 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/rules/phase2process/detalle/icb_aplica.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use rust_decimal::Decimal; 3 | 4 | use crate::enricher::bounds::detalle::icb::DetalleIcbGetter; 5 | use crate::enricher::bounds::detalle::icb_aplica::{ 6 | DetalleICBAplicaGetter, DetalleIcbAplicaSetter, 7 | }; 8 | 9 | pub trait DetalleICBAplicaProcessRule { 10 | fn process(&mut self) -> Result; 11 | } 12 | 13 | impl DetalleICBAplicaProcessRule for T 14 | where 15 | T: DetalleICBAplicaGetter + DetalleIcbAplicaSetter + DetalleIcbGetter, 16 | { 17 | fn process(&mut self) -> Result { 18 | match (&self.get_icb_aplica(), &self.get_icb()) { 19 | (false, Some(icb)) => { 20 | if icb > &Decimal::ZERO { 21 | self.set_icb_aplica(true); 22 | Ok(true) 23 | } else { 24 | Ok(false) 25 | } 26 | } 27 | _ => Ok(false), 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/rules/phase2process/detalle/igv.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | use crate::enricher::bounds::detalle::igv::{DetalleIgvGetter, DetalleIgvSetter}; 4 | use crate::enricher::bounds::detalle::igv_base_imponible::DetalleIgvBaseImponibleGetter; 5 | use crate::enricher::bounds::detalle::igv_tasa::DetalleIgvTasaGetter; 6 | 7 | pub trait DetalleIGVProcessRule { 8 | fn process(&mut self) -> Result; 9 | } 10 | 11 | impl DetalleIGVProcessRule for T 12 | where 13 | T: DetalleIgvGetter + DetalleIgvSetter + DetalleIgvBaseImponibleGetter + DetalleIgvTasaGetter, 14 | { 15 | fn process(&mut self) -> Result { 16 | match ( 17 | &self.get_igv(), 18 | &self.get_igv_base_imponible(), 19 | &self.get_igv_tasa(), 20 | ) { 21 | (None, Some(igv_base_imponible), Some(igv_tasa)) => { 22 | let igv = igv_base_imponible * *igv_tasa; 23 | self.set_igv(igv); 24 | Ok(true) 25 | } 26 | _ => Ok(false), 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/rules/phase2process/detalle/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod detalles; 2 | pub mod icb; 3 | pub mod icb_aplica; 4 | pub mod igv; 5 | pub mod igv_base_imponible; 6 | pub mod isc; 7 | pub mod isc_base_imponible; 8 | pub mod precio; 9 | pub mod precio_con_impuestos; 10 | pub mod precio_referencia; 11 | pub mod total_impuestos; 12 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/rules/phase2process/detalle/precio_referencia.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | use crate::catalogs::{Catalog7, FromCode}; 4 | use crate::enricher::bounds::detalle::igv_tipo::DetalleIgvTipoGetter; 5 | use crate::enricher::bounds::detalle::precio::DetallePrecioGetter; 6 | use crate::enricher::bounds::detalle::precio_con_impuestos::DetallePrecioConImpuestosGetter; 7 | use crate::enricher::bounds::detalle::precio_referencia::{ 8 | DetallePrecioReferenciaGetter, DetallePrecioReferenciaSetter, 9 | }; 10 | 11 | pub trait DetallePrecioReferenciaProcessRule { 12 | fn process(&mut self) -> Result; 13 | } 14 | 15 | impl DetallePrecioReferenciaProcessRule for T 16 | where 17 | T: DetallePrecioReferenciaGetter 18 | + DetallePrecioReferenciaSetter 19 | + DetallePrecioGetter 20 | + DetallePrecioConImpuestosGetter 21 | + DetalleIgvTipoGetter, 22 | { 23 | fn process(&mut self) -> Result { 24 | match ( 25 | &self.get_precio_referencia(), 26 | &self.get_igv_tipo(), 27 | &self.get_precio(), 28 | &self.get_precio_con_impuestos(), 29 | ) { 30 | (None, Some(igv_tipo), Some(precio), Some(precio_con_impuestos)) => { 31 | if let Ok(catalog) = Catalog7::from_code(igv_tipo) { 32 | let precio_referencia = if catalog.onerosa() { 33 | precio_con_impuestos 34 | } else { 35 | precio 36 | }; 37 | self.set_precio_referencia(*precio_referencia); 38 | Ok(true) 39 | } else { 40 | Ok(false) 41 | } 42 | } 43 | _ => Ok(false), 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/rules/phase2process/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod detalle; 2 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/rules/phase3summary/invoice/detraccion.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | use crate::enricher::bounds::invoice::detraccion::{ 4 | InvoiceDetraccionGetter, InvoiceDetraccionMontoGetter, InvoiceDetraccionMontoSetter, 5 | InvoiceDetraccionPorcentajeGetter, 6 | }; 7 | use crate::enricher::bounds::invoice::total_importe::InvoiceTotalImporteGetter; 8 | use crate::models::common::TotalImporteInvoice; 9 | 10 | pub trait DetraccionSummaryRule { 11 | fn summary(&mut self) -> Result; 12 | } 13 | 14 | impl DetraccionSummaryRule for T 15 | where 16 | T: InvoiceDetraccionGetter + InvoiceTotalImporteGetter, 17 | { 18 | fn summary(&mut self) -> Result { 19 | match (self.get_total_importe().clone(), self.get_detraccion()) { 20 | (Some(total_importe), Some(detraccion)) => { 21 | let results = [ 22 | DetraccionMontoRule::summary(detraccion, &total_importe).unwrap_or_default() 23 | ]; 24 | Ok(results.contains(&true)) 25 | } 26 | _ => Ok(false), 27 | } 28 | } 29 | } 30 | 31 | // 32 | 33 | pub trait DetraccionMontoRule { 34 | fn summary(&mut self, total_importe: &TotalImporteInvoice) -> Result; 35 | } 36 | 37 | impl DetraccionMontoRule for T 38 | where 39 | T: InvoiceDetraccionMontoGetter 40 | + InvoiceDetraccionMontoSetter 41 | + InvoiceDetraccionPorcentajeGetter, 42 | { 43 | fn summary(&mut self, total_importe: &TotalImporteInvoice) -> Result { 44 | match &self.get_monto() { 45 | Some(_) => Ok(false), 46 | None => { 47 | let monto = total_importe.importe_con_impuestos * self.get_porcentaje(); 48 | self.set_monto(monto); 49 | Ok(true) 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/rules/phase3summary/invoice/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod detraccion; 2 | pub mod percepcion; 3 | pub mod total_importe; 4 | pub mod total_impuestos; 5 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/rules/phase3summary/leyenda.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | use crate::catalogs::{Catalog, Catalog7}; 4 | use crate::enricher::bounds::detalle::DetallesGetter; 5 | use crate::enricher::bounds::leyendas::{LeyendasGetter, LeyendasSetter}; 6 | 7 | pub trait LeyendaIVAPSummaryRule { 8 | fn summary(&mut self) -> Result; 9 | } 10 | 11 | fn insert_leyenda(obj: &mut T, code: &'static str, label: &'static str) -> bool 12 | where 13 | T: LeyendasGetter + LeyendasSetter, 14 | { 15 | if !obj.get_leyendas().contains_key(code) { 16 | obj.insert_leyendas(code, label); 17 | true 18 | } else { 19 | false 20 | } 21 | } 22 | 23 | impl LeyendaIVAPSummaryRule for T 24 | where 25 | T: DetallesGetter + LeyendasGetter + LeyendasSetter, 26 | { 27 | fn summary(&mut self) -> Result { 28 | if self 29 | .get_detalles() 30 | .iter() 31 | .any(|e| e.igv_tipo.unwrap_or("") == Catalog7::GravadoIvap.code()) 32 | { 33 | Ok(insert_leyenda( 34 | self, 35 | "2007", 36 | "Leyenda: Operacion sujeta a IVAP", 37 | )) 38 | } else { 39 | Ok(false) 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/rules/phase3summary/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod invoice; 2 | pub mod leyenda; 3 | pub mod note; 4 | pub mod utils; 5 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/rules/phase3summary/note/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod total_importe; 2 | pub mod total_impuestos; 3 | -------------------------------------------------------------------------------- /xbuilder/src/enricher/rules/phase3summary/note/total_importe.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | use crate::enricher::bounds::detalle::DetallesGetter; 4 | use crate::enricher::bounds::note::total_importe::{ 5 | NoteTotalImporteGetter, NoteTotalImporteSetter, 6 | }; 7 | use crate::enricher::rules::phase3summary::utils::{importe_sin_impuestos, total_impuestos}; 8 | use crate::models::common::TotalImporteNote; 9 | 10 | pub trait NoteTotalImporteSummaryRule { 11 | fn summary(&mut self) -> Result; 12 | } 13 | 14 | impl NoteTotalImporteSummaryRule for T 15 | where 16 | T: NoteTotalImporteGetter + NoteTotalImporteSetter + DetallesGetter, 17 | { 18 | fn summary(&mut self) -> Result { 19 | match &self.get_total_importe() { 20 | Some(..) => Ok(false), 21 | None => { 22 | let total_impuestos = total_impuestos(self.get_detalles()); 23 | let importe_sin_impuestos = importe_sin_impuestos(self.get_detalles()); 24 | 25 | let importe = importe_sin_impuestos + total_impuestos; 26 | 27 | self.set_total_importe(TotalImporteNote { 28 | importe_sin_impuestos, 29 | importe, 30 | }); 31 | Ok(true) 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /xbuilder/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod catalogs; 2 | pub mod enricher; 3 | pub mod models; 4 | pub mod prelude; 5 | pub mod renderer; 6 | 7 | pub const FACTURA_SERIE_REGEX: &str = "^[F|f].*$"; 8 | pub const BOLETA_SERIE_REGEX: &str = "^[B|f].*$"; 9 | -------------------------------------------------------------------------------- /xbuilder/src/models/credit_note.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use chrono::NaiveDate; 4 | use rust_decimal::Decimal; 5 | use serde::Serialize; 6 | 7 | use crate::models::common::{ 8 | Cliente, Detalle, DocumentoRelacionado, Firmante, Guia, Proveedor, TotalImporteNote, 9 | TotalImpuestos, 10 | }; 11 | 12 | /// Nota de credito 13 | #[derive(Debug, Serialize, Default)] 14 | pub struct CreditNote { 15 | pub leyendas: HashMap<&'static str, &'static str>, 16 | 17 | pub serie_numero: &'static str, 18 | pub moneda: Option<&'static str>, 19 | pub fecha_emision: Option, 20 | pub proveedor: Proveedor, 21 | pub cliente: Cliente, 22 | pub firmante: Option, 23 | pub icb_tasa: Option, 24 | pub igv_tasa: Option, 25 | pub ivap_tasa: Option, 26 | 27 | /// Catalog9 28 | pub tipo_nota: Option<&'static str>, 29 | pub comprobante_afectado_serie_numero: &'static str, 30 | /// Catalog1 31 | pub comprobante_afectado_tipo: Option<&'static str>, 32 | pub sustento_descripcion: &'static str, 33 | 34 | pub detalles: Vec, 35 | 36 | pub total_importe: Option, 37 | pub total_impuestos: Option, 38 | 39 | pub guias: Vec, 40 | pub documentos_relacionados: Vec, 41 | 42 | pub orden_de_compra: Option<&'static str>, 43 | } 44 | -------------------------------------------------------------------------------- /xbuilder/src/models/debit_note.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use chrono::NaiveDate; 4 | use rust_decimal::Decimal; 5 | use serde::Serialize; 6 | 7 | use crate::models::common::{ 8 | Cliente, Detalle, DocumentoRelacionado, Firmante, Guia, Proveedor, TotalImporteNote, 9 | TotalImpuestos, 10 | }; 11 | 12 | /// Nota de debito 13 | #[derive(Debug, Serialize, Default)] 14 | pub struct DebitNote { 15 | pub leyendas: HashMap<&'static str, &'static str>, 16 | 17 | pub serie_numero: &'static str, 18 | pub moneda: Option<&'static str>, 19 | pub fecha_emision: Option, 20 | pub proveedor: Proveedor, 21 | pub cliente: Cliente, 22 | pub firmante: Option, 23 | pub icb_tasa: Option, 24 | pub igv_tasa: Option, 25 | pub ivap_tasa: Option, 26 | 27 | // Catalog10 28 | pub tipo_nota: Option<&'static str>, 29 | pub comprobante_afectado_serie_numero: &'static str, 30 | // Catalog1 31 | pub comprobante_afectado_tipo: Option<&'static str>, 32 | pub sustento_descripcion: &'static str, 33 | 34 | pub detalles: Vec, 35 | 36 | pub total_importe: Option, 37 | pub total_impuestos: Option, 38 | 39 | pub guias: Vec, 40 | pub documentos_relacionados: Vec, 41 | 42 | pub orden_de_compra: Option<&'static str>, 43 | } 44 | -------------------------------------------------------------------------------- /xbuilder/src/models/invoice.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use chrono::{NaiveDate, NaiveTime}; 4 | use rust_decimal::Decimal; 5 | use serde::Serialize; 6 | 7 | use crate::models::common::{ 8 | Anticipo, Cliente, Descuento, Detalle, Detraccion, Direccion, DocumentoRelacionado, Firmante, 9 | FormaDePago, Guia, Percepcion, Proveedor, TotalImporteInvoice, TotalImpuestos, 10 | }; 11 | 12 | /// Boleta o Factura 13 | #[derive(Debug, Serialize, Default)] 14 | pub struct Invoice { 15 | pub leyendas: HashMap<&'static str, &'static str>, 16 | 17 | pub serie_numero: &'static str, 18 | pub moneda: Option<&'static str>, 19 | pub fecha_emision: Option, 20 | pub hora_emision: Option, 21 | pub fecha_vencimiento: Option, 22 | pub proveedor: Proveedor, 23 | pub cliente: Cliente, 24 | pub firmante: Option, 25 | pub icb_tasa: Option, 26 | pub igv_tasa: Option, 27 | pub ivap_tasa: Option, 28 | 29 | /// Catalog1 30 | pub tipo_comprobante: Option<&'static str>, 31 | 32 | /// Catalog51 33 | pub tipo_operacion: Option<&'static str>, 34 | 35 | pub detraccion: Option, 36 | pub percepcion: Option, 37 | 38 | pub direccion_entrega: Option, 39 | pub forma_de_pago: Option, 40 | 41 | pub anticipos: Vec, 42 | pub descuentos: Vec, 43 | 44 | pub detalles: Vec, 45 | 46 | pub total_importe: Option, 47 | pub total_impuestos: Option, 48 | 49 | pub guias: Vec, 50 | pub documentos_relacionados: Vec, 51 | 52 | pub observaciones: Option<&'static str>, 53 | pub orden_de_compra: Option<&'static str>, 54 | } 55 | -------------------------------------------------------------------------------- /xbuilder/src/models/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod common; 2 | pub mod credit_note; 3 | pub mod debit_note; 4 | pub mod invoice; 5 | -------------------------------------------------------------------------------- /xbuilder/src/prelude.rs: -------------------------------------------------------------------------------- 1 | pub use crate::catalogs::*; 2 | pub use crate::enricher::{Defaults, Enrich}; 3 | pub use crate::models::common::*; 4 | pub use crate::models::credit_note::*; 5 | pub use crate::models::debit_note::*; 6 | pub use crate::models::invoice::*; 7 | pub use crate::renderer::Renderer; 8 | -------------------------------------------------------------------------------- /xbuilder/tests/credit_note.rs: -------------------------------------------------------------------------------- 1 | use rust_decimal_macros::dec; 2 | 3 | use xbuilder::models::common::Detalle; 4 | use xbuilder::prelude::*; 5 | 6 | use crate::common::{assert_credit_note, credit_note_base}; 7 | 8 | mod common; 9 | 10 | const BASE: &str = "tests/resources/e2e/renderer/creditnote/CreditNoteTest"; 11 | 12 | #[serial_test::serial] 13 | #[tokio::test] 14 | async fn credit_note() { 15 | let mut credit_note = CreditNote { 16 | detalles: vec![ 17 | Detalle { 18 | descripcion: "Item1", 19 | cantidad: dec!(10), 20 | precio: Some(dec!(100)), 21 | ..Default::default() 22 | }, 23 | Detalle { 24 | descripcion: "Item2", 25 | cantidad: dec!(10), 26 | precio: Some(dec!(100)), 27 | ..Default::default() 28 | }, 29 | ], 30 | ..credit_note_base() 31 | }; 32 | 33 | assert_credit_note(&mut credit_note, &format!("{BASE}/MinData_RUC.xml")).await; 34 | } 35 | -------------------------------------------------------------------------------- /xbuilder/tests/credit_note_orden_de_compra.rs: -------------------------------------------------------------------------------- 1 | use rust_decimal_macros::dec; 2 | 3 | use xbuilder::models::common::Detalle; 4 | use xbuilder::prelude::*; 5 | 6 | use crate::common::{assert_credit_note, credit_note_base}; 7 | 8 | mod common; 9 | 10 | const BASE: &str = "tests/resources/e2e/renderer/creditnote/CreditNoteOrdenDeCompraTest"; 11 | 12 | #[serial_test::serial] 13 | #[tokio::test] 14 | async fn credit_note() { 15 | let mut credit_note = CreditNote { 16 | orden_de_compra: Some("123456"), 17 | detalles: vec![ 18 | Detalle { 19 | descripcion: "Item1", 20 | cantidad: dec!(10), 21 | precio: Some(dec!(100)), 22 | ..Default::default() 23 | }, 24 | Detalle { 25 | descripcion: "Item2", 26 | cantidad: dec!(10), 27 | precio: Some(dec!(100)), 28 | ..Default::default() 29 | }, 30 | ], 31 | ..credit_note_base() 32 | }; 33 | 34 | assert_credit_note(&mut credit_note, &format!("{BASE}/ordenDeCompra.xml")).await; 35 | } 36 | -------------------------------------------------------------------------------- /xbuilder/tests/debit_note.rs: -------------------------------------------------------------------------------- 1 | use rust_decimal_macros::dec; 2 | 3 | use xbuilder::models::common::Detalle; 4 | use xbuilder::prelude::*; 5 | 6 | use crate::common::{assert_debit_note, debit_note_base}; 7 | 8 | mod common; 9 | 10 | const BASE: &str = "tests/resources/e2e/renderer/debitnote/DebitNoteTest"; 11 | 12 | #[serial_test::serial] 13 | #[tokio::test] 14 | async fn debit_note() { 15 | let mut debit_note = DebitNote { 16 | detalles: vec![ 17 | Detalle { 18 | descripcion: "Item1", 19 | cantidad: dec!(10), 20 | precio: Some(dec!(100)), 21 | ..Default::default() 22 | }, 23 | Detalle { 24 | descripcion: "Item2", 25 | cantidad: dec!(10), 26 | precio: Some(dec!(100)), 27 | ..Default::default() 28 | }, 29 | ], 30 | ..debit_note_base() 31 | }; 32 | 33 | assert_debit_note(&mut debit_note, &format!("{BASE}/MinData_RUC.xml")).await; 34 | } 35 | -------------------------------------------------------------------------------- /xbuilder/tests/invoice_anticipos.rs: -------------------------------------------------------------------------------- 1 | use rust_decimal_macros::dec; 2 | 3 | use xbuilder::prelude::*; 4 | 5 | use crate::common::assert_invoice; 6 | use crate::common::invoice_base; 7 | 8 | mod common; 9 | 10 | const BASE: &str = "tests/resources/e2e/renderer/invoice/InvoiceAnticiposTest"; 11 | 12 | #[serial_test::serial] 13 | #[tokio::test] 14 | async fn invoice_anticipos() { 15 | let mut invoice = Invoice { 16 | detalles: vec![ 17 | Detalle { 18 | descripcion: "Item1", 19 | cantidad: dec!(2), 20 | precio: Some(dec!(100)), 21 | ..Default::default() 22 | }, 23 | Detalle { 24 | descripcion: "Item2", 25 | cantidad: dec!(2), 26 | precio: Some(dec!(100)), 27 | ..Default::default() 28 | }, 29 | ], 30 | anticipos: vec![Anticipo { 31 | comprobante_serie_numero: "F001-2", 32 | monto: dec!(100), 33 | tipo: None, 34 | comprobante_tipo: None, 35 | }], 36 | ..invoice_base() 37 | }; 38 | 39 | assert_invoice(&mut invoice, &format!("{BASE}/minAnticipos.xml")).await; 40 | } 41 | -------------------------------------------------------------------------------- /xbuilder/tests/invoice_descuentos.rs: -------------------------------------------------------------------------------- 1 | use rust_decimal::Decimal; 2 | use rust_decimal_macros::dec; 3 | 4 | use xbuilder::prelude::*; 5 | 6 | use crate::common::assert_invoice; 7 | use crate::common::invoice_base; 8 | 9 | mod common; 10 | 11 | const BASE: &str = "tests/resources/e2e/renderer/invoice/InvoiceDescuentosTest"; 12 | 13 | #[serial_test::serial] 14 | #[tokio::test] 15 | async fn invoice_anticipos() { 16 | let mut invoice = Invoice { 17 | detalles: vec![Detalle { 18 | descripcion: "Item1", 19 | cantidad: Decimal::ONE, 20 | precio: Some(dec!(100)), 21 | ..Default::default() 22 | }], 23 | descuentos: vec![Descuento { 24 | monto: dec!(50), 25 | tipo: None, 26 | monto_base: None, 27 | factor: None, 28 | }], 29 | ..invoice_base() 30 | }; 31 | 32 | assert_invoice(&mut invoice, &format!("{BASE}/descuentoGlobal.xml")).await; 33 | } 34 | -------------------------------------------------------------------------------- /xbuilder/tests/invoice_detraccion.rs: -------------------------------------------------------------------------------- 1 | use rust_decimal_macros::dec; 2 | 3 | use xbuilder::prelude::*; 4 | 5 | use crate::common::assert_invoice; 6 | use crate::common::invoice_base; 7 | 8 | mod common; 9 | 10 | const BASE: &str = "tests/resources/e2e/renderer/invoice/InvoiceDetraccionTest"; 11 | 12 | #[serial_test::serial] 13 | #[tokio::test] 14 | async fn invoice_detraccion() { 15 | let mut invoice = Invoice { 16 | detalles: vec![Detalle { 17 | descripcion: "Item1", 18 | cantidad: dec!(4), 19 | precio: Some(dec!(200)), 20 | ..Default::default() 21 | }], 22 | detraccion: Some(Detraccion { 23 | medio_de_pago: Catalog59::DepositoEnCuenta.code(), 24 | cuenta_bancaria: "0004-3342343243", 25 | tipo_bien_detraido: "014", 26 | porcentaje: dec!(0.04), 27 | monto: None, 28 | }), 29 | ..invoice_base() 30 | }; 31 | 32 | assert_invoice(&mut invoice, &format!("{BASE}/detraccion.xml")).await; 33 | } 34 | -------------------------------------------------------------------------------- /xbuilder/tests/invoice_documento_relacionado.rs: -------------------------------------------------------------------------------- 1 | use rust_decimal_macros::dec; 2 | 3 | use xbuilder::prelude::*; 4 | 5 | use crate::common::assert_invoice; 6 | use crate::common::invoice_base; 7 | 8 | mod common; 9 | 10 | const BASE: &str = "tests/resources/e2e/renderer/invoice/InvoiceDocumentoRelacionadoTest"; 11 | 12 | #[serial_test::serial] 13 | #[tokio::test] 14 | async fn invoice_documento_relacionado_y_orden_de_compra() { 15 | let mut invoice = Invoice { 16 | detalles: vec![ 17 | Detalle { 18 | descripcion: "Item1", 19 | cantidad: dec!(2), 20 | precio: Some(dec!(100)), 21 | ..Default::default() 22 | }, 23 | Detalle { 24 | descripcion: "Item2", 25 | cantidad: dec!(2), 26 | precio: Some(dec!(100)), 27 | ..Default::default() 28 | }, 29 | ], 30 | documentos_relacionados: vec![DocumentoRelacionado { 31 | serie_numero: "B111-1", 32 | tipo_documento: Catalog12::DeclaracionSimplificadaDeImportacion.code(), 33 | }], 34 | orden_de_compra: Some("123456"), 35 | ..invoice_base() 36 | }; 37 | 38 | assert_invoice(&mut invoice, &format!("{BASE}/documentoRelacionado.xml")).await; 39 | } 40 | -------------------------------------------------------------------------------- /xbuilder/tests/invoice_fecha_vencimiento.rs: -------------------------------------------------------------------------------- 1 | use chrono::NaiveDate; 2 | use rust_decimal_macros::dec; 3 | 4 | use xbuilder::prelude::*; 5 | 6 | use crate::common::assert_invoice; 7 | use crate::common::invoice_base; 8 | 9 | mod common; 10 | 11 | const BASE: &str = "tests/resources/e2e/renderer/invoice/InvoiceFechaVencimientoTest"; 12 | 13 | #[serial_test::serial] 14 | #[tokio::test] 15 | async fn invoice_custom_moneda() { 16 | let mut invoice = Invoice { 17 | fecha_vencimiento: NaiveDate::from_ymd_opt(2022, 1, 1), 18 | detalles: vec![ 19 | Detalle { 20 | descripcion: "Item1", 21 | cantidad: dec!(10), 22 | precio: Some(dec!(100)), 23 | ..Default::default() 24 | }, 25 | Detalle { 26 | descripcion: "Item2", 27 | cantidad: dec!(10), 28 | precio: Some(dec!(100)), 29 | ..Default::default() 30 | }, 31 | ], 32 | ..invoice_base() 33 | }; 34 | 35 | assert_invoice(&mut invoice, &format!("{BASE}/conFechaVencimiento.xml")).await; 36 | } 37 | -------------------------------------------------------------------------------- /xbuilder/tests/invoice_guias.rs: -------------------------------------------------------------------------------- 1 | use rust_decimal_macros::dec; 2 | 3 | use xbuilder::prelude::*; 4 | 5 | use crate::common::assert_invoice; 6 | use crate::common::invoice_base; 7 | 8 | mod common; 9 | 10 | const BASE: &str = "tests/resources/e2e/renderer/invoice/InvoiceGuiasTest"; 11 | 12 | #[serial_test::serial] 13 | #[tokio::test] 14 | async fn invoice_guia_serie_t() { 15 | let mut invoice = Invoice { 16 | detalles: vec![ 17 | Detalle { 18 | descripcion: "Item1", 19 | cantidad: dec!(2), 20 | precio: Some(dec!(100)), 21 | ..Default::default() 22 | }, 23 | Detalle { 24 | descripcion: "Item2", 25 | cantidad: dec!(2), 26 | precio: Some(dec!(100)), 27 | ..Default::default() 28 | }, 29 | ], 30 | guias: vec![Guia { 31 | tipo_documento: Catalog1::GuiaRemisionRemitente.code(), 32 | serie_numero: "T001-1", 33 | }], 34 | ..invoice_base() 35 | }; 36 | 37 | assert_invoice(&mut invoice, &format!("{BASE}/guiaSerieT.xml")).await; 38 | } 39 | -------------------------------------------------------------------------------- /xbuilder/tests/invoice_igv_tipo.rs: -------------------------------------------------------------------------------- 1 | use rust_decimal::Decimal; 2 | use rust_decimal_macros::dec; 3 | 4 | use xbuilder::models::common::Detalle; 5 | use xbuilder::prelude::*; 6 | 7 | use crate::common::assert_invoice; 8 | use crate::common::invoice_base; 9 | 10 | mod common; 11 | 12 | const BASE: &str = "tests/resources/e2e/renderer/invoice/InvoiceTipoIgvTest"; 13 | 14 | static CATALOG7_VARIANTS: &[Catalog7] = &[ 15 | Catalog7::GravadoOperacionOnerosa, 16 | Catalog7::GravadoRetiroPorPremio, 17 | Catalog7::GravadoRetiroPorDonacion, 18 | Catalog7::GravadoRetiro, 19 | Catalog7::GravadoRetiroPorPublicidad, 20 | Catalog7::GravadoBonificaciones, 21 | Catalog7::GravadoRetiroPorEntregaATrabajadores, 22 | Catalog7::GravadoIvap, 23 | Catalog7::ExoneradoOperacionOnerosa, 24 | Catalog7::ExoneradoTransferenciaGratuita, 25 | Catalog7::InafectoOperacionOnerosa, 26 | Catalog7::InafectoRetiroPorBonificacion, 27 | Catalog7::InafectoRetiro, 28 | Catalog7::InafectoRetiroPorMuestrasMedicas, 29 | Catalog7::InafectoRetiroPorConvenioColectivo, 30 | Catalog7::InafectoRetiroPorPremio, 31 | Catalog7::InafectoRetiroPorPublicidad, 32 | Catalog7::Exportacion, 33 | ]; 34 | 35 | #[serial_test::serial] 36 | #[tokio::test] 37 | async fn invoice_precio_unitario() { 38 | for catalog7 in CATALOG7_VARIANTS { 39 | let mut invoice = Invoice { 40 | detalles: vec![Detalle { 41 | descripcion: "Item1", 42 | cantidad: Decimal::ONE, 43 | precio: Some(dec!(100)), 44 | igv_tipo: Some(catalog7.code()), 45 | ..Default::default() 46 | }], 47 | ..invoice_base() 48 | }; 49 | 50 | assert_invoice(&mut invoice, &format!("{BASE}/invoice_pu_{catalog7}.xml")).await; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /xbuilder/tests/invoice_issue30.rs: -------------------------------------------------------------------------------- 1 | use rust_decimal_macros::dec; 2 | 3 | use xbuilder::prelude::*; 4 | 5 | use crate::common::assert_invoice; 6 | use crate::common::{invoice_base, proveedor_base}; 7 | 8 | mod common; 9 | 10 | const BASE: &str = "tests/resources/e2e/renderer/invoice/InvoiceIssue30Test"; 11 | 12 | #[serial_test::serial] 13 | #[tokio::test] 14 | async fn invoice_issue30() { 15 | let mut invoice = Invoice { 16 | proveedor: Proveedor { 17 | ruc: "12345678912", 18 | razon_social: "Project OpenUBL S.A.C.", 19 | ..proveedor_base() 20 | }, 21 | detalles: vec![Detalle { 22 | descripcion: "Item1", 23 | cantidad: dec!(10), 24 | precio: Some(dec!(6.68)), 25 | ..Default::default() 26 | }], 27 | ..invoice_base() 28 | }; 29 | 30 | assert_invoice(&mut invoice, &format!("{BASE}/with-precioUnitario.xml")).await; 31 | } 32 | -------------------------------------------------------------------------------- /xbuilder/tests/invoice_moneda.rs: -------------------------------------------------------------------------------- 1 | use rust_decimal::Decimal; 2 | use rust_decimal_macros::dec; 3 | 4 | use xbuilder::prelude::*; 5 | 6 | use crate::common::assert_invoice; 7 | use crate::common::invoice_base; 8 | 9 | mod common; 10 | 11 | const BASE: &str = "tests/resources/e2e/renderer/invoice/InvoiceMonedaTest"; 12 | 13 | #[serial_test::serial] 14 | #[tokio::test] 15 | async fn invoice_custom_moneda() { 16 | let mut invoice = Invoice { 17 | moneda: Some("USD"), 18 | detalles: vec![Detalle { 19 | descripcion: "Item1", 20 | cantidad: Decimal::ONE, 21 | precio: Some(dec!(100)), 22 | ..Default::default() 23 | }], 24 | ..invoice_base() 25 | }; 26 | 27 | assert_invoice(&mut invoice, &format!("{BASE}/customMoneda.xml")).await; 28 | } 29 | -------------------------------------------------------------------------------- /xbuilder/tests/invoice_orden_compra.rs: -------------------------------------------------------------------------------- 1 | use rust_decimal_macros::dec; 2 | 3 | use xbuilder::prelude::*; 4 | 5 | use crate::common::assert_invoice; 6 | use crate::common::invoice_base; 7 | 8 | mod common; 9 | 10 | const BASE: &str = "tests/resources/e2e/renderer/invoice/InvoiceOrdeDeCompraTest"; 11 | 12 | #[serial_test::serial] 13 | #[tokio::test] 14 | async fn invoice_custom_moneda() { 15 | let mut invoice = Invoice { 16 | orden_de_compra: Some("123456"), 17 | detalles: vec![ 18 | Detalle { 19 | descripcion: "Item1", 20 | cantidad: dec!(2), 21 | precio: Some(dec!(100)), 22 | ..Default::default() 23 | }, 24 | Detalle { 25 | descripcion: "Item2", 26 | cantidad: dec!(2), 27 | precio: Some(dec!(100)), 28 | ..Default::default() 29 | }, 30 | ], 31 | ..invoice_base() 32 | }; 33 | 34 | assert_invoice(&mut invoice, &format!("{BASE}/ordenDeCompra.xml")).await; 35 | } 36 | -------------------------------------------------------------------------------- /xbuilder/tests/invoice_percepcion.rs: -------------------------------------------------------------------------------- 1 | use rust_decimal_macros::dec; 2 | 3 | use xbuilder::prelude::*; 4 | 5 | use crate::common::assert_invoice; 6 | use crate::common::invoice_base; 7 | 8 | mod common; 9 | 10 | const BASE: &str = "tests/resources/e2e/renderer/invoice/InvoicePercepcionTest"; 11 | 12 | #[serial_test::serial] 13 | #[tokio::test] 14 | async fn invoice_percepcion() { 15 | let mut invoice = Invoice { 16 | percepcion: Some(Percepcion { 17 | tipo: "51", 18 | porcentaje: Some(dec!(0.02)), 19 | monto: None, 20 | monto_base: None, 21 | monto_total: None, 22 | }), 23 | detalles: vec![Detalle { 24 | descripcion: "Item1", 25 | cantidad: dec!(4), 26 | precio: Some(dec!(200)), 27 | ..Default::default() 28 | }], 29 | ..invoice_base() 30 | }; 31 | 32 | assert_invoice(&mut invoice, &format!("{BASE}/percepcion.xml")).await; 33 | } 34 | -------------------------------------------------------------------------------- /xbuilder/tests/resources/certificates/LLAMA-PE-CERTIFICADO-DEMO-10467793549.pfx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-openubl/xhandler-rust/2c4a2947117d7d011f4423ec746153f892cead9e/xbuilder/tests/resources/certificates/LLAMA-PE-CERTIFICADO-DEMO-10467793549.pfx -------------------------------------------------------------------------------- /xbuilder/tests/resources/certificates/private.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEArhO3H48lGkRNcPNhA6uTd804NnMxBkXKKTgR8DldX8vTmrf0 3 | JqNGMLxUlqSG1KlRelHQXvIz7GWO0NgE0DZ9eMEULS7S8YMuj6RZFCudDb/aasxH 4 | yCvjVfdKJUF4BIPPKN2dvjFBAQz4fI/3/PceptIqzwzl+8SryXEbJgAUmjaS2POE 5 | 65RePRIINOV1Vi7lwvLzH0Zl1sr+oytOnXAI1YRlKZhgcS5v5XeX/qfRpbIQdqxc 6 | loVAQX/voN8QsLT6chZr/gEZbUnDs2HD286/Xzg27Rw8Bwy7HIbhhKYPK2TsFanp 7 | MhsTVtC3gxp6umLb3Fuala7RBC76nDZC9A+95QIDAQABAoIBABhHrbIcMCuivT50 8 | 4+I0K0R5fk6x8HOUhmcLaA0eozR6ZJBe+hHtkhu4GQBOAHRnDXNHOA4WMEHXxHzC 9 | tKEqCIQwQhUvQ8Ll7jegz7/teWFykg91YMm9vV6/ODtMD2Zp0Bo+FwNxMUTpPzt4 10 | hTlmaoMQK2JnxShBvUhCm2vIdRcxLHV63HjRWqHu98vKYxQ5ByQX3nVBP757zRI2 11 | rhC5g0yzQucGj2GMeD3t8W/NozNaUx9qXq2YaqhIYfhbzKZH41ZeIpE0Au7aNS4W 12 | BTpWkO1patCpSZHhTV9RIbBCG7al0ukLs3FfbWoHCAJAHUyuEvG4htSb0WqudlJn 13 | /rPNdP0CgYEA4SK8NgON4wvdi42dr43NdcOVbWes4HM4M8f1pi7W9RSracuAXj7o 14 | eyirKPnUMJchRNOlF/aTghbgtbgAYBFxYYfFbc12BURiAgo6firu6ILD7696V/ux 15 | iQPSg/chVrBkN1rYYf1sTgcJB9N7uuiBQZAh8NJWJNvviPVxNfFhoO8CgYEAxfEM 16 | CnEBiiOiKi72eclGGAAQr/JAdoaCXZPi1lmbAkWULtyqoo3MzyuyJ3GDwb1j8e2J 17 | qPEvqAW8w993Z5vqk/N9MA7rlSE6UPxTHs8ZKNWcdci0rReurG+evrGRRJmKuvqK 18 | 0/7Nqr/f039VuRqvgtWxJeFoBNZVpwGG/LeCJmsCgYAVcjyhnJcQkNnK6HOj/Isc 19 | 88OxR1YFj5REAoFZEk8xy4VEr7kLwUxeJxKe9aWL92mY59xrOvb0Rn+jb+LBRAgb 20 | 9VYOTqs2dzwq25SU3jwh9Ar8MyghZ32TAsU0Av+vBWCWkVXZh82gZTUsBK5dsLZX 21 | a4aALVk9a6IW1uKw88yMCwKBgFk3e2jdZIdB5l7DCh78ZFZ++QaE1x9VIz9QX8aj 22 | XqWYfODeXx6jcTPTixoSJQPW/ExX91spUoSWCW3ztBsEAKgs8DkQEIkIEAPepwxU 23 | 5g8ssLe5/g2ihf181f03hbV4yznZoWdKCqMyloz6cMXczEzZSl47iancfYCnxJL1 24 | l3j/AoGBAIQDUua/Ia2LLJE24kamiLmdtECHsXg/Wrp++YaGc2btHblAN5TNQfy3 25 | S4yvQIOzaVp7AQMXq/AUdua1YcLS1Op/CsocgVMzpckZ7FVS8BFuQnQx8ltnAcqb 26 | nCo6UzUdOPKNRw2EDyk9yK83wEtvkvlHOVdRsOlYN5ZSrkq1X92A 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /xbuilder/tests/resources/xsd/2.0/message.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /xbuilder/tests/resources/xsd/2.0/message.xsl: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /xbuilder/tests/resources/xsd/2.1/common/UBL-XAdESv141-2.1.xsd: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /xhandler/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xhandler" 3 | version = "0.1.0" 4 | edition = "2021" 5 | license = "Apache-2.0" 6 | description = "Creates XML files based on UBL under the standards of Peru." 7 | 8 | [dependencies] 9 | xbuilder = { path = "../xbuilder" } 10 | xsender = { path = "../xsender" } 11 | xsigner = { path = "../xsigner" } 12 | -------------------------------------------------------------------------------- /xhandler/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod prelude; 2 | -------------------------------------------------------------------------------- /xhandler/src/prelude.rs: -------------------------------------------------------------------------------- 1 | pub use xbuilder::prelude::*; 2 | pub use xsender::prelude::*; 3 | pub use xsigner::*; 4 | -------------------------------------------------------------------------------- /xsender/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xsender" 3 | description = "Sends XML files through SOAP - SUNAT" 4 | version.workspace = true 5 | edition.workspace = true 6 | license.workspace = true 7 | 8 | [dependencies] 9 | xml = { workspace = true } 10 | zip = { workspace = true } 11 | tera = { workspace = true } 12 | static-files = { workspace = true } 13 | lazy_static = { workspace = true } 14 | serde = { workspace = true, features = ["derive"] } 15 | reqwest = { workspace = true } 16 | regex = { workspace = true } 17 | base64 = { workspace = true } 18 | thiserror = { workspace = true } 19 | anyhow = { workspace = true } 20 | sha2 = { workspace = true } 21 | 22 | [dev-dependencies] 23 | serial_test = { workspace = true } 24 | tokio = { workspace = true, features = ["macros"] } 25 | 26 | [build-dependencies] 27 | static-files = { workspace = true } 28 | -------------------------------------------------------------------------------- /xsender/README.md: -------------------------------------------------------------------------------- 1 | # XSender 2 | 3 | Send files to SUNAT through SOAP or REST 4 | 5 | -------------------------------------------------------------------------------- /xsender/build.rs: -------------------------------------------------------------------------------- 1 | use static_files::resource_dir; 2 | 3 | fn main() -> std::io::Result<()> { 4 | resource_dir("./resources/templates").build() 5 | } 6 | -------------------------------------------------------------------------------- /xsender/resources/templates/get_status_crd.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{username}} 6 | {{password}} 7 | 8 | 9 | 10 | 11 | 12 | {{body.ruc}} 13 | {{body.tipo_comprobante}} 14 | {{body.serie_comprobante}} 15 | {{body.numero_comprobante}} 16 | 17 | 18 | -------------------------------------------------------------------------------- /xsender/resources/templates/send_bill.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{username}} 6 | {{password}} 7 | 8 | 9 | 10 | 11 | 12 | {{body.filename}} 13 | {{body.file_content}} 14 | 15 | 16 | -------------------------------------------------------------------------------- /xsender/resources/templates/send_summary.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{username}} 6 | {{password}} 7 | 8 | 9 | 10 | 11 | 12 | {{body.filename}} 13 | {{body.file_content}} 14 | 15 | 16 | -------------------------------------------------------------------------------- /xsender/resources/templates/validate_cdp_criterios.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{username}} 6 | {{password}} 7 | 8 | 9 | 10 | 11 | 12 | {{body.ruc}} 13 | {{body.tipo_comprobante}} 14 | {{body.serie_comprobante}} 15 | {{body.numero_comprobante}} 16 | {{body.tipo_documento_identidad_receptor}} 17 | {{body.numero_documento_identidad_receptor}} 18 | {{body.fecha_emision}} 19 | {{body.importe_total}} 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /xsender/resources/templates/validate_file.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{username}} 6 | {{password}} 7 | 8 | 9 | 10 | 11 | 12 | {{body.filename}} 13 | {{body.file_content}} 14 | 15 | 16 | -------------------------------------------------------------------------------- /xsender/resources/templates/verify_ticket.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{username}} 6 | {{password}} 7 | 8 | 9 | 10 | 11 | 12 | {{ticket}} 13 | 14 | 15 | -------------------------------------------------------------------------------- /xsender/resources/test/bill_service_response_fault.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | soap-env:Client.0111 7 | No tiene el perfil para enviar comprobantes electronicos - Detalle: Rejected by policy. 8 | 9 | 10 | -------------------------------------------------------------------------------- /xsender/resources/test/bill_service_response_ticket.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 1703154974517 6 | 7 | 8 | -------------------------------------------------------------------------------- /xsender/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod analyzer; 2 | mod client_sunat; 3 | mod constants; 4 | mod file_sender; 5 | mod models; 6 | pub mod prelude; 7 | mod soap; 8 | mod ubl_file; 9 | mod zip_manager; 10 | -------------------------------------------------------------------------------- /xsender/src/prelude.rs: -------------------------------------------------------------------------------- 1 | pub use crate::client_sunat::*; 2 | pub use crate::file_sender::*; 3 | pub use crate::models::*; 4 | pub use crate::ubl_file::*; 5 | -------------------------------------------------------------------------------- /xsender/src/soap/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod cdr; 2 | pub mod envelope; 3 | pub mod send_file_response; 4 | pub mod verify_ticket_response; 5 | 6 | pub struct SoapFault { 7 | pub code: String, 8 | pub message: String, 9 | } 10 | -------------------------------------------------------------------------------- /xsigner/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xsigner" 3 | version = "0.1.0" 4 | edition = "2021" 5 | license = "Apache-2.0" 6 | description = "Sign your XML files" 7 | 8 | [dependencies] 9 | thiserror = { workspace = true } 10 | anyhow = { workspace = true } 11 | libxml = { workspace = true } 12 | quick-xml = { workspace = true } 13 | rsa = { workspace = true, features = ["sha2"] } 14 | x509-cert = { workspace = true, features = ["builder"] } 15 | der = { workspace = true, features = ["alloc", "derive", "flagset", "oid"] } 16 | base64 = { workspace = true } 17 | openssl = { workspace = true } 18 | xml_c14n = { workspace = true } 19 | 20 | [dev-dependencies] 21 | -------------------------------------------------------------------------------- /xsigner/README.md: -------------------------------------------------------------------------------- 1 | # XSigner 2 | 3 | Sign your XML files 4 | 5 | ```shell 6 | https://xmlsec.readthedocs.io/en/stable/install.html 7 | ``` 8 | 9 | ## Fedora 10 | sudo dnf install pkg-config libxml2-devel xmlsec1-devel xmlsec1-openssl clang-devel 11 | -------------------------------------------------------------------------------- /xsigner/resources/test/LLAMA-PE-CERTIFICADO-DEMO-10467793549.pfx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-openubl/xhandler-rust/2c4a2947117d7d011f4423ec746153f892cead9e/xsigner/resources/test/LLAMA-PE-CERTIFICADO-DEMO-10467793549.pfx -------------------------------------------------------------------------------- /xsigner/resources/test/private.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEArhO3H48lGkRNcPNhA6uTd804NnMxBkXKKTgR8DldX8vTmrf0 3 | JqNGMLxUlqSG1KlRelHQXvIz7GWO0NgE0DZ9eMEULS7S8YMuj6RZFCudDb/aasxH 4 | yCvjVfdKJUF4BIPPKN2dvjFBAQz4fI/3/PceptIqzwzl+8SryXEbJgAUmjaS2POE 5 | 65RePRIINOV1Vi7lwvLzH0Zl1sr+oytOnXAI1YRlKZhgcS5v5XeX/qfRpbIQdqxc 6 | loVAQX/voN8QsLT6chZr/gEZbUnDs2HD286/Xzg27Rw8Bwy7HIbhhKYPK2TsFanp 7 | MhsTVtC3gxp6umLb3Fuala7RBC76nDZC9A+95QIDAQABAoIBABhHrbIcMCuivT50 8 | 4+I0K0R5fk6x8HOUhmcLaA0eozR6ZJBe+hHtkhu4GQBOAHRnDXNHOA4WMEHXxHzC 9 | tKEqCIQwQhUvQ8Ll7jegz7/teWFykg91YMm9vV6/ODtMD2Zp0Bo+FwNxMUTpPzt4 10 | hTlmaoMQK2JnxShBvUhCm2vIdRcxLHV63HjRWqHu98vKYxQ5ByQX3nVBP757zRI2 11 | rhC5g0yzQucGj2GMeD3t8W/NozNaUx9qXq2YaqhIYfhbzKZH41ZeIpE0Au7aNS4W 12 | BTpWkO1patCpSZHhTV9RIbBCG7al0ukLs3FfbWoHCAJAHUyuEvG4htSb0WqudlJn 13 | /rPNdP0CgYEA4SK8NgON4wvdi42dr43NdcOVbWes4HM4M8f1pi7W9RSracuAXj7o 14 | eyirKPnUMJchRNOlF/aTghbgtbgAYBFxYYfFbc12BURiAgo6firu6ILD7696V/ux 15 | iQPSg/chVrBkN1rYYf1sTgcJB9N7uuiBQZAh8NJWJNvviPVxNfFhoO8CgYEAxfEM 16 | CnEBiiOiKi72eclGGAAQr/JAdoaCXZPi1lmbAkWULtyqoo3MzyuyJ3GDwb1j8e2J 17 | qPEvqAW8w993Z5vqk/N9MA7rlSE6UPxTHs8ZKNWcdci0rReurG+evrGRRJmKuvqK 18 | 0/7Nqr/f039VuRqvgtWxJeFoBNZVpwGG/LeCJmsCgYAVcjyhnJcQkNnK6HOj/Isc 19 | 88OxR1YFj5REAoFZEk8xy4VEr7kLwUxeJxKe9aWL92mY59xrOvb0Rn+jb+LBRAgb 20 | 9VYOTqs2dzwq25SU3jwh9Ar8MyghZ32TAsU0Av+vBWCWkVXZh82gZTUsBK5dsLZX 21 | a4aALVk9a6IW1uKw88yMCwKBgFk3e2jdZIdB5l7DCh78ZFZ++QaE1x9VIz9QX8aj 22 | XqWYfODeXx6jcTPTixoSJQPW/ExX91spUoSWCW3ztBsEAKgs8DkQEIkIEAPepwxU 23 | 5g8ssLe5/g2ihf181f03hbV4yznZoWdKCqMyloz6cMXczEzZSl47iancfYCnxJL1 24 | l3j/AoGBAIQDUua/Ia2LLJE24kamiLmdtECHsXg/Wrp++YaGc2btHblAN5TNQfy3 25 | S4yvQIOzaVp7AQMXq/AUdua1YcLS1Op/CsocgVMzpckZ7FVS8BFuQnQx8ltnAcqb 26 | nCo6UzUdOPKNRw2EDyk9yK83wEtvkvlHOVdRsOlYN5ZSrkq1X92A 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /xsigner/resources/test/public.cer: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIE+DCCA+CgAwIBAgIJALURz7AYmJ5+MA0GCSqGSIb3DQEBBQUAMIIBDTEbMBkG 3 | CgmSJomT8ixkARkWC0xMQU1BLlBFIFNBMQswCQYDVQQGEwJQRTENMAsGA1UECAwE 4 | TElNQTENMAsGA1UEBwwETElNQTEYMBYGA1UECgwPVFUgRU1QUkVTQSBTLkEuMUUw 5 | QwYDVQQLDDxETkkgOTk5OTk5OSBSVUMgMTA0Njc3OTM1NDkgLSBDRVJUSUZJQ0FE 6 | TyBQQVJBIERFTU9TVFJBQ0nDk04xRDBCBgNVBAMMO05PTUJSRSBSRVBSRVNFTlRB 7 | TlRFIExFR0FMIC0gQ0VSVElGSUNBRE8gUEFSQSBERU1PU1RSQUNJw5NOMRwwGgYJ 8 | KoZIhvcNAQkBFg1kZW1vQGxsYW1hLnBlMB4XDTE5MTEwODEyNTY1MFoXDTIxMTEw 9 | NzEyNTY1MFowggENMRswGQYKCZImiZPyLGQBGRYLTExBTUEuUEUgU0ExCzAJBgNV 10 | BAYTAlBFMQ0wCwYDVQQIDARMSU1BMQ0wCwYDVQQHDARMSU1BMRgwFgYDVQQKDA9U 11 | VSBFTVBSRVNBIFMuQS4xRTBDBgNVBAsMPEROSSA5OTk5OTk5IFJVQyAxMDQ2Nzc5 12 | MzU0OSAtIENFUlRJRklDQURPIFBBUkEgREVNT1NUUkFDScOTTjFEMEIGA1UEAww7 13 | Tk9NQlJFIFJFUFJFU0VOVEFOVEUgTEVHQUwgLSBDRVJUSUZJQ0FETyBQQVJBIERF 14 | TU9TVFJBQ0nDk04xHDAaBgkqhkiG9w0BCQEWDWRlbW9AbGxhbWEucGUwggEiMA0G 15 | CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCuE7cfjyUaRE1w82EDq5N3zTg2czEG 16 | RcopOBHwOV1fy9Oat/Qmo0YwvFSWpIbUqVF6UdBe8jPsZY7Q2ATQNn14wRQtLtLx 17 | gy6PpFkUK50Nv9pqzEfIK+NV90olQXgEg88o3Z2+MUEBDPh8j/f89x6m0irPDOX7 18 | xKvJcRsmABSaNpLY84TrlF49Egg05XVWLuXC8vMfRmXWyv6jK06dcAjVhGUpmGBx 19 | Lm/ld5f+p9GlshB2rFyWhUBBf++g3xCwtPpyFmv+ARltScOzYcPbzr9fODbtHDwH 20 | DLschuGEpg8rZOwVqekyGxNW0LeDGnq6YtvcW5qVrtEELvqcNkL0D73lAgMBAAGj 21 | VzBVMB0GA1UdDgQWBBTe18LHVKkeRrWs3Bxp1ikK50l96jAfBgNVHSMEGDAWgBTe 22 | 18LHVKkeRrWs3Bxp1ikK50l96jATBgNVHSUEDDAKBggrBgEFBQcDATANBgkqhkiG 23 | 9w0BAQUFAAOCAQEASBWcP4AiqUUZSG2/Z3RU3BgvOVV3if8xYAaZT99n5PsvyBiZ 24 | 3Gh6VpAW9ezoe25ZNSqGMmGfq+R4mEuiqK4h6jDJp0fN47IwRhWjttB9dwpxIDEk 25 | WW7zPdueGx+BY8EuyfNDWR/C7GPfu6azSHiapzeKC57AAZ8xo8kDdhXxDy8hTqNG 26 | olkqnc6QutW8cYPeonqyhi2THN163lZ3Cx5OV8vGFQ3ou2msF0klY9qXopI9i8wQ 27 | jEeOG6bCvVxdID9ZjTbuGbO9pAN9hH7hZ41XEG/CspSWbFf1/Wbnlfusne9v9NgR 28 | j0MM+LAHM7AO5/7j1XwRq4x+U9TSVPgpoU9l5Q== 29 | -----END CERTIFICATE----- 30 | --------------------------------------------------------------------------------