diff --git a/app/Jobs/Entity/CreateEntityPdf.php b/app/Jobs/Entity/CreateEntityPdf.php index 631bf2d35ef5..6ee8b367f301 100644 --- a/app/Jobs/Entity/CreateEntityPdf.php +++ b/app/Jobs/Entity/CreateEntityPdf.php @@ -12,7 +12,7 @@ namespace App\Jobs\Entity; use App\Exceptions\FilePermissionsFailure; -use App\Jobs\Invoice\CreateXInvoice; +use App\Jobs\Invoice\CreateEInvoice; use App\Libraries\MultiDB; use App\Models\Credit; use App\Models\CreditInvitation; @@ -213,7 +213,7 @@ class CreateEntityPdf implements ShouldQueue } } if ($this->entity_string == "invoice" && $this->company->enable_e_invoice){ - (new CreateXInvoice($this->entity, true))->handle(); + (new CreateEInvoice($this->entity, true))->handle(); } $this->invitation = null; $this->entity = null; diff --git a/app/Jobs/Invoice/CreateXInvoice.php b/app/Jobs/Invoice/CreateEInvoice.php similarity index 60% rename from app/Jobs/Invoice/CreateXInvoice.php rename to app/Jobs/Invoice/CreateEInvoice.php index 0db2523458c5..e3cad18b3bf0 100644 --- a/app/Jobs/Invoice/CreateXInvoice.php +++ b/app/Jobs/Invoice/CreateEInvoice.php @@ -2,23 +2,18 @@ namespace App\Jobs\Invoice; +use App\Utils\Ninja; use App\Models\Invoice; -use App\Models\Product; -use App\Services\Invoice\EInvoice\FacturaEInvoice; -use App\Services\Invoice\EInvoice\ZugferdEInvoice; -use horstoeko\zugferd\codelists\ZugferdDutyTaxFeeCategories; -use horstoeko\zugferd\ZugferdDocumentBuilder; -use horstoeko\zugferd\ZugferdDocumentPdfBuilder; -use horstoeko\zugferd\ZugferdProfiles; use Illuminate\Bus\Queueable; +use Illuminate\Support\Facades\App; +use Illuminate\Queue\SerializesModels; +use Illuminate\Queue\InteractsWithQueue; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; -use Illuminate\Queue\InteractsWithQueue; -use Illuminate\Queue\SerializesModels; -use Illuminate\Support\Facades\Storage; +use App\Services\Invoice\EInvoice\FacturaEInvoice; +use App\Services\Invoice\EInvoice\ZugferdEInvoice; - -class CreateXInvoice implements ShouldQueue +class CreateEInvoice implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; @@ -36,8 +31,20 @@ class CreateXInvoice implements ShouldQueue */ public function handle(): string { + /* Forget the singleton*/ + App::forgetInstance('translator'); - switch ($this->invoice->client->getSetting('e_invoice_type')) { + /* Init a new copy of the translator*/ + $t = app('translator'); + /* Set the locale*/ + App::setLocale($this->invoice->client->locale()); + + /* Set customized translations _NOW_ */ + $t->replace(Ninja::transformTranslations($this->invoice->client->getMergedSettings())); + + $e_invoice_type = $this->invoice->client->getSetting('e_invoice_type'); + + switch ($e_invoice_type) { case "EN16931": case "XInvoice_2_2": case "XInvoice_2_1": @@ -47,12 +54,15 @@ class CreateXInvoice implements ShouldQueue case "XInvoice-BasicWL": case "XInvoice-Basic": return (new ZugferdEInvoice($this->invoice, $this->alterPDF, $this->custom_pdf_path))->run(); - case "Facturae_3_2_2": - return (new FacturaEInvoice($this->invoice))->run(); + case "Facturae_3.2": + case "Facturae_3.2.1": + case "Facturae_3.2.2": + return (new FacturaEInvoice($this->invoice, str_replace("Facturae_", "", $e_invoice_type)))->run(); default: return (new ZugferdEInvoice($this->invoice, $this->alterPDF, $this->custom_pdf_path))->run(); break; } + } } diff --git a/app/Jobs/Invoice/ZipInvoices.php b/app/Jobs/Invoice/ZipInvoices.php index 3eb9bd21823e..22476675c2f4 100644 --- a/app/Jobs/Invoice/ZipInvoices.php +++ b/app/Jobs/Invoice/ZipInvoices.php @@ -79,7 +79,7 @@ class ZipInvoices implements ShouldQueue $this->invoices->each(function ($invoice) { (new CreateEntityPdf($invoice->invitations()->first()))->handle(); if ($this->company->use_xinvoice){ - (new CreateXInvoice($invoice, false))->handle(); + (new CreateEInvoice($invoice, false))->handle(); } }); diff --git a/app/Services/Email/EmailDefaults.php b/app/Services/Email/EmailDefaults.php index 13b8294bb146..0e13943b36c0 100644 --- a/app/Services/Email/EmailDefaults.php +++ b/app/Services/Email/EmailDefaults.php @@ -11,7 +11,7 @@ namespace App\Services\Email; -use App\Jobs\Invoice\CreateXInvoice; +use App\Jobs\Invoice\CreateEInvoice; use App\Services\Invoice\GetInvoiceXInvoice; use App\DataMapper\EmailTemplateDefaults; use App\Jobs\Entity\CreateRawPdf; @@ -301,7 +301,7 @@ class EmailDefaults if ($this->email->email_object->company->enable_e_invoice && $this->email->email_object->entity instanceof Invoice) { $tempfile = tmpfile(); file_put_contents(stream_get_meta_data($tempfile)['uri'], $pdf); - $xinvoice_path = (new CreateXInvoice($this->email->email_object->entity, true, stream_get_meta_data($tempfile)['uri']))->handle(); + $xinvoice_path = (new CreateEInvoice($this->email->email_object->entity, true, stream_get_meta_data($tempfile)['uri']))->handle(); $this->email->email_object->attachments = array_merge($this->email->email_object->attachments, [['file' => base64_encode(file_get_contents(stream_get_meta_data($tempfile)['uri'])), 'name' => $this->email->email_object->entity->numberFormatter().'.pdf']]); $this->email->email_object->attachments = array_merge($this->email->email_object->attachments, [['file' => base64_encode(file_get_contents($xinvoice_path)), 'name' => explode(".", $this->email->email_object->entity->getFileName('xml'))[0]."-xinvoice.xml"]]); } diff --git a/app/Services/Invoice/EInvoice/FacturaEInvoice.php b/app/Services/Invoice/EInvoice/FacturaEInvoice.php index ede0779891d8..7505956c74ab 100644 --- a/app/Services/Invoice/EInvoice/FacturaEInvoice.php +++ b/app/Services/Invoice/EInvoice/FacturaEInvoice.php @@ -12,10 +12,11 @@ namespace App\Services\Invoice\EInvoice; use App\Models\Invoice; -use App\Services\AbstractService; use josemmo\Facturae\Facturae; +use App\Services\AbstractService; use josemmo\Facturae\FacturaeItem; use josemmo\Facturae\FacturaeParty; +use Illuminate\Support\Facades\Storage; class FacturaEInvoice extends AbstractService { @@ -110,15 +111,16 @@ class FacturaEInvoice extends AbstractService // FacturaeCentre::ROLE_B2B_ISSUER Issuer in FACeB2B - public function __construct(public Invoice $invoice) + public function __construct(public Invoice $invoice, private mixed $profile) { } public function run() { + $this->calc = $this->invoice->calc(); - $this->fac = new Facturae(); + $this->fac = new Facturae($this->profile); $this->fac->setNumber('', $this->invoice->number); $this->fac->setIssueDate($this->invoice->date); $this->fac->setPrecision(Facturae::PRECISION_LINE); @@ -129,7 +131,17 @@ class FacturaEInvoice extends AbstractService ->setDiscount() ->setPoNumber(); - return $this->fac->export(); + + $disk = config('filesystems.default'); + + if (!Storage::disk($disk)->exists($this->invoice->client->e_invoice_filepath($this->invoice->invitations->first()))) { + Storage::makeDirectory($this->invoice->client->e_invoice_filepath($this->invoice->invitations->first())); + } + + $this->fac->export(Storage::disk($disk)->path($this->invoice->client->e_invoice_filepath($this->invoice->invitations->first()) . $this->invoice->getFileName("xsig"))); + + return $this->invoice->client->e_invoice_filepath($this->invoice->invitations->first()) . $this->invoice->getFileName("xsig"); + } private function setPoNumber(): self diff --git a/app/Services/Invoice/EInvoice/ZugferdEInvoice.php b/app/Services/Invoice/EInvoice/ZugferdEInvoice.php index 4bebf34032e8..07e7c4cc25f8 100644 --- a/app/Services/Invoice/EInvoice/ZugferdEInvoice.php +++ b/app/Services/Invoice/EInvoice/ZugferdEInvoice.php @@ -78,17 +78,21 @@ class ZugferdEInvoice extends AbstractService ->setDocumentBuyerContact($client->primary_contact()->first()->first_name . " " . $client->primary_contact()->first()->last_name, "", $client->primary_contact()->first()->phone, "", $client->primary_contact()->first()->email) ->setDocumentShipToAddress($client->shipping_address1, $client->shipping_address2, "", $client->shipping_postal_code, $client->shipping_city, $client->shipping_country->iso_3166_2, $client->shipping_state) ->addDocumentPaymentTerm(ctrans("texts.xinvoice_payable", ['payeddue' => date_create($this->invoice->date)->diff(date_create($this->invoice->due_date))->format("%d"), 'paydate' => $this->invoice->due_date])); - if (!empty($this->invoice->public_notes)) { + + if (!empty($this->invoice->public_notes)) { $xrechnung->addDocumentNote($this->invoice->public_notes); } + if (!empty($this->invoice->po_number)) { $xrechnung->setDocumentBuyerOrderReferencedDocument($this->invoice->po_number); } + if (empty($client->routing_id)) { $xrechnung->setDocumentBuyerReference(ctrans("texts.xinvoice_no_buyers_reference")); } else { $xrechnung->setDocumentBuyerReference($client->routing_id); } + $xrechnung->addDocumentPaymentMean(68, ctrans("texts.xinvoice_online_payment")); if (str_contains($company->getSetting('vat_number'), "/")) { @@ -159,6 +163,7 @@ class ZugferdEInvoice extends AbstractService $xrechnung->addDocumentTax($this->getTaxType(""), "VAT", $item["total"] / (explode("%", end($tax))[0] / 100), $item["total"], explode("%", end($tax))[0]); // TODO: Add correct tax type within getTaxType } + if (!empty($globaltax && isset($invoicing_data->getTotalTaxMap()[$globaltax]["name"]))) { $tax = explode(" ", $invoicing_data->getTotalTaxMap()[$globaltax]["name"]); $xrechnung->addDocumentTax($this->getTaxType(""), "VAT", $invoicing_data->getTotalTaxMap()[$globaltax]["total"] / (explode("%", end($tax))[0] / 100), $invoicing_data->getTotalTaxMap()[$globaltax]["total"], explode("%", end($tax))[0]); @@ -166,9 +171,11 @@ class ZugferdEInvoice extends AbstractService } $disk = config('filesystems.default'); - if (!Storage::exists($client->e_invoice_filepath($this->invoice->invitations->first()))) { + + if (!Storage::disk($disk)->exists($client->e_invoice_filepath($this->invoice->invitations->first()))) { Storage::makeDirectory($client->e_invoice_filepath($this->invoice->invitations->first())); } + $xrechnung->writeFile(Storage::disk($disk)->path($client->e_invoice_filepath($this->invoice->invitations->first()) . $this->invoice->getFileName("xml"))); // The validity can be checked using https://portal3.gefeg.com/invoice/validation diff --git a/app/Services/Invoice/GetInvoiceXInvoice.php b/app/Services/Invoice/GetInvoiceXInvoice.php index 99881cabc0b5..9fcde4017a1e 100644 --- a/app/Services/Invoice/GetInvoiceXInvoice.php +++ b/app/Services/Invoice/GetInvoiceXInvoice.php @@ -11,7 +11,7 @@ namespace App\Services\Invoice; -use App\Jobs\Invoice\CreateXInvoice; +use App\Jobs\Invoice\CreateEInvoice; use App\Models\ClientContact; use App\Models\Invoice; use App\Services\AbstractService; @@ -43,7 +43,7 @@ class GetInvoiceXInvoice extends AbstractService $file = Storage::disk($disk)->exists($file_path); if (! $file) { - $file_path = (new CreateXInvoice($this->invoice, false))->handle(); + $file_path = (new CreateEInvoice($this->invoice, false))->handle(); } return $file_path; diff --git a/app/Services/Invoice/InvoiceService.php b/app/Services/Invoice/InvoiceService.php index e7bf0d28375d..82693c3a634c 100644 --- a/app/Services/Invoice/InvoiceService.php +++ b/app/Services/Invoice/InvoiceService.php @@ -14,7 +14,7 @@ namespace App\Services\Invoice; use App\Events\Invoice\InvoiceWasArchived; use App\Jobs\Entity\CreateEntityPdf; use App\Jobs\Inventory\AdjustProductInventory; -use App\Jobs\Invoice\CreateXInvoice; +use App\Jobs\Invoice\CreateEInvoice; use App\Libraries\Currency\Conversion\CurrencyApi; use App\Models\CompanyGateway; use App\Models\Expense; @@ -451,7 +451,7 @@ class InvoiceService (new CreateEntityPdf($invitation))->handle(); if ($invitation instanceof InvoiceInvitation) { - (new CreateXInvoice($invitation->invoice, true))->handle(); + (new CreateEInvoice($invitation->invoice, true))->handle(); } }); @@ -462,7 +462,7 @@ class InvoiceService CreateEntityPdf::dispatch($invitation); if ($invitation instanceof InvoiceInvitation) { - CreateXInvoice::dispatch($invitation->invoice, true); + CreateEInvoice::dispatch($invitation->invoice, true); } }); } catch (\Exception $e) { diff --git a/tests/Feature/EInvoice/FacturaeTest.php b/tests/Feature/EInvoice/FacturaeTest.php index e0002ffba486..9f0f393e0398 100644 --- a/tests/Feature/EInvoice/FacturaeTest.php +++ b/tests/Feature/EInvoice/FacturaeTest.php @@ -40,7 +40,7 @@ class FacturaeTest extends TestCase public function testInvoiceGeneration() { - $f = new FacturaEInvoice($this->invoice); + $f = new FacturaEInvoice($this->invoice, "3.2.2"); $f->run(); $this->assertNotNull($f->run()); diff --git a/tests/Unit/EInvoiceTest.php b/tests/Unit/EInvoiceTest.php index 7f6a3ecb4bd9..6c31974bf3e9 100644 --- a/tests/Unit/EInvoiceTest.php +++ b/tests/Unit/EInvoiceTest.php @@ -12,7 +12,7 @@ use Tests\TestCase; use Tests\MockAccountData; use App\Jobs\Entity\CreateEntityPdf; -use App\Jobs\Invoice\CreateXInvoice; +use App\Jobs\Invoice\CreateEInvoice; use Illuminate\Support\Facades\Storage; use horstoeko\zugferd\ZugferdDocumentReader; use Illuminate\Routing\Middleware\ThrottleRequests; @@ -42,7 +42,7 @@ class EInvoiceTest extends TestCase $this->company->e_invoice_type = "EN16931"; $this->invoice->client->routing_id = 'DE123456789'; $this->invoice->client->save(); - $xinvoice = (new CreateXInvoice($this->invoice, false))->handle(); + $xinvoice = (new CreateEInvoice($this->invoice, false))->handle(); $this->assertNotNull($xinvoice); $this->assertTrue(Storage::exists($xinvoice)); } @@ -56,7 +56,7 @@ class EInvoiceTest extends TestCase $this->invoice->client->routing_id = 'DE123456789'; $this->invoice->client->save(); - $xinvoice = (new CreateXInvoice($this->invoice, false))->handle(); + $xinvoice = (new CreateEInvoice($this->invoice, false))->handle(); nlog(Storage::path($xinvoice)); $document = ZugferdDocumentReader::readAndGuessFromFile(Storage::path($xinvoice)); $document->getDocumentInformation($documentno, $documenttypecode, $documentdate, $documentcurrency, $taxcurrency, $taxname, $documentlangeuage, $rest); @@ -69,7 +69,7 @@ class EInvoiceTest extends TestCase public function checkEmbededPDFFile() { $pdf = (new CreateEntityPdf($this->invoice->invitations()->first()))->handle(); - (new CreateXInvoice($this->invoice, true, $pdf))->handle(); + (new CreateEInvoice($this->invoice, true, $pdf))->handle(); $document = ZugferdDocumentReader::readAndGuessFromFile($pdf); $document->getDocumentInformation($documentno, $documenttypecode, $documentdate, $documentcurrency, $taxcurrency, $taxname, $documentlangeuage, $rest); $this->assertEquals($this->invoice->number, $documentno);