diff --git a/app/Models/BaseModel.php b/app/Models/BaseModel.php index 024fb684e5..6698ba6df3 100644 --- a/app/Models/BaseModel.php +++ b/app/Models/BaseModel.php @@ -330,8 +330,13 @@ class BaseModel extends Model } // special catch here for einvoicing eventing - if ($event_id == Webhook::EVENT_SENT_INVOICE && ($this instanceof Invoice) && $this->backup->guid == "" && $this->client->peppolSendingEnabled()) { - \App\Services\EDocument\Jobs\SendEDocument::dispatch(get_class($this), $this->id, $this->company->db); + if ($event_id == Webhook::EVENT_SENT_INVOICE && ($this instanceof Invoice) && $this->backup->guid == "") { + if($this->client->peppolSendingEnabled()) { + \App\Services\EDocument\Jobs\SendEDocument::dispatch(get_class($this), $this->id, $this->company->db); + } + elseif($this->company->verifactuEnabled()) { + $this->service()->sendVerifactu(); + } } } diff --git a/app/Models/Project.php b/app/Models/Project.php index 99dbccefc8..3e1bcee373 100644 --- a/app/Models/Project.php +++ b/app/Models/Project.php @@ -1,4 +1,13 @@ $invoices * @property-read \Illuminate\Database\Eloquent\Collection $quotes * @property-read \Illuminate\Database\Eloquent\Collection $expenses - * * @mixin \Eloquent */ class Project extends BaseModel @@ -134,17 +142,17 @@ class Project extends BaseModel return $this->hasMany(Task::class); } - public function expenses(): HasMany + public function expenses(): \Illuminate\Database\Eloquent\Relations\HasMany { return $this->hasMany(Expense::class); } - public function invoices(): HasMany + public function invoices(): \Illuminate\Database\Eloquent\Relations\HasMany { return $this->hasMany(Invoice::class)->withTrashed(); } - public function quotes(): HasMany + public function quotes(): \Illuminate\Database\Eloquent\Relations\HasMany { return $this->hasMany(Quote::class); } diff --git a/app/Services/EDocument/Standards/Verifactu.php b/app/Services/EDocument/Standards/Verifactu.php index 2b58f4f7ca..78c3799a23 100644 --- a/app/Services/EDocument/Standards/Verifactu.php +++ b/app/Services/EDocument/Standards/Verifactu.php @@ -192,7 +192,7 @@ class Verifactu extends AbstractService $response = $this->aeat_client->send($soapXml); - if($response['success']){ + if($response['success'] || $response['status'] == 'ParcialmenteCorrecto'){ $this->writeLog($response); } diff --git a/app/Services/EDocument/Standards/Verifactu/AeatClient.php b/app/Services/EDocument/Standards/Verifactu/AeatClient.php index 2e460b5dcc..7fa34aa943 100644 --- a/app/Services/EDocument/Standards/Verifactu/AeatClient.php +++ b/app/Services/EDocument/Standards/Verifactu/AeatClient.php @@ -35,6 +35,10 @@ class AeatClient $this->certificate = $this->certificate ?? config('services.verifactu.certificate'); $this->ssl_key = $this->ssl_key ?? config('services.verifactu.ssl_key'); + if(config('services.verifactu.test_mode')) { + $this->setTestMode(); + } + return $this; } diff --git a/app/Services/EDocument/Standards/Verifactu/SendToAeat.php b/app/Services/EDocument/Standards/Verifactu/SendToAeat.php index b6fd53ebf5..fafb741e89 100644 --- a/app/Services/EDocument/Standards/Verifactu/SendToAeat.php +++ b/app/Services/EDocument/Standards/Verifactu/SendToAeat.php @@ -44,7 +44,6 @@ class SendToAeat implements ShouldQueue /** * Modification Invoices - (modify) - * - If Amount > 0 - We generates a F3 document which replaces the original invoice. And becomes the new invoice. * - If Amount < 0 - We generate a R2 document which is a negative modification on the original invoice. * Create Invoices - (create) Generates a F1 document. * Cancellation Invoices - (cancel) Generates a R3 document with full negative values of the original invoice. @@ -111,7 +110,12 @@ class SendToAeat implements ShouldQueue nlog($response); - $this->writeActivity($invoice, $response['success'] ? Activity::VERIFACTU_INVOICE_SENT : Activity::VERIFACTU_INVOICE_SENT_FAILURE, $response['message']); + $message = ''; + if (isset($response['errors'][0]['message'])) { + $message = $response['errors'][0]['message']; + } + + $this->writeActivity($invoice, $response['success'] ? Activity::VERIFACTU_INVOICE_SENT : Activity::VERIFACTU_INVOICE_SENT_FAILURE, $message); $this->systemLog($invoice, $response, $response['success'] ? SystemLog::EVENT_VERIFACTU_SUCCESS : SystemLog::EVENT_VERIFACTU_FAILURE, SystemLog::TYPE_VERIFACTU_INVOICE); } @@ -120,6 +124,7 @@ class SendToAeat implements ShouldQueue { $verifactu = new Verifactu($invoice); + $document = (new RegistroAlta($invoice))->run()->getInvoice(); $last_hash = $invoice->company->verifactu_logs()->first(); @@ -127,25 +132,36 @@ class SendToAeat implements ShouldQueue $huella = $this->cancellationHash($document, $last_hash->hash); $cancellation = $document->createCancellation(); + $cancellation->setHuella($huella); $soapXml = $cancellation->toSoapEnvelope(); - $response = $verifactu->send($soapXml); + $response = $verifactu->setInvoice($document) + ->setHuella($huella) + ->setPreviousHash($last_hash->hash) + ->send($soapXml); nlog($response); + + $message = ''; if($response['success']) { - //if successful, we need to pop this invoice from the child array of the parent invoice! + //if successful, we need to pop this invoice from the child array of the parent invoice! $parent = Invoice::withTrashed()->find($invoice->backup->parent_invoice_id); - if($parent) { - $parent->backup->child_invoice_ids = $parent->backup->child_invoice_ids->reject(fn($id) => $id === $invoice->hashed_id); - $parent->saveQuietly(); - } + + if($parent) { + $parent->backup->child_invoice_ids = $parent->backup->child_invoice_ids->reject(fn($id) => $id === $invoice->hashed_id); + $parent->saveQuietly(); + } + } + + if(isset($response['errors'][0]['message'])){ + $message = $response['errors'][0]['message']; } //@todo - verifactu logging - $this->writeActivity($invoice, $response['success'] ? Activity::VERIFACTU_CANCELLATION_SENT : Activity::VERIFACTU_CANCELLATION_SENT_FAILURE, $response['message']); + $this->writeActivity($invoice, $response['success'] ? Activity::VERIFACTU_CANCELLATION_SENT : Activity::VERIFACTU_CANCELLATION_SENT_FAILURE, $message); $this->systemLog($invoice, $response, $response['success'] ? SystemLog::EVENT_VERIFACTU_SUCCESS : SystemLog::EVENT_VERIFACTU_FAILURE, SystemLog::TYPE_VERIFACTU_CANCELLATION); } diff --git a/app/Services/Invoice/HandleCancellation.php b/app/Services/Invoice/HandleCancellation.php index 02cff4bc60..fc7ce3f360 100644 --- a/app/Services/Invoice/HandleCancellation.php +++ b/app/Services/Invoice/HandleCancellation.php @@ -17,11 +17,13 @@ use App\Repositories\InvoiceRepository; use App\Services\AbstractService; use App\Utils\Ninja; use App\Utils\Traits\GeneratesCounter; +use App\Utils\Traits\MakesHash; use stdClass; class HandleCancellation extends AbstractService { use GeneratesCounter; + use MakesHash; public function __construct(private Invoice $invoice, private ?string $reason = null) { diff --git a/app/Services/Invoice/InvoiceService.php b/app/Services/Invoice/InvoiceService.php index 55723af146..5e036778a9 100644 --- a/app/Services/Invoice/InvoiceService.php +++ b/app/Services/Invoice/InvoiceService.php @@ -678,7 +678,6 @@ class InvoiceService } - /** * sendVerifactu * diff --git a/app/Services/Invoice/TriggeredActions.php b/app/Services/Invoice/TriggeredActions.php index 2dfb4e4492..87244351ab 100644 --- a/app/Services/Invoice/TriggeredActions.php +++ b/app/Services/Invoice/TriggeredActions.php @@ -81,8 +81,13 @@ class TriggeredActions extends AbstractService $company->save(); } - if($this->request->has('retry_e_send') && $this->request->input('retry_e_send') == 'true' && strlen($this->invoice->backup->guid ?? '') == 0 && $this->invoice->client->peppolSendingEnabled()) { - \App\Services\EDocument\Jobs\SendEDocument::dispatch(get_class($this->invoice), $this->invoice->id, $this->invoice->company->db); + if($this->request->has('retry_e_send') && $this->request->input('retry_e_send') == 'true' && strlen($this->invoice->backup->guid ?? '') == 0) { + if($this->invoice->client->peppolSendingEnabled()) { + \App\Services\EDocument\Jobs\SendEDocument::dispatch(get_class($this->invoice), $this->invoice->id, $this->invoice->company->db); + } + elseif($this->invoice->company->verifactuEnabled()) { + $this->invoice->service()->sendVerifactu(); + } } if($this->request->has('redirect')) { diff --git a/composer.lock b/composer.lock index 8e9c736dc2..56a134c563 100644 --- a/composer.lock +++ b/composer.lock @@ -5168,16 +5168,16 @@ }, { "name": "laravel/framework", - "version": "v11.45.1", + "version": "v11.45.2", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "b09ba32795b8e71df10856a2694706663984a239" + "reference": "d134bf11e2208c0c5bd488cf19e612ca176b820a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/b09ba32795b8e71df10856a2694706663984a239", - "reference": "b09ba32795b8e71df10856a2694706663984a239", + "url": "https://api.github.com/repos/laravel/framework/zipball/d134bf11e2208c0c5bd488cf19e612ca176b820a", + "reference": "d134bf11e2208c0c5bd488cf19e612ca176b820a", "shasum": "" }, "require": { @@ -5285,7 +5285,7 @@ "league/flysystem-read-only": "^3.25.1", "league/flysystem-sftp-v3": "^3.25.1", "mockery/mockery": "^1.6.10", - "orchestra/testbench-core": "^9.13.2", + "orchestra/testbench-core": "^9.16.0", "pda/pheanstalk": "^5.0.6", "php-http/discovery": "^1.15", "phpstan/phpstan": "^2.0", @@ -5379,7 +5379,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-06-03T14:01:40+00:00" + "time": "2025-08-13T20:28:00+00:00" }, { "name": "laravel/octane", @@ -8238,16 +8238,16 @@ }, { "name": "nikic/php-parser", - "version": "v5.6.0", + "version": "v5.6.1", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "221b0d0fdf1369c71047ad1d18bb5880017bbc56" + "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/221b0d0fdf1369c71047ad1d18bb5880017bbc56", - "reference": "221b0d0fdf1369c71047ad1d18bb5880017bbc56", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", + "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", "shasum": "" }, "require": { @@ -8266,7 +8266,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-master": "5.x-dev" } }, "autoload": { @@ -8290,9 +8290,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.1" }, - "time": "2025-07-27T20:03:57+00:00" + "time": "2025-08-13T20:13:15+00:00" }, { "name": "nordigen/nordigen-php", @@ -8675,16 +8675,16 @@ }, { "name": "open-telemetry/context", - "version": "1.3.0", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/context.git", - "reference": "4d5d98f1d4311a55b8d07e3d4c06d2430b4e6efc" + "reference": "438f71812242db3f196fb4c717c6f92cbc819be6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/context/zipball/4d5d98f1d4311a55b8d07e3d4c06d2430b4e6efc", - "reference": "4d5d98f1d4311a55b8d07e3d4c06d2430b4e6efc", + "url": "https://api.github.com/repos/opentelemetry-php/context/zipball/438f71812242db3f196fb4c717c6f92cbc819be6", + "reference": "438f71812242db3f196fb4c717c6f92cbc819be6", "shasum": "" }, "require": { @@ -8730,7 +8730,7 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2025-08-04T03:25:06+00:00" + "time": "2025-08-13T01:12:00+00:00" }, { "name": "paragonie/constant_time_encoding", @@ -20559,23 +20559,23 @@ }, { "name": "sebastian/recursion-context", - "version": "6.0.2", + "version": "6.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "694d156164372abbd149a4b85ccda2e4670c0e16" + "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/694d156164372abbd149a4b85ccda2e4670c0e16", - "reference": "694d156164372abbd149a4b85ccda2e4670c0e16", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/f6458abbf32a6c8174f8f26261475dc133b3d9dc", + "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc", "shasum": "" }, "require": { "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^11.0" + "phpunit/phpunit": "^11.3" }, "type": "library", "extra": { @@ -20611,15 +20611,27 @@ "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.2" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.3" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" } ], - "time": "2024-07-03T05:10:34+00:00" + "time": "2025-08-13T04:42:22+00:00" }, { "name": "sebastian/type", diff --git a/config/services.php b/config/services.php index d5e176535e..d88b9a9fb0 100644 --- a/config/services.php +++ b/config/services.php @@ -151,5 +151,6 @@ return [ 'certificate' => env('VERIFACTU_CERTIFICATE', ''), 'ssl_key' => env('VERIFACTU_SSL_KEY', ''), 'sender_name' => env('VERIFACTU_SENDER_NAME', 'CERTIFICADO FISICA PRUEBAS'), + 'test_mode' => env('VERIFACTU_TEST_MODE', false), ], ]; \ No newline at end of file