From da4294791e861b89b4b976b459b739eb0920a7a7 Mon Sep 17 00:00:00 2001
From: Carlos Feria <2582866+carlosthe19916@users.noreply.github.com>
Date: Mon, 2 Mar 2026 22:33:43 +0100
Subject: [PATCH 1/3] feat: add invoice attributos
Signed-off-by: Carlos Feria <2582866+carlosthe19916@users.noreply.github.com>
---
xbuilder/tests/credit_note_atributos.rs | 43 ++++++
xbuilder/tests/debit_note_atributos.rs | 43 ++++++
xbuilder/tests/invoice_atributos.rs | 79 ++++++++++
.../atributosSimple.xml | 138 +++++++++++++++++
.../atributosSimple.xml | 138 +++++++++++++++++
.../atributosConPeriodo.xml | 139 ++++++++++++++++++
.../InvoiceAtributosTest/atributosSimple.xml | 134 +++++++++++++++++
7 files changed, 714 insertions(+)
create mode 100644 xbuilder/tests/credit_note_atributos.rs
create mode 100644 xbuilder/tests/debit_note_atributos.rs
create mode 100644 xbuilder/tests/invoice_atributos.rs
create mode 100644 xbuilder/tests/resources/e2e/renderer/creditnote/CreditNoteAtributosTest/atributosSimple.xml
create mode 100644 xbuilder/tests/resources/e2e/renderer/debitnote/DebitNoteAtributosTest/atributosSimple.xml
create mode 100644 xbuilder/tests/resources/e2e/renderer/invoice/InvoiceAtributosTest/atributosConPeriodo.xml
create mode 100644 xbuilder/tests/resources/e2e/renderer/invoice/InvoiceAtributosTest/atributosSimple.xml
diff --git a/xbuilder/tests/credit_note_atributos.rs b/xbuilder/tests/credit_note_atributos.rs
new file mode 100644
index 00000000..cd90d9e2
--- /dev/null
+++ b/xbuilder/tests/credit_note_atributos.rs
@@ -0,0 +1,43 @@
+use rust_decimal_macros::dec;
+
+use xbuilder::prelude::*;
+
+use crate::common::{assert_credit_note, credit_note_base};
+
+mod common;
+
+const BASE: &str = "tests/resources/e2e/renderer/creditnote/CreditNoteAtributosTest";
+
+#[serial_test::serial]
+#[tokio::test]
+async fn credit_note_with_atributos() {
+ let mut credit_note = CreditNote {
+ detalles: vec![Detalle {
+ descripcion: "Item1",
+ cantidad: dec!(10),
+ precio: Some(dec!(100)),
+ atributos: vec![
+ Atributo {
+ nombre: "Marca",
+ codigo: "BRA",
+ valor: Some("Samsung"),
+ fecha_inicio: None,
+ fecha_fin: None,
+ duracion: None,
+ },
+ Atributo {
+ nombre: "Color",
+ codigo: "COL",
+ valor: Some("Negro"),
+ fecha_inicio: None,
+ fecha_fin: None,
+ duracion: None,
+ },
+ ],
+ ..Default::default()
+ }],
+ ..credit_note_base()
+ };
+
+ assert_credit_note(&mut credit_note, &format!("{BASE}/atributosSimple.xml")).await;
+}
diff --git a/xbuilder/tests/debit_note_atributos.rs b/xbuilder/tests/debit_note_atributos.rs
new file mode 100644
index 00000000..6dbd880e
--- /dev/null
+++ b/xbuilder/tests/debit_note_atributos.rs
@@ -0,0 +1,43 @@
+use rust_decimal_macros::dec;
+
+use xbuilder::prelude::*;
+
+use crate::common::{assert_debit_note, debit_note_base};
+
+mod common;
+
+const BASE: &str = "tests/resources/e2e/renderer/debitnote/DebitNoteAtributosTest";
+
+#[serial_test::serial]
+#[tokio::test]
+async fn debit_note_with_atributos() {
+ let mut debit_note = DebitNote {
+ detalles: vec![Detalle {
+ descripcion: "Item1",
+ cantidad: dec!(10),
+ precio: Some(dec!(100)),
+ atributos: vec![
+ Atributo {
+ nombre: "Marca",
+ codigo: "BRA",
+ valor: Some("Samsung"),
+ fecha_inicio: None,
+ fecha_fin: None,
+ duracion: None,
+ },
+ Atributo {
+ nombre: "Color",
+ codigo: "COL",
+ valor: Some("Negro"),
+ fecha_inicio: None,
+ fecha_fin: None,
+ duracion: None,
+ },
+ ],
+ ..Default::default()
+ }],
+ ..debit_note_base()
+ };
+
+ assert_debit_note(&mut debit_note, &format!("{BASE}/atributosSimple.xml")).await;
+}
diff --git a/xbuilder/tests/invoice_atributos.rs b/xbuilder/tests/invoice_atributos.rs
new file mode 100644
index 00000000..41cdf502
--- /dev/null
+++ b/xbuilder/tests/invoice_atributos.rs
@@ -0,0 +1,79 @@
+use chrono::NaiveDate;
+use rust_decimal_macros::dec;
+
+use xbuilder::prelude::*;
+
+use crate::common::assert_invoice;
+use crate::common::invoice_base;
+
+mod common;
+
+const BASE: &str = "tests/resources/e2e/renderer/invoice/InvoiceAtributosTest";
+
+#[serial_test::serial]
+#[tokio::test]
+async fn invoice_with_atributos_simple() {
+ let mut invoice = Invoice {
+ detalles: vec![Detalle {
+ descripcion: "Item1",
+ cantidad: dec!(2),
+ precio: Some(dec!(100)),
+ atributos: vec![
+ Atributo {
+ nombre: "Marca",
+ codigo: "BRA",
+ valor: Some("Samsung"),
+ fecha_inicio: None,
+ fecha_fin: None,
+ duracion: None,
+ },
+ Atributo {
+ nombre: "Color",
+ codigo: "COL",
+ valor: Some("Negro"),
+ fecha_inicio: None,
+ fecha_fin: None,
+ duracion: None,
+ },
+ ],
+ ..Default::default()
+ }],
+ ..invoice_base()
+ };
+
+ assert_invoice(&mut invoice, &format!("{BASE}/atributosSimple.xml")).await;
+}
+
+#[serial_test::serial]
+#[tokio::test]
+async fn invoice_with_atributos_con_periodo() {
+ let mut invoice = Invoice {
+ detalles: vec![Detalle {
+ descripcion: "Item1",
+ cantidad: dec!(2),
+ precio: Some(dec!(100)),
+ atributos: vec![
+ Atributo {
+ nombre: "Garantia",
+ codigo: "WAR",
+ valor: Some("12 meses"),
+ fecha_inicio: Some(NaiveDate::from_ymd_opt(2019, 12, 24).unwrap()),
+ fecha_fin: Some(NaiveDate::from_ymd_opt(2020, 12, 24).unwrap()),
+ duracion: Some(365),
+ },
+ Atributo {
+ nombre: "Modelo",
+ codigo: "MOD",
+ valor: Some("Galaxy S10"),
+ fecha_inicio: None,
+ fecha_fin: None,
+ duracion: None,
+ },
+ ],
+ ..Default::default()
+ }],
+ ..invoice_base()
+ };
+
+ assert_invoice(&mut invoice, &format!("{BASE}/atributosConPeriodo.xml")).await;
+}
diff --git a/xbuilder/tests/resources/e2e/renderer/creditnote/CreditNoteAtributosTest/atributosSimple.xml b/xbuilder/tests/resources/e2e/renderer/creditnote/CreditNoteAtributosTest/atributosSimple.xml
new file mode 100644
index 00000000..5b7f1d87
--- /dev/null
+++ b/xbuilder/tests/resources/e2e/renderer/creditnote/CreditNoteAtributosTest/atributosSimple.xml
@@ -0,0 +1,138 @@
+
+
+
+
+
+
+
+ 2.1
+ 2.0
+ FC01-1
+ 2019-12-24
+ PEN
+
+ F001-1
+ 01
+
+
+
+
+ F001-1
+ 01
+
+
+
+ 12345678912
+
+
+ 12345678912
+
+
+
+
+
+
+
+ #PROJECT-OPENUBL-SIGN
+
+
+
+
+
+
+ 12345678912
+
+
+
+
+ 0000
+
+
+
+
+
+
+
+ 12121212121
+
+
+
+
+
+
+
+ 180.00
+
+ 1000.00
+ 180.00
+
+ S
+
+ 1000
+ IGV
+ VAT
+
+
+
+
+
+ 1000.00
+ 1180.00
+ 1180.00
+
+
+ 1
+ 10
+ 1000.00
+
+
+ 118.00
+ 01
+
+
+
+ 180.00
+
+ 1000.00
+ 180.00
+
+ S
+ 18.00
+ 10
+
+ 1000
+ IGV
+ VAT
+
+
+
+
+
+
+
+ Marca
+ BRA
+ Samsung
+
+
+ Color
+ COL
+ Negro
+
+
+
+ 100.00
+
+
+
diff --git a/xbuilder/tests/resources/e2e/renderer/debitnote/DebitNoteAtributosTest/atributosSimple.xml b/xbuilder/tests/resources/e2e/renderer/debitnote/DebitNoteAtributosTest/atributosSimple.xml
new file mode 100644
index 00000000..56ea7741
--- /dev/null
+++ b/xbuilder/tests/resources/e2e/renderer/debitnote/DebitNoteAtributosTest/atributosSimple.xml
@@ -0,0 +1,138 @@
+
+
+
+
+
+
+
+ 2.1
+ 2.0
+ FD01-1
+ 2019-12-24
+ PEN
+
+ F001-1
+ 01
+
+
+
+
+ F001-1
+ 01
+
+
+
+ 12345678912
+
+
+ 12345678912
+
+
+
+
+
+
+
+ #PROJECT-OPENUBL-SIGN
+
+
+
+
+
+
+ 12345678912
+
+
+
+
+ 0000
+
+
+
+
+
+
+
+ 12121212121
+
+
+
+
+
+
+
+ 180.00
+
+ 1000.00
+ 180.00
+
+ S
+
+ 1000
+ IGV
+ VAT
+
+
+
+
+
+ 1000.00
+ 1180.00
+ 1180.00
+
+
+ 1
+ 10
+ 1000.00
+
+
+ 118.00
+ 01
+
+
+
+ 180.00
+
+ 1000.00
+ 180.00
+
+ S
+ 18.00
+ 10
+
+ 1000
+ IGV
+ VAT
+
+
+
+
+
+
+
+ Marca
+ BRA
+ Samsung
+
+
+ Color
+ COL
+ Negro
+
+
+
+ 100.00
+
+
+
diff --git a/xbuilder/tests/resources/e2e/renderer/invoice/InvoiceAtributosTest/atributosConPeriodo.xml b/xbuilder/tests/resources/e2e/renderer/invoice/InvoiceAtributosTest/atributosConPeriodo.xml
new file mode 100644
index 00000000..4cf5cf0e
--- /dev/null
+++ b/xbuilder/tests/resources/e2e/renderer/invoice/InvoiceAtributosTest/atributosConPeriodo.xml
@@ -0,0 +1,139 @@
+
+
+
+
+
+
+
+ 2.1
+ 2.0
+ F001-1
+ 2019-12-24
+ 01
+ PEN
+
+ 12345678912
+
+
+ 12345678912
+
+
+
+
+
+
+
+ #PROJECT-OPENUBL-SIGN
+
+
+
+
+
+
+ 12345678912
+
+
+
+
+ 0000
+
+
+
+
+
+
+
+ 12121212121
+
+
+
+
+
+
+
+ FormaPago
+ Contado
+
+
+ 36.00
+
+ 200.00
+ 36.00
+
+ S
+
+ 1000
+ IGV
+ VAT
+
+
+
+
+
+ 200.00
+ 236.00
+ 0
+ 0
+ 236.00
+
+
+ 1
+ 2
+ 200.00
+
+
+ 118.00
+ 01
+
+
+
+ 36.00
+
+ 200.00
+ 36.00
+
+ S
+ 18.00
+ 10
+
+ 1000
+ IGV
+ VAT
+
+
+
+
+
+
+
+ Garantia
+ WAR
+ 12 meses
+
+ 2019-12-24
+ 2020-12-24
+ 365
+
+
+
+ Modelo
+ MOD
+ Galaxy S10
+
+
+
+ 100.00
+
+
+
diff --git a/xbuilder/tests/resources/e2e/renderer/invoice/InvoiceAtributosTest/atributosSimple.xml b/xbuilder/tests/resources/e2e/renderer/invoice/InvoiceAtributosTest/atributosSimple.xml
new file mode 100644
index 00000000..4408001d
--- /dev/null
+++ b/xbuilder/tests/resources/e2e/renderer/invoice/InvoiceAtributosTest/atributosSimple.xml
@@ -0,0 +1,134 @@
+
+
+
+
+
+
+
+ 2.1
+ 2.0
+ F001-1
+ 2019-12-24
+ 01
+ PEN
+
+ 12345678912
+
+
+ 12345678912
+
+
+
+
+
+
+
+ #PROJECT-OPENUBL-SIGN
+
+
+
+
+
+
+ 12345678912
+
+
+
+
+ 0000
+
+
+
+
+
+
+
+ 12121212121
+
+
+
+
+
+
+
+ FormaPago
+ Contado
+
+
+ 36.00
+
+ 200.00
+ 36.00
+
+ S
+
+ 1000
+ IGV
+ VAT
+
+
+
+
+
+ 200.00
+ 236.00
+ 0
+ 0
+ 236.00
+
+
+ 1
+ 2
+ 200.00
+
+
+ 118.00
+ 01
+
+
+
+ 36.00
+
+ 200.00
+ 36.00
+
+ S
+ 18.00
+ 10
+
+ 1000
+ IGV
+ VAT
+
+
+
+
+
+
+
+ Marca
+ BRA
+ Samsung
+
+
+ Color
+ COL
+ Negro
+
+
+
+ 100.00
+
+
+
From bf34a4fb22be696c8451dbf32c805a1306f4702c Mon Sep 17 00:00:00 2001
From: Carlos Feria <2582866+carlosthe19916@users.noreply.github.com>
Date: Mon, 2 Mar 2026 23:06:20 +0100
Subject: [PATCH 2/3] fix: struct
---
xbuilder/src/models/common.rs | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/xbuilder/src/models/common.rs b/xbuilder/src/models/common.rs
index f842f054..27514766 100644
--- a/xbuilder/src/models/common.rs
+++ b/xbuilder/src/models/common.rs
@@ -140,6 +140,17 @@ pub struct DocumentoRelacionado {
pub serie_numero: &'static str,
}
+/// Atributo adicional de un detalle
+#[derive(Clone, Debug, Serialize, Default)]
+pub struct Atributo {
+ pub nombre: &'static str,
+ pub codigo: &'static str,
+ pub valor: Option<&'static str>,
+ pub fecha_inicio: Option,
+ pub fecha_fin: Option,
+ pub duracion: Option,
+}
+
/// Detalle de las ventas en Boleta/Factura/Nota Credito/Nota Debito
#[derive(Clone, Debug, Serialize, Default)]
pub struct Detalle {
@@ -173,6 +184,8 @@ pub struct Detalle {
pub isc_base_imponible: Option,
pub total_impuestos: Option,
+
+ pub atributos: Vec,
}
/// Total Importe Invoice
From 95f422115aade199c6d338d6a96c49404d78eeb8 Mon Sep 17 00:00:00 2001
From: Carlos Feria <2582866+carlosthe19916@users.noreply.github.com>
Date: Tue, 3 Mar 2026 08:21:44 +0100
Subject: [PATCH 3/3] fix: templating
Signed-off-by: Carlos Feria <2582866+carlosthe19916@users.noreply.github.com>
---
.../ubl/standard/include/document-line.xml | 22 +++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/xbuilder/resources/templates/ubl/standard/include/document-line.xml b/xbuilder/resources/templates/ubl/standard/include/document-line.xml
index 15819271..7656d435 100644
--- a/xbuilder/resources/templates/ubl/standard/include/document-line.xml
+++ b/xbuilder/resources/templates/ubl/standard/include/document-line.xml
@@ -73,6 +73,28 @@
{{item.codigo_sunat}}
{%- endif %}
+ {%- for atributo in item.atributos %}
+
+ {{atributo.nombre}}
+ {{atributo.codigo}}
+ {%- if atributo.valor %}
+ {{atributo.valor}}
+ {%- endif %}
+ {%- if atributo.fecha_inicio or atributo.fecha_fin or atributo.duracion %}
+
+ {%- if atributo.fecha_inicio %}
+ {{atributo.fecha_inicio}}
+ {%- endif %}
+ {%- if atributo.fecha_fin %}
+ {{atributo.fecha_fin}}
+ {%- endif %}
+ {%- if atributo.duracion %}
+ {{atributo.duracion}}
+ {%- endif %}
+
+ {%- endif %}
+
+ {%- endfor %}
{{item.precio | round_decimal}}