Skip to content

Commit 1bef5fa

Browse files
committed
Add new XML test cases for DespatchAdvice scenarios
- Created vehiculoConCarreta.xml for vehicle with cart scenario. - Created vehiculoM1L.xml for M1L vehicle scenario. - Added transportistaBasico.xml for basic transporter scenario. - Added transportistaComercioExterior.xml for foreign trade transporter scenario. - Created transportistaConCarreta.xml for transporter with cart scenario. - Added transportistaMultiplesConductores.xml for transporter with multiple drivers scenario.
1 parent 0441fa8 commit 1bef5fa

31 files changed

+4004
-726
lines changed

examples/springbot/src/main/java/io/github/project/openubl/quickstart/xbuilder/springboot/XBuilderController.java

Lines changed: 491 additions & 395 deletions
Large diffs are not rendered by default.

xbuilder/core/src/main/java/io/github/project/openubl/xbuilder/content/catalogs/Catalog20.java

Lines changed: 87 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,104 @@
1+
/*
2+
* Catálogo 20 - Motivo de traslado
3+
*
4+
* Fuente normativa: Anexo N.° 8 de la RS 000123-2022/SUNAT,
5+
* actualizado por RS 000240-2024/SUNAT.
6+
*/
17
package io.github.project.openubl.xbuilder.content.catalogs;
28

9+
import java.util.Optional;
10+
import java.util.stream.Stream;
11+
12+
/**
13+
* Catálogo N.° 20 - Motivo de traslado.
14+
* <p>
15+
* Vigente según RS 000123-2022/SUNAT y RS 000240-2024/SUNAT.
16+
* Aplicable tanto a GRE-Remitente (09) como GRE-Transportista (31).
17+
*/
318
public enum Catalog20 implements Catalog {
19+
20+
/** 01 - Venta */
421
VENTA("01"),
5-
VENTA_SUJETA_A_CONFIRMACION_DEL_COMPRADOR("14"),
22+
23+
/** 02 - Compra */
624
COMPRA("02"),
7-
TRASLADO_ENTRE_ESTABLECIMIENTOS_DE_LA_MISMA_EMPRESA("04"),
8-
TRASLADO_EMISOR_ITINERANTE_CP("18"),
25+
26+
/** 03 - Venta con entrega a terceros (consignación) */
27+
CONSIGNACION("03"),
28+
29+
/** 04 - Traslado entre establecimientos de la misma empresa */
30+
TRASLADO_ENTRE_ESTABLECIMIENTOS("04"),
31+
32+
/** 05 - Devolución */
33+
DEVOLUCION("05"),
34+
35+
/** 06 - Traslado de bienes para transformación */
36+
TRASLADO_TRANSFORMACION("06"),
37+
38+
/** 07 - Recojo de bienes transformados */
39+
RECOJO_BIENES_TRANSFORMADOS("07"),
40+
41+
/** 08 - Importación */
942
IMPORTACION("08"),
43+
44+
/** 09 - Exportación */
1045
EXPORTACION("09"),
11-
TRASLADO_A_ZONA_PRIMARIA("19"),
12-
OTROS("13");
46+
47+
/**
48+
* 10 - Importación: traslado de bienes con DAM/DS con levante.
49+
* <p>
50+
* Aplica cuando la mercancía tiene levante autorizado.
51+
* Incorporado por RS 000240-2024/SUNAT para trazabilidad de comercio exterior.
52+
* Vigente desde 14-nov-2024; uso como motivo mandatorio pospuesto al
53+
* 01-jul-2026.
54+
*/
55+
IMPORTACION_CON_DAM("10"),
56+
57+
/** 11 - Importación temporal */
58+
IMPORTACION_TEMPORAL("11"),
59+
60+
/** 13 - Otros */
61+
OTROS("13"),
62+
63+
/** 14 - Venta sujeta a confirmación del comprador */
64+
VENTA_SUJETA_A_CONFIRMACION("14"),
65+
66+
/**
67+
* 15 - Traslado de bienes zona IVAP.
68+
* <p>
69+
* Aplica para traslados de bienes gravados con IVAP (arroz).
70+
*/
71+
TRASLADO_ZONA_IVAP("15"),
72+
73+
/** 16 - Exportación temporal (admisión temporal) */
74+
EXPORTACION_TEMPORAL("16"),
75+
76+
/** 17 - Reexportación */
77+
REEXPORTACION("17"),
78+
79+
/** 18 - Traslado emisor itinerante de comprobantes de pago */
80+
TRASLADO_EMISOR_ITINERANTE_CP("18"),
81+
82+
/**
83+
* 19 - Traslado de mercancía extranjera (zona primaria a depósito temporal).
84+
* <p>
85+
* Uso obligatorio a partir del 01-jul-2026 para traslado de mercancía
86+
* extranjera sin destinación aduanera o sin levante, en reemplazo del ticket de
87+
* salida.
88+
* Vigencia de la derogación del ticket pospuesta por RS 000133-2025/SUNAT.
89+
*/
90+
TRASLADO_MERCANCIA_EXTRANJERA("19");
1391

1492
private final String code;
1593

1694
Catalog20(String code) {
1795
this.code = code;
1896
}
1997

98+
public static Optional<Catalog20> valueOfCode(String code) {
99+
return Stream.of(Catalog20.values()).filter(p -> p.code.equals(code)).findFirst();
100+
}
101+
20102
@Override
21103
public String getCode() {
22104
return code;

xbuilder/core/src/main/java/io/github/project/openubl/xbuilder/content/catalogs/Catalog20_1.java

Lines changed: 0 additions & 109 deletions
This file was deleted.

xbuilder/core/src/main/java/io/github/project/openubl/xbuilder/content/catalogs/IndicadorEnvio.java

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,32 @@ public enum IndicadorEnvio implements Catalog {
4444
BIEN_NORMALIZADO("SUNAT_Envio_IndicadorBienNormalizado"),
4545

4646
/**
47-
* Indica que el traslado es en vehículos de categoría M1 o L.
47+
* Indica que el traslado es en vehículos de categoría M1 o L (vehículos
48+
* menores).
4849
* <p>
4950
* Aplica: GRE-Remitente (transporte privado).
50-
* Cuando se marca, no se requiere placa ni conductor.
51+
* Cuando se marca, no se requiere consignar placa ni conductor.
52+
* <p>
53+
* <b>PENDIENTE DE VALIDACIÓN:</b> El token exacto de este indicador debe
54+
* confirmarse contra el Anexo N.° 14 o el catálogo interno del portal SUNAT.
55+
* La separación conceptual respecto de {@link #TRANSBORDO_PROGRAMADO} es
56+
* correcta.
57+
* <p>
58+
* Ref: Anexo N.° 14, RS 000123-2022/SUNAT — campo cbc:SpecialInstructions.
59+
*/
60+
VEHICULO_M1_L("SUNAT_Envio_IndicadorTrasladoVehiculoM1L"),
61+
62+
/**
63+
* Indica que se ha producido un transbordo programado durante el trayecto.
64+
* <p>
65+
* Este indicador corresponde a la GRE por eventos: se utiliza cuando ocurre
66+
* un hecho no imputable al emisor que obliga a un transbordo o reinicio del
67+
* traslado. NO es equivalente a vehículo categoría M1/L.
68+
* <p>
69+
* Aplica: GRE-Remitente, GRE-Transportista (GRE complementaria por eventos).
70+
* Ref: Numeral 4, Anexo RS 000123-2022/SUNAT — GRE por eventos.
5171
*/
52-
VEHICULO_M1_L("SUNAT_Envio_IndicadorTransbordoProgramado"),
72+
TRANSBORDO_PROGRAMADO("SUNAT_Envio_IndicadorTransbordoProgramado"),
5373

5474
/**
5575
* Indica que el retorno del vehículo está programado y la GRE ampara el
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package io.github.project.openubl.xbuilder.content.catalogs;
2+
3+
/**
4+
* Tipo de conductor en la Guía de Remisión Electrónica.
5+
* <p>
6+
* Se mapea al campo {@code cbc:JobTitle} del elemento {@code cac:DriverPerson}
7+
* en el XML UBL 2.1.
8+
* <p>
9+
* SUNAT distingue dos roles:
10+
* <ul>
11+
* <li><b>Principal</b>: conductor responsable del vehículo (obligatorio).</li>
12+
* <li><b>Secundario</b>: conductor de relevo o copiloto (opcional).</li>
13+
* </ul>
14+
* <p>
15+
* Ref: Anexo N.° 14 UBL 2.1, RS 000123-2022/SUNAT.
16+
*/
17+
public enum TipoConductor implements Catalog {
18+
19+
/**
20+
* Conductor principal — obligatorio cuando se requiere consignar conductor.
21+
*/
22+
PRINCIPAL("Principal"),
23+
24+
/**
25+
* Conductor secundario (relevo, copiloto) — opcional.
26+
*/
27+
SECUNDARIO("Secundario");
28+
29+
private final String code;
30+
31+
TipoConductor(String code) {
32+
this.code = code;
33+
}
34+
35+
@Override
36+
public String getCode() {
37+
return code;
38+
}
39+
}

xbuilder/core/src/main/java/io/github/project/openubl/xbuilder/content/jaxb/mappers/DespatchAdviceMapper.java

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,6 @@ default Tercero mapDespatchAdviceTercero(XMLDespatchAdvice.SellerSupplierParty x
138138
@Mapping(target = "choferes", source = "shipmentStage.driverPersons", qualifiedByName = "mapChoferes")
139139
@Mapping(target = "indicadores", source = "specialInstructions", qualifiedByName = "mapIndicadores")
140140
@Mapping(target = "contenedores", source = "transportHandlingUnit", qualifiedByName = "mapContenedores")
141-
@Mapping(target = "contenedoresDetalle", source = "transportHandlingUnit", qualifiedByName = "mapContenedoresDetalle")
142141
@Mapping(target = "pesoItems", source = "netWeightMeasure.value")
143142
@Mapping(target = "sustentoPeso", source = "information")
144143
@Mapping(target = "puerto", source = "firstArrivalPortLocation", qualifiedByName = "mapPuerto")
@@ -147,7 +146,6 @@ default Tercero mapDespatchAdviceTercero(XMLDespatchAdvice.SellerSupplierParty x
147146
@Mapping(target = "chofer", ignore = true)
148147
@Mapping(target = "indicador", ignore = true)
149148
@Mapping(target = "contenedor", ignore = true)
150-
@Mapping(target = "contenedorDetalle", ignore = true)
151149
@Mapping(target = "declaracionAduanera", ignore = true)
152150
@Mapping(target = "declaracionesAduaneras", ignore = true)
153151
@Mapping(target = "numeroManifiesto", ignore = true)
@@ -235,22 +233,7 @@ default String mapPrimaryDriverNum(List<XMLDespatchAdvice.DriverPerson> drivers)
235233
}
236234

237235
@Named("mapContenedores")
238-
default List<String> mapContenedores(List<XMLDespatchAdvice.TransportHandlingUnit> units) {
239-
if (units == null)
240-
return java.util.Collections.emptyList();
241-
List<String> result = new java.util.ArrayList<>();
242-
for (XMLDespatchAdvice.TransportHandlingUnit unit : units) {
243-
if (unit.getPackages() != null) {
244-
unit.getPackages().stream().map(XMLDespatchAdvice.Package::getTraceID)
245-
.filter(java.util.Objects::nonNull)
246-
.forEach(result::add);
247-
}
248-
}
249-
return result;
250-
}
251-
252-
@Named("mapContenedoresDetalle")
253-
default List<Contenedor> mapContenedoresDetalle(List<XMLDespatchAdvice.TransportHandlingUnit> units) {
236+
default List<Contenedor> mapContenedores(List<XMLDespatchAdvice.TransportHandlingUnit> units) {
254237
if (units == null)
255238
return java.util.Collections.emptyList();
256239
List<Contenedor> result = new java.util.ArrayList<>();

xbuilder/core/src/main/java/io/github/project/openubl/xbuilder/content/models/standard/guia/DespatchAdviceValidator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ private static void validateEnvioModalidad(DespatchAdvice da, Envio envio, List<
166166
if ("02".equals(modalidad) && isGRERemitente) {
167167
// Transporte privado en GRE-Remitente: conductor y vehículo requeridos
168168
boolean tieneIndicadorM1L = envio.getIndicadores() != null &&
169-
envio.getIndicadores().contains("SUNAT_Envio_IndicadorTransbordoProgramado");
169+
envio.getIndicadores().contains("SUNAT_Envio_IndicadorTrasladoVehiculoM1L");
170170

171171
if (!tieneIndicadorM1L) {
172172
if (envio.getChoferes() == null || envio.getChoferes().isEmpty()) {

0 commit comments

Comments
 (0)