Repository navigation
Retenciones. Múltiples pagos para una factura. #258
Description
Hoy me topé con un detalle para en las retenciones. Si una factura de proveedor se paga en cuotas, es decir, tiene más de un pago y se van a realizar la retención con más de uno de ellos. No se puede hacer un loop de los pagos a pesar que el template lo permita.
Fragmento:
{% if det.pagos %}
{% for pay in det.pagos %}
<cac:Payment>
<cbc:ID>{{ loop.index }}</cbc:ID>
<cbc:PaidAmount currencyID="{{ pay.moneda }}">{{ pay.importe|n_format }}</cbc:PaidAmount>
<cbc:PaidDate>{{ pay.fecha|date('Y-m-d') }}</cbc:PaidDate>
</cac:Payment>
{% endfor %}
{% endif %}
Entonces, si se hace algo como esto:
sac:SUNATRetentionDocumentReference
<cbc:ID schemeID="01">FE01-2234</cbc:ID>
cbc:IssueDate2025-11-02</cbc:IssueDate>
<cbc:TotalInvoiceAmount currencyID="PEN">30.00</cbc:TotalInvoiceAmount>
cac:Payment
cbc:ID1</cbc:ID>
<cbc:PaidAmount currencyID="PEN">30.00</cbc:PaidAmount>
cbc:PaidDate2025-11-01</cbc:PaidDate>
</cac:Payment>
cac:Payment
cbc:ID2</cbc:ID>
<cbc:PaidAmount currencyID="PEN">36.00</cbc:PaidAmount>
cbc:PaidDate2025-11-01</cbc:PaidDate>
</cac:Payment>
sac:SUNATRetentionInformation
<sac:SUNATRetentionAmount currencyID="PEN">1.98</sac:SUNATRetentionAmount>
sac:SUNATRetentionDate2025-11-09</sac:SUNATRetentionDate>
<sac:SUNATNetTotalPaid currencyID="PEN">64.02</sac:SUNATNetTotalPaid>
</sac:SUNATRetentionInformation>
</sac:SUNATRetentionDocumentReference>
Nos genera el siguiente error:
Error al enviar a SUNAT: 0306 | {'message':'No se puede leer (parsear) el archivo XML - Detalle: http://xxx.xxx.xxx/ol-ti-itemision-otroscpe-gem/guia_remision_gem: cvc-complex-type 2.4: in element {urn:sunat:names:specification:ubl:peru:schema:xsd:SunatAggregateComponents-1}SUNATRetentionDocumentReference of type {urn:sunat:names:specification:ubl:peru:schema:xsd:SunatAggregateComponents-1}SUNATRetentionDocumentReferenceType, found (in namespace urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2), but next item should be end-element'}
Esto se debe a que dentro del XSD:
<!-- Retencion -->
<xsd:complexType name="SUNATRetentionDocumentReferenceType">
<xsd:sequence>
<xsd:element ref="cbc:ID" />
<xsd:element ref="cbc:IssueDate" />
<xsd:element ref="cbc:TotalInvoiceAmount" />
<xsd:element ref="cac:Payment" minOccurs="0" maxOccurs="1" />
<xsd:element ref="SUNATRetentionInformation" minOccurs="0"
maxOccurs="1" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="SUNATRetentionInformationType">
<xsd:sequence>
<xsd:element ref="SUNATRetentionAmount" />
<xsd:element ref="SUNATRetentionDate" />
<xsd:element ref="SUNATNetTotalPaid" />
<xsd:element ref="cac:ExchangeRate" minOccurs="0"
maxOccurs="1" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="SUNATRetentionDateType">
<xsd:simpleContent>
<xsd:extension base="udt:DateType" />
</xsd:simpleContent>
</xsd:complexType>
<!-- Fin Retencion -->
<xsd:element ref="cac:Payment" minOccurs="0" maxOccurs="1" />
Solo se puede hacer un pago por factura.
Por otro lado, para el número de pago se genera por un loop:
cbc:ID{{ loop.index }}</cbc:ID>
Entonces siempre será uno.
Entonces he agregado unos métodos para el número de pago:
/**
* Número de pago.
*
* @var int
*/
private $numero;
/**
* @return int
*/
public function getNumero(): ?int
{
return $this->numero;
}
/**
* @param float $numero
*
* @return Payment
*/
public function setNumero(?int $numero): Payment
{
$this->numero = $numero;
return $this;
}
y usarlo así:
$payment = (new Payment())
->setMoneda($currency)
->setFecha($date_payment)
->setImporte($amount)
->setNumero($r->payment_number);
De modo que se utilice el número de pago a la factura que se haya determinado en el sistema.
Con eso conseguí esto:
sac:SUNATRetentionDocumentReference
<cbc:ID schemeID="01">FE01-2234</cbc:ID>
cbc:IssueDate2025-11-02</cbc:IssueDate>
<cbc:TotalInvoiceAmount currencyID="PEN">66.00</cbc:TotalInvoiceAmount>
cac:Payment
cbc:ID1</cbc:ID>
<cbc:PaidAmount currencyID="PEN">30.00</cbc:PaidAmount>
cbc:PaidDate2025-11-01</cbc:PaidDate>
</cac:Payment>
cac:Payment
cbc:ID2</cbc:ID>
<cbc:PaidAmount currencyID="PEN">36.00</cbc:PaidAmount>
cbc:PaidDate2025-11-01</cbc:PaidDate>
</cac:Payment>
sac:SUNATRetentionInformation
<sac:SUNATRetentionAmount currencyID="PEN">1.98</sac:SUNATRetentionAmount>
sac:SUNATRetentionDate2025-11-09</sac:SUNATRetentionDate>
<sac:SUNATNetTotalPaid currencyID="PEN">64.02</sac:SUNATNetTotalPaid>
</sac:SUNATRetentionInformation>
</sac:SUNATRetentionDocumentReference>
y se aceptó el xml
Activity
jmaxb2691 commentedon Nov 11, 2025
Olvidé agregar que cambié el template para agregar el nuevo método.
giansalex commentedon Nov 13, 2025
Si solo se acepta un Payment por detalle, los pagos lo agregas en varios detalles?
jmaxb2691 commentedon Nov 13, 2025
Así es. Tal y como está aquí:
fix(perception): validar un cobro por detalle y número de cobro (#258)
fix(retention/perception): número de pago configurable y un pago por …