Fixes for verifactu

This commit is contained in:
David Bomba 2025-05-05 10:34:08 +10:00
parent f1b374ee38
commit 6bb576b949
3 changed files with 59 additions and 38 deletions

View File

@ -2,22 +2,23 @@
namespace App\Services\EDocument\Standards; namespace App\Services\EDocument\Standards;
use App\Models\Invoice;
use App\Services\AbstractService; use App\Services\AbstractService;
use App\Services\EDocument\Standards\Verifactu\InvoiceninjaToVerifactuMapper;
use App\Services\EDocument\Standards\Verifactu\VerifactuClient; use App\Services\EDocument\Standards\Verifactu\VerifactuClient;
use App\Services\EDocument\Standards\Verifactu\InvoiceninjaToVerifactuMapper;
class Verifactu extends AbstractService class Verifactu extends AbstractService
{ {
public function __construct(private Invoice $invoice)
{
}
public function run() public function run()
{ {
$client = new VerifactuClient(VerifactuClient::MODE_PROD); $client = new VerifactuClient(VerifactuClient::MODE_PROD);
$mapper = new InvoiceninjaToVerifactuMapper(); $mapper = new InvoiceninjaToVerifactuMapper();
$invoice = null; //TODO fetch actual invoice $regFacAlta = $mapper->mapRegistroFacturacionAlta($this->invoice);
$regFacAlta = $mapper->mapRegistroFacturacionAlta($invoice);
$response = $client->sendRegistroAlta($regFacAlta); $response = $client->sendRegistroAlta($regFacAlta);
var_dump($response); var_dump($response);
} }
} }

View File

