Intercept reason for rectification

This commit is contained in:
David Bomba 2025-10-16 14:27:48 +11:00
parent ef5b692424
commit c0db1de1de
10 changed files with 49 additions and 63 deletions

View File

@ -59,6 +59,7 @@ class InvoiceBackupCast implements CastsAttributes
'child_invoice_ids' => $value->child_invoice_ids->toArray(),
'redirect' => $value->redirect,
'adjustable_amount' => $value->adjustable_amount,
'notes' => $value->notes,
])
];
}

View File

@ -31,6 +31,7 @@ class InvoiceBackup implements Castable
* @param Collection $child_invoice_ids The collection of child invoice IDs
* @param string $redirect The redirect url for the invoice
* @param float $adjustable_amount The adjustable amount for the invoice
* @param string $notes The notes field - can be multi purpose, but general usage for Verifactu cancellation reason
* @return void
*/
public function __construct(
@ -42,6 +43,7 @@ class InvoiceBackup implements Castable
public Collection $child_invoice_ids = new Collection(),
public ?string $redirect = null,
public float $adjustable_amount = 0,
public ?string $notes = null,
) {}
/**
@ -65,6 +67,7 @@ class InvoiceBackup implements Castable
child_invoice_ids: isset($data['child_invoice_ids']) ? collect($data['child_invoice_ids']) : new Collection(),
redirect: $data['redirect'] ?? null,
adjustable_amount: $data['adjustable_amount'] ?? 0,
notes: $data['notes'] ?? null,
);
}

View File

@ -18,7 +18,6 @@ use App\Utils\Traits\MakesHash;
use Illuminate\Validation\Rule;
use App\Utils\Traits\CleanLineItems;
use App\Http\ValidationRules\Project\ValidProjectForClient;
use App\Http\ValidationRules\Invoice\CanGenerateModificationInvoice;
use App\Http\ValidationRules\Invoice\VerifactuAmountCheck;
class StoreInvoiceRequest extends Request

View File

@ -66,8 +66,6 @@ class VerifactuDocumentValidator extends XsltDocumentValidator
// Detect document type to determine which validation to apply
$documentType = $this->detectDocumentType($businessContent);
nlog("Detected document type: " . $documentType);
// For modifications, we need to use a different validation approach
// since the standard XSD doesn't support modification structure
if ($documentType === 'modification') {

View File

@ -190,67 +190,48 @@ class Verifactu extends AbstractService
public function calculateQrCode(VerifactuLog $log)
{
nlog($log->toArray());
try{
$csv = $log->status;
$nif = $log->nif;
$invoiceNumber = $log->invoice_number;
$date = $log->date->format('d-m-Y');
$total = (string)round($log->invoice->amount, 2);
$url = sprintf(
$this->aeat_client->base_qr_url,
urlencode($csv),
urlencode($nif),
urlencode($invoiceNumber),
urlencode($date),
urlencode($total)
);
$csv = $log->status;
$nif = $log->nif;
$invoiceNumber = $log->invoice_number;
$date = $log->date->format('d-m-Y');
$total = (string)round($log->invoice->amount, 2);
$url = sprintf(
$this->aeat_client->base_qr_url,
urlencode($csv),
urlencode($nif),
urlencode($invoiceNumber),
urlencode($date),
urlencode($total)
);
$result = Builder::create()
->writer(new PngWriter())
->data($url)
->encoding(new Encoding('UTF-8'))
->errorCorrectionLevel(ErrorCorrectionLevel::Medium) // AEAT: level M or higher
->size(300) // AEAT minimum recommended: 30x30 mm ≈ 300px @ 254 DPI
->margin(10)
->labelText('VERI*FACTU')
->labelFont(new OpenSans(14))
->build();
$result = Builder::create()
->writer(new PngWriter())
->data($url)
->encoding(new Encoding('UTF-8'))
->errorCorrectionLevel(ErrorCorrectionLevel::Medium) // AEAT: level M or higher
->size(300) // AEAT minimum recommended: 30x30 mm ≈ 300px @ 254 DPI
->margin(10)
->labelText('VERI*FACTU')
->labelFont(new OpenSans(14))
->build();
return $result->getString();
return $result->getString();
} catch (\Exception $e) {
nlog($e->getMessage());
nlog("VERIFACTU ERROR: [qr]" . $e->getMessage());
return '';
}
}
public function send(string $soapXml): array
{
nlog(["sending", $soapXml]);
nlog("VERIFACTU: [send]" . $soapXml);
$response = $this->aeat_client->send($soapXml);
if($response['success'] || $response['status'] == 'ParcialmenteCorrecto'){
// if($this->invoice->backup->document_type == 'F1'){
// $this->invoice->backup->adjustable_amount = $this->registro_alta->calc->getTotal();
// $this->invoice->saveQuietly();
// }
// elseif(in_array($this->invoice->backup->document_type, ['R1','R2'])){
// // $_parent = Invoice::withTrashed()->find($this->decodePrimaryKey($this->invoice->backup->parent_invoice_id));
// // if($_parent){
// // $_parent->backup->adjustable_amount += $this->registro_alta->calc->getTotal();
// // $_parent->saveQuietly();
// // }
// $this->invoice->backup->adjustable_amount = $this->registro_alta->calc->getTotal();
// $this->invoice->saveQuietly();
// //@todo calculate if the invoice has been fully cancelled, if it has tag it as CANCELLED
// }
$this->writeLog($response);
}

View File

@ -782,7 +782,7 @@ class Invoice extends BaseXmlModel implements XmlModelInterface
if (in_array($this->tipoFactura, [self::TIPO_FACTURA_SUSTITUIDA, self::TIPO_FACTURA_RECTIFICATIVA, self::TIPO_FACTURA_RECTIFICATIVA_PARTIAL]) && $this->tipoRectificativa !== null) {
$root->appendChild($this->createElement($doc, 'TipoRectificativa', $this->tipoRectificativa));
}
// 6. FacturasRectificadas (only for R1 invoices)
if (in_array($this->tipoFactura, [self::TIPO_FACTURA_RECTIFICATIVA_PARTIAL, self::TIPO_FACTURA_RECTIFICATIVA]) && $this->facturasRectificadas !== null) {
$facturasRectificadasElement = $this->createElement($doc, 'FacturasRectificadas');

View File

@ -333,9 +333,6 @@ class RegistroAlta
throw new \Exception('Parent invoice not found');
}
nlog("invoice amount: " . $this->invoice->amount);
nlog("parent invoice amount: " . $_i->amount);
if(BcMath::lessThan(abs($this->invoice->amount), $_i->amount)) {
$document_type = 'R1';
}
@ -343,6 +340,9 @@ class RegistroAlta
$this->v_invoice->setTipoFactura($document_type);
$this->v_invoice->setTipoRectificativa('I'); // S for substitutive rectification
if(strlen($this->invoice->backup->notes ?? '') > 0) {
$this->v_invoice->setDescripcionOperacion($this->invoice->backup->notes);
}
// Set up rectified invoice information
$facturasRectificadas = [
[

View File

@ -113,7 +113,8 @@ class HandleCancellation extends AbstractService
$replicated_invoice->backup->parent_invoice_id = $this->invoice->hashed_id;
$replicated_invoice->backup->parent_invoice_number = $this->invoice->number;
$replicated_invoice->backup->document_type = 'R2'; // Full Credit Note Generated for the invoice
$replicated_invoice->backup->notes = $this->reason;
$invoice_repository = new InvoiceRepository();
$replicated_invoice = $invoice_repository->save([], $replicated_invoice);
$replicated_invoice->service()->markSent()->sendVerifactu()->save();

View File

@ -720,9 +720,7 @@ class InvoiceService
*
*/
/** New Invoice - F1 Type */
if(empty($this->invoice->client->vat_number) ||
!in_array($this->invoice->client->country->iso_3166_2, (new \App\DataMapper\Tax\BaseRule())->eu_country_codes)
) {
if(empty($this->invoice->client->vat_number) || !in_array($this->invoice->client->country->iso_3166_2, (new \App\DataMapper\Tax\BaseRule())->eu_country_codes)) {
$this->invoice->backup->guid = 'exempt';
$this->invoice->saveQuietly();
@ -749,6 +747,11 @@ class InvoiceService
}
$modified_invoice->backup->child_invoice_ids->push($this->invoice->hashed_id);
if(isset($invoice_array['reason'])) {
$this->invoice->backup->notes = $invoice_array['reason'];
}
$modified_invoice->save();
$this->markSent();

View File

@ -857,12 +857,12 @@ Motivo de la rectificación: Corrección de base imponible<br/>
Tipo de rectificación: I (Por diferencias)\n
Código seguro de verificación (CSV): {$verifactu_log->status}";
$text = match($this->entity->backup->document_type) {
'F1' => $f1_text,
'R2' => $r2_text,
'R1' => $r1_text,
default => '',
};
$text = match($this->entity->backup->document_type) {
'F1' => $f1_text,
'R1' => $r1_text,
'R2' => $r2_text,
default => '',
};
return "<tr><td>{$text}</td></tr><tr><td><img src=\"data:image/png;base64,{$qr_code}\" alt=\"Verifactu QR Code\"></td></tr>";
}