@ -10,6 +10,8 @@ use App\DataMapper\Tax\BaseRule;
use App\Services\EDocument\Standards\Verifactu\Types\IDOtro; use App\Services\EDocument\Standards\Verifactu\Types\IDOtro;
use App\Services\EDocument\Standards\Verifactu\Types\Detalle; use App\Services\EDocument\Standards\Verifactu\Types\Detalle;
use App\Services\EDocument\Standards\Verifactu\Types\Desglose; use App\Services\EDocument\Standards\Verifactu\Types\Desglose;
use App\Services\EDocument\Standards\Verifactu\Types\IDFactura;
use App\Services\EDocument\Standards\Verifactu\Types\RegistroAlta;
use App\Services\EDocument\Standards\Verifactu\Types\Destinatarios; use App\Services\EDocument\Standards\Verifactu\Types\Destinatarios;
use App\Services\EDocument\Standards\Verifactu\Types\IDDestinatario; use App\Services\EDocument\Standards\Verifactu\Types\IDDestinatario;
use App\Services\EDocument\Standards\Verifactu\Types\IDFacturaExpedida; use App\Services\EDocument\Standards\Verifactu\Types\IDFacturaExpedida;
@ -63,15 +65,15 @@ class InvoiceninjaToVerifactuMapper
* $registroFacturacionAlta->setTipoRectificativa('S'); // for Substitutive * $registroFacturacionAlta->setTipoRectificativa('S'); // for Substitutive
*/ */
public function mapRegistroFacturacionAlta(Invoice $invoice): RegistroFacturacionAlta // Registration Entry public function mapRegistroFacturacionAlta(Invoice $invoice): RegistroAlta // Registration Entry
{ {
$registroFacturacionAlta = new RegistroFacturacionAlta(); // Registration Entry $registroFacturacionAlta = new RegistroAlta(); // Registration Entry
// Set version // Set version
$registroFacturacionAlta->setIDVersion(''); $registroFacturacionAlta->setIDVersion('1.0');
// Set invoice ID (IDFactura) // Set invoice ID (IDFactura)
$idFactura = new IDFacturaExpedida(); // Issued Invoice ID $idFactura = new IDFactura(); // Issued Invoice ID
$idFactura->setIDEmisorFactura($invoice->company->settings->vat_number); // Invoice Issuer ID $idFactura->setIDEmisorFactura($invoice->company->settings->vat_number); // Invoice Issuer ID
$idFactura->setNumSerieFactura($invoice->number); // Invoice Serial Number $idFactura->setNumSerieFactura($invoice->number); // Invoice Serial Number
$idFactura->setFechaExpedicionFactura(\Carbon\Carbon::parse($invoice->date)->format('d-m-Y')); // Invoice Issue Date $idFactura->setFechaExpedicionFactura(\Carbon\Carbon::parse($invoice->date)->format('d-m-Y')); // Invoice Issue Date
@ -92,7 +94,7 @@ class InvoiceninjaToVerifactuMapper
// $registroFacturacionAlta->setRechazoPrevio('RechazoPrevio::VALUE_N'); // Previous Rejection // $registroFacturacionAlta->setRechazoPrevio('RechazoPrevio::VALUE_N'); // Previous Rejection
// Set invoice type (TipoFactura) // Set invoice type (TipoFactura)
$registroFacturacionAlta->setTipoFactura($this->getInvoiceType()); $registroFacturacionAlta->setTipoFactura($this->getInvoiceType($invoice));
// Delivery Date of the goods or services (we force invoice->date for this.) // Delivery Date of the goods or services (we force invoice->date for this.)
$registroFacturacionAlta->setFechaOperacion(\Carbon\Carbon::parse($invoice->date)->format('d-m-Y')); $registroFacturacionAlta->setFechaOperacion(\Carbon\Carbon::parse($invoice->date)->format('d-m-Y'));
@ -142,7 +144,7 @@ class InvoiceninjaToVerifactuMapper
$registroFacturacionAlta->setTipoHuella('01'); $registroFacturacionAlta->setTipoHuella('01');
// Set generation date (FechaHoraHusoGenRegistro) // Set generation date (FechaHoraHusoGenRegistro)
$registroFacturacionAlta->setFechaHoraHusoGenRegistro(new \DateTime()); //@todo set the timezone to the company locale $registroFacturacionAlta->setFechaHoraHusoGenRegistro(\Carbon\Carbon::now()->format('Y-m-d\TH:i:sP')); //@todo set the timezone to the company locale
$registroFacturacionAlta->setHuella($this->getHash($invoice, $registroFacturacionAlta)); // Digital Fingerprint $registroFacturacionAlta->setHuella($this->getHash($invoice, $registroFacturacionAlta)); // Digital Fingerprint
@ -187,10 +189,10 @@ class InvoiceninjaToVerifactuMapper
* Based on the type of record, the hash will need to be calculated differently. * Based on the type of record, the hash will need to be calculated differently.
* *
* @param Invoice $invoice * @param Invoice $invoice
* @param RegistroFacturacionAlta $registroFacturacionAlta * @param RegistroAlta $registroAlta
* @return string * @return string
*/ */
private function getHash(Invoice $invoice, RegistroFacturacionAlta $registroFacturacionAlta): string private function getHash(Invoice $invoice, RegistroAlta $registroAlta): string
{ {
// $hash = ''; // $hash = '';
// Tipo de factura Invoice type // Tipo de factura Invoice type
@ -207,14 +209,14 @@ class InvoiceninjaToVerifactuMapper
// Serie Invoice series // Serie Invoice series
// Concepto Concept or description of the invoice // Concepto Concept or description of the invoice
$hash = "IDEmisorFactura=" . $registroFacturacionAlta->getIDFactura()->getIDEmisorFactura() . $hash = "IDEmisorFactura=" . $registroAlta->getIDFactura()->getIDEmisorFactura() .
"&NumSerieFactura=" . $registroFacturacionAlta->getIDFactura()->getNumSerieFactura() . "&NumSerieFactura=" . $registroAlta->getIDFactura()->getNumSerieFactura() .
"&FechaExpedicionFactura=" . $registroFacturacionAlta->getIDFactura()->getFechaExpedicionFactura() . "&FechaExpedicionFactura=" . $registroAlta->getIDFactura()->getFechaExpedicionFactura() .
"&TipoFactura=" . $registroFacturacionAlta->getTipoFactura() . "&TipoFactura=" . $registroAlta->getTipoFactura() .
"&CuotaTotal=" . $registroFacturacionAlta->getCuotaTotal() . "&CuotaTotal=" . $registroAlta->getCuotaTotal() .
"&ImporteTotal=" . $registroFacturacionAlta->getImporteTotal() . "&ImporteTotal=" . $registroAlta->getImporteTotal() .
"&Huella=" . $registroFacturacionAlta->getHuella() . // Fingerprint of the previous record "&Huella=" . $registroAlta->getHuella() . // Fingerprint of the previous record
"&FechaHoraHusoGenRegistro=" . $registroFacturacionAlta->getFechaHoraHusoGenRegistro()->format('Y-m-d\TH:i:sP'); "&FechaHoraHusoGenRegistro=" . $registroAlta->getFechaHoraHusoGenRegistro();
$hash = utf8_encode($hash); $hash = utf8_encode($hash);
@ -234,18 +236,18 @@ class InvoiceninjaToVerifactuMapper
* - Huella: Hash of the cancelled invoice * - Huella: Hash of the cancelled invoice
* - FechaHoraHusoGenRegistro: Date and time of record generation * - FechaHoraHusoGenRegistro: Date and time of record generation
*/ */
private function getHashForCancellation(RegistroFacturacionAnulacion $registroAnulacion): string // private function getHashForCancellation(RegistroFacturacionAnulacion $registroAnulacion): string
{ // {
$hash = "IDEmisorFacturaAnulada=" . $registroAnulacion->getIDFactura()->getIDEmisorFactura() . // $hash = "IDEmisorFacturaAnulada=" . $registroAnulacion->getIDFactura()->getIDEmisorFactura() .
"&NumSerieFacturaAnulada=" . $registroAnulacion->getIDFactura()->getNumSerieFactura() . // "&NumSerieFacturaAnulada=" . $registroAnulacion->getIDFactura()->getNumSerieFactura() .
"&FechaExpedicionFacturaAnulada=" . $registroAnulacion->getIDFactura()->getFechaExpedicionFactura() . // "&FechaExpedicionFacturaAnulada=" . $registroAnulacion->getIDFactura()->getFechaExpedicionFactura() .
"&Huella=" . $registroAnulacion->getHuella() . // Hash of the cancelled invoice //@todo, when we init the doc, we need to set this!! // "&Huella=" . $registroAnulacion->getHuella() . // Hash of the cancelled invoice //@todo, when we init the doc, we need to set this!!
"&FechaHoraHusoGenRegistro=" . $registroAnulacion->getFechaHoraHusoGenRegistro()->format('Y-m-d\TH:i:sP'); // "&FechaHoraHusoGenRegistro=" . $registroAnulacion->getFechaHoraHusoGenRegistro();
$hash = utf8_encode($hash); // $hash = utf8_encode($hash);
return strtoupper(hash('sha256', $hash)); // return strtoupper(hash('sha256', $hash));
} // }
/** /**
* getInvoiceType * getInvoiceType
* *

View File

@ -8,19 +8,35 @@ use Symfony\Component\Serializer\Annotation\SerializedName;
* ObligadoEmision represents a required entity with NombreRazon and NIF. * ObligadoEmision represents a required entity with NombreRazon and NIF.
* Extends PersonaFisicaJuridicaES but enforces both properties to be required at construction time. * Extends PersonaFisicaJuridicaES but enforces both properties to be required at construction time.
*/ */
class ObligadoEmision extends PersonaFisicaJuridicaES class ObligadoEmision
{ {
public function __construct() /** @var string */
{ #[SerializedName('sum1:NombreRazon')]
protected $NombreRazon;
}
/** @var string|null */
#[SerializedName('sum1:NIF')]
protected $NIF;
public function setNombreRazon(?string $nombreRazon): self public function setNombreRazon(?string $nombreRazon): self
{ {
if (empty($nombreRazon)) { if (empty($nombreRazon)) {
throw new \InvalidArgumentException('NombreRazon is required for ObligadoEmision'); throw new \InvalidArgumentException('NombreRazon is required for ObligadoEmision');
} }
return parent::setNombreRazon($nombreRazon);
$this->NombreRazon = $nombreRazon;
return $this;
}
public function getNombreRazon(): string
{
return $this->NombreRazon;
}
public function getNIF(): string
{
return $this->NIF;
} }
public function setNIF(string $nif): self public function setNIF(string $nif): self
@ -28,6 +44,8 @@ class ObligadoEmision extends PersonaFisicaJuridicaES
if (empty($nif)) { if (empty($nif)) {
throw new \InvalidArgumentException('NIF is required for ObligadoEmision'); throw new \InvalidArgumentException('NIF is required for ObligadoEmision');
} }
return parent::setNIF($nif);
$this->NIF = $nif;
return $this;
} }
} }