From 8fe73e9be97e5fde9c3ffbb2d05c18ba2875cc01 Mon Sep 17 00:00:00 2001 From: Lars Kusch Date: Sun, 10 Mar 2024 08:03:38 +0100 Subject: [PATCH 01/23] Refactor e-invoicing to allow more types for quotes, po, ... --- .../{Invoice => EInvoice}/CreateEInvoice.php | 7 ++++--- app/Livewire/PdfSlot.php | 2 +- .../Standards}/FacturaEInvoice.php | 2 +- .../Standards}/FatturaPA.php | 2 +- .../Standards}/RoEInvoice.php | 12 ++++++------ .../Standards}/ZugferdEInvoice.php | 2 +- app/Services/Invoice/InvoiceService.php | 18 +++++++++--------- app/Services/Pdf/PdfService.php | 2 +- tests/Feature/EInvoice/FacturaeTest.php | 5 ++--- tests/Feature/EInvoice/FatturaPATest.php | 4 ++-- tests/Unit/EInvoiceTest.php | 2 +- 11 files changed, 29 insertions(+), 29 deletions(-) rename app/Jobs/{Invoice => EInvoice}/CreateEInvoice.php (93%) rename app/Services/{Invoice/EInvoice => EInvoicing/Standards}/FacturaEInvoice.php (99%) rename app/Services/{Invoice/EInvoice => EInvoicing/Standards}/FatturaPA.php (99%) rename app/Services/{Invoice/EInvoice => EInvoicing/Standards}/RoEInvoice.php (99%) rename app/Services/{Invoice/EInvoice => EInvoicing/Standards}/ZugferdEInvoice.php (99%) diff --git a/app/Jobs/Invoice/CreateEInvoice.php b/app/Jobs/EInvoice/CreateEInvoice.php similarity index 93% rename from app/Jobs/Invoice/CreateEInvoice.php rename to app/Jobs/EInvoice/CreateEInvoice.php index 0ea7ae8a96d8..204bb9e036b2 100644 --- a/app/Jobs/Invoice/CreateEInvoice.php +++ b/app/Jobs/EInvoice/CreateEInvoice.php @@ -9,11 +9,11 @@ * @license https://www.elastic.co/licensing/elastic-license */ -namespace App\Jobs\Invoice; +namespace App\Jobs\EInvoice; use App\Models\Invoice; -use App\Services\Invoice\EInvoice\FacturaEInvoice; -use App\Services\Invoice\EInvoice\ZugferdEInvoice; +use App\Services\EInvoicing\Standards\FacturaEInvoice; +use App\Services\EInvoicing\Standards\ZugferdEInvoice; use App\Utils\Ninja; use horstoeko\zugferd\ZugferdDocumentBuilder; use Illuminate\Bus\Queueable; @@ -22,6 +22,7 @@ use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\App; +use function App\Jobs\Invoice\app; class CreateEInvoice implements ShouldQueue { diff --git a/app/Livewire/PdfSlot.php b/app/Livewire/PdfSlot.php index 1322d6a9664f..b81d5d9d3e4c 100644 --- a/app/Livewire/PdfSlot.php +++ b/app/Livewire/PdfSlot.php @@ -12,7 +12,7 @@ namespace App\Livewire; -use App\Jobs\Invoice\CreateEInvoice; +use App\Jobs\EInvoice\CreateEInvoice; use App\Libraries\MultiDB; use App\Models\CreditInvitation; use App\Models\InvoiceInvitation; diff --git a/app/Services/Invoice/EInvoice/FacturaEInvoice.php b/app/Services/EInvoicing/Standards/FacturaEInvoice.php similarity index 99% rename from app/Services/Invoice/EInvoice/FacturaEInvoice.php rename to app/Services/EInvoicing/Standards/FacturaEInvoice.php index e4b7f28bd905..623fe724d172 100644 --- a/app/Services/Invoice/EInvoice/FacturaEInvoice.php +++ b/app/Services/EInvoicing/Standards/FacturaEInvoice.php @@ -9,7 +9,7 @@ * @license https://www.elastic.co/licensing/elastic-license */ -namespace App\Services\Invoice\EInvoice; +namespace App\Services\EInvoicing\Standards; use App\Models\Invoice; use App\Models\PaymentType; diff --git a/app/Services/Invoice/EInvoice/FatturaPA.php b/app/Services/EInvoicing/Standards/FatturaPA.php similarity index 99% rename from app/Services/Invoice/EInvoice/FatturaPA.php rename to app/Services/EInvoicing/Standards/FatturaPA.php index 2c448c7cfa1b..7b40e9c8b33f 100644 --- a/app/Services/Invoice/EInvoice/FatturaPA.php +++ b/app/Services/EInvoicing/Standards/FatturaPA.php @@ -9,7 +9,7 @@ * @license https://www.elastic.co/licensing/elastic-license */ -namespace App\Services\Invoice\EInvoice; +namespace App\Services\EInvoicing\Standards; use App\Models\Invoice; use App\Services\AbstractService; diff --git a/app/Services/Invoice/EInvoice/RoEInvoice.php b/app/Services/EInvoicing/Standards/RoEInvoice.php similarity index 99% rename from app/Services/Invoice/EInvoice/RoEInvoice.php rename to app/Services/EInvoicing/Standards/RoEInvoice.php index d01aae492022..4a8ce29969e9 100644 --- a/app/Services/Invoice/EInvoice/RoEInvoice.php +++ b/app/Services/EInvoicing/Standards/RoEInvoice.php @@ -9,28 +9,28 @@ * @license https://www.elastic.co/licensing/elastic-license */ -namespace App\Services\Invoice\EInvoice; +namespace App\Services\EInvoicing\Standards; use App\Models\Invoice; use App\Services\AbstractService; use CleverIt\UBL\Invoice\Address; +use CleverIt\UBL\Invoice\ClassifiedTaxCategory; use CleverIt\UBL\Invoice\Contact; use CleverIt\UBL\Invoice\Country; use CleverIt\UBL\Invoice\Generator; use CleverIt\UBL\Invoice\Invoice as UBLInvoice; use CleverIt\UBL\Invoice\InvoiceLine; use CleverIt\UBL\Invoice\Item; +use CleverIt\UBL\Invoice\LegalEntity; use CleverIt\UBL\Invoice\LegalMonetaryTotal; use CleverIt\UBL\Invoice\Party; +use CleverIt\UBL\Invoice\PayeeFinancialAccount; +use CleverIt\UBL\Invoice\PaymentMeans; +use CleverIt\UBL\Invoice\Price; use CleverIt\UBL\Invoice\TaxCategory; use CleverIt\UBL\Invoice\TaxScheme; use CleverIt\UBL\Invoice\TaxSubTotal; use CleverIt\UBL\Invoice\TaxTotal; -use CleverIt\UBL\Invoice\PaymentMeans; -use CleverIt\UBL\Invoice\PayeeFinancialAccount; -use CleverIt\UBL\Invoice\LegalEntity; -use CleverIt\UBL\Invoice\ClassifiedTaxCategory; -use CleverIt\UBL\Invoice\Price; class RoEInvoice extends AbstractService { diff --git a/app/Services/Invoice/EInvoice/ZugferdEInvoice.php b/app/Services/EInvoicing/Standards/ZugferdEInvoice.php similarity index 99% rename from app/Services/Invoice/EInvoice/ZugferdEInvoice.php rename to app/Services/EInvoicing/Standards/ZugferdEInvoice.php index 59ab32605a58..c08cbe7d1fb7 100644 --- a/app/Services/Invoice/EInvoice/ZugferdEInvoice.php +++ b/app/Services/EInvoicing/Standards/ZugferdEInvoice.php @@ -9,7 +9,7 @@ * @license https://www.elastic.co/licensing/elastic-license */ -namespace App\Services\Invoice\EInvoice; +namespace App\Services\EInvoicing\Standards; use App\Models\Invoice; use App\Models\Product; diff --git a/app/Services/Invoice/InvoiceService.php b/app/Services/Invoice/InvoiceService.php index effe34daceba..5d13c90c473b 100644 --- a/app/Services/Invoice/InvoiceService.php +++ b/app/Services/Invoice/InvoiceService.php @@ -11,21 +11,21 @@ namespace App\Services\Invoice; -use App\Models\Task; -use App\Utils\Ninja; +use App\Events\Invoice\InvoiceWasArchived; +use App\Jobs\EInvoice\CreateEInvoice; +use App\Jobs\Entity\CreateRawPdf; +use App\Jobs\Inventory\AdjustProductInventory; +use App\Libraries\Currency\Conversion\CurrencyApi; +use App\Models\CompanyGateway; use App\Models\Expense; use App\Models\Invoice; use App\Models\Payment; use App\Models\Subscription; -use App\Models\CompanyGateway; -use Illuminate\Support\Carbon; +use App\Models\Task; +use App\Utils\Ninja; use App\Utils\Traits\MakesHash; -use App\Jobs\Entity\CreateRawPdf; -use App\Jobs\Invoice\CreateEInvoice; +use Illuminate\Support\Carbon; use Illuminate\Support\Facades\Storage; -use App\Events\Invoice\InvoiceWasArchived; -use App\Jobs\Inventory\AdjustProductInventory; -use App\Libraries\Currency\Conversion\CurrencyApi; class InvoiceService { diff --git a/app/Services/Pdf/PdfService.php b/app/Services/Pdf/PdfService.php index e0db7d406419..81162798c0e5 100644 --- a/app/Services/Pdf/PdfService.php +++ b/app/Services/Pdf/PdfService.php @@ -11,7 +11,7 @@ namespace App\Services\Pdf; -use App\Jobs\Invoice\CreateEInvoice; +use App\Jobs\EInvoice\CreateEInvoice; use App\Models\Company; use App\Models\CreditInvitation; use App\Models\Invoice; diff --git a/tests/Feature/EInvoice/FacturaeTest.php b/tests/Feature/EInvoice/FacturaeTest.php index 6909a79fc2a6..d070ab388e6d 100644 --- a/tests/Feature/EInvoice/FacturaeTest.php +++ b/tests/Feature/EInvoice/FacturaeTest.php @@ -11,7 +11,6 @@ namespace Tests\Feature\EInvoice; -use App\Services\Invoice\EInvoice\FacturaEInvoice; use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Routing\Middleware\ThrottleRequests; use Illuminate\Support\Facades\Storage; @@ -40,11 +39,11 @@ class FacturaeTest extends TestCase public function testInvoiceGeneration() { - $f = new FacturaEInvoice($this->invoice, "3.2.2"); + $f = new \App\Services\EInvoicing\Standards\FacturaEInvoice($this->invoice, "3.2.2"); $path = $f->run(); $this->assertNotNull($f->run()); - + // nlog($f->run()); // $this->assertTrue($this->validateInvoiceXML($path)); diff --git a/tests/Feature/EInvoice/FatturaPATest.php b/tests/Feature/EInvoice/FatturaPATest.php index dfe243f55823..f6c63d0653ec 100644 --- a/tests/Feature/EInvoice/FatturaPATest.php +++ b/tests/Feature/EInvoice/FatturaPATest.php @@ -11,7 +11,7 @@ namespace Tests\Feature\EInvoice; -use App\Services\Invoice\EInvoice\FatturaPA; +use App\Services\EInvoicing\Standards\FatturaPA; use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Routing\Middleware\ThrottleRequests; use Tests\MockAccountData; @@ -42,7 +42,7 @@ class FatturaPATest extends TestCase $xml = $fat->run(); // nlog($xml); - + $this->assertnotNull($xml); } } diff --git a/tests/Unit/EInvoiceTest.php b/tests/Unit/EInvoiceTest.php index 7f0925d1cdff..4b8977dc50e2 100644 --- a/tests/Unit/EInvoiceTest.php +++ b/tests/Unit/EInvoiceTest.php @@ -9,8 +9,8 @@ * @license https://www.elastic.co/licensing/elastic-license */ +use App\Jobs\EInvoice\CreateEInvoice; use App\Jobs\Entity\CreateRawPdf; -use App\Jobs\Invoice\CreateEInvoice; use horstoeko\zugferd\ZugferdDocumentReader; use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Routing\Middleware\ThrottleRequests; From 99d6ac5a7044772aabac7d89195a067c507f1f0e Mon Sep 17 00:00:00 2001 From: Lars Kusch Date: Sun, 10 Mar 2024 08:13:34 +0100 Subject: [PATCH 02/23] Rename some files, added additional checks for more file types in CreateEDocument.php --- app/Jobs/EDocument/CreateEDocument.php | 130 ++++++++++++++++++ app/Jobs/EInvoice/CreateEInvoice.php | 87 ------------ app/Livewire/PdfSlot.php | 4 +- .../Standards/FacturaEInvoice.php | 0 .../Standards/FatturaPA.php | 0 .../Standards/RoEInvoice.php | 0 .../Standards/ZugferdEDokument.php} | 2 +- app/Services/Invoice/InvoiceService.php | 4 +- app/Services/Pdf/PdfService.php | 4 +- tests/Unit/EInvoiceTest.php | 6 +- 10 files changed, 140 insertions(+), 97 deletions(-) create mode 100644 app/Jobs/EDocument/CreateEDocument.php delete mode 100644 app/Jobs/EInvoice/CreateEInvoice.php rename app/Services/{EInvoicing => EDocument}/Standards/FacturaEInvoice.php (100%) rename app/Services/{EInvoicing => EDocument}/Standards/FatturaPA.php (100%) rename app/Services/{EInvoicing => EDocument}/Standards/RoEInvoice.php (100%) rename app/Services/{EInvoicing/Standards/ZugferdEInvoice.php => EDocument/Standards/ZugferdEDokument.php} (99%) diff --git a/app/Jobs/EDocument/CreateEDocument.php b/app/Jobs/EDocument/CreateEDocument.php new file mode 100644 index 000000000000..07e967c0b399 --- /dev/null +++ b/app/Jobs/EDocument/CreateEDocument.php @@ -0,0 +1,130 @@ +document->client->locale()); + + /* Set customized translations _NOW_ */ + $t->replace(Ninja::transformTranslations($this->document->client->getMergedSettings())); + + $e_document_type = $this->document->client->getSetting('e_invoice_type'); + if ($this->document instanceof Invoice){ + switch ($e_document_type) { + case "EN16931": + case "XInvoice_3_0": + case "XInvoice_2_3": + case "XInvoice_2_2": + case "XInvoice_2_1": + case "XInvoice_2_0": + case "XInvoice_1_0": + case "XInvoice-Extended": + case "XInvoice-BasicWL": + case "XInvoice-Basic": + $zugferd = (new ZugferdEDokument($this->invoice))->run(); + + return $this->returnObject ? $zugferd->xrechnung : $zugferd->getXml(); + 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: + + $zugferd = (new ZugferdEDokument($this->invoice))->run(); + + return $this->returnObject ? $zugferd : $zugferd->getXml(); + + } + } + elseif ($this->document instanceof Quote){ + switch ($e_document_type){ + case "EN16931": + case "XInvoice_3_0": + case "XInvoice_2_3": + case "XInvoice_2_2": + case "XInvoice_2_1": + case "XInvoice_2_0": + case "XInvoice_1_0": + case "XInvoice-Extended": + case "XInvoice-BasicWL": + case "XInvoice-Basic": + $zugferd = (new ZugferdEDokument($this->invoice))->run(); + return $this->returnObject ? $zugferd->xrechnung : $zugferd->getXml(); + default: + $zugferd = (new ZugferdEDokument($this->invoice))->run(); + return $this->returnObject ? $zugferd : $zugferd->getXml(); + } + } + elseif ($this->document instanceof PurchaseOrder){ + switch ($e_document_type){ + case "EN16931": + case "XInvoice_3_0": + case "XInvoice_2_3": + case "XInvoice_2_2": + case "XInvoice_2_1": + case "XInvoice_2_0": + case "XInvoice_1_0": + case "XInvoice-Extended": + case "XInvoice-BasicWL": + case "XInvoice-Basic": + $zugferd = (new ZugferdEDokument($this->invoice))->run(); + return $this->returnObject ? $zugferd->xrechnung : $zugferd->getXml(); + default: + $zugferd = (new ZugferdEDokument($this->invoice))->run(); + return $this->returnObject ? $zugferd : $zugferd->getXml(); + } + } + else{ + return ""; + } + } +} diff --git a/app/Jobs/EInvoice/CreateEInvoice.php b/app/Jobs/EInvoice/CreateEInvoice.php deleted file mode 100644 index 204bb9e036b2..000000000000 --- a/app/Jobs/EInvoice/CreateEInvoice.php +++ /dev/null @@ -1,87 +0,0 @@ -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_3_0": - case "XInvoice_2_3": - case "XInvoice_2_2": - case "XInvoice_2_1": - case "XInvoice_2_0": - case "XInvoice_1_0": - case "XInvoice-Extended": - case "XInvoice-BasicWL": - case "XInvoice-Basic": - $zugferd = (new ZugferdEInvoice($this->invoice))->run(); - - return $this->returnObject ? $zugferd->xrechnung : $zugferd->getXml(); - 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: - - $zugferd = (new ZugferdEInvoice($this->invoice))->run(); - - return $this->returnObject ? $zugferd : $zugferd->getXml(); - - } - - } -} diff --git a/app/Livewire/PdfSlot.php b/app/Livewire/PdfSlot.php index b81d5d9d3e4c..81ad1fab61c1 100644 --- a/app/Livewire/PdfSlot.php +++ b/app/Livewire/PdfSlot.php @@ -12,7 +12,7 @@ namespace App\Livewire; -use App\Jobs\EInvoice\CreateEInvoice; +use App\Jobs\EDocument\CreateEDocument; use App\Libraries\MultiDB; use App\Models\CreditInvitation; use App\Models\InvoiceInvitation; @@ -113,7 +113,7 @@ class PdfSlot extends Component $file_name = $this->entity->numberFormatter().'.xml'; - $file = (new CreateEInvoice($this->entity))->handle(); + $file = (new CreateEDocument($this->entity))->handle(); $headers = ['Content-Type' => 'application/xml']; diff --git a/app/Services/EInvoicing/Standards/FacturaEInvoice.php b/app/Services/EDocument/Standards/FacturaEInvoice.php similarity index 100% rename from app/Services/EInvoicing/Standards/FacturaEInvoice.php rename to app/Services/EDocument/Standards/FacturaEInvoice.php diff --git a/app/Services/EInvoicing/Standards/FatturaPA.php b/app/Services/EDocument/Standards/FatturaPA.php similarity index 100% rename from app/Services/EInvoicing/Standards/FatturaPA.php rename to app/Services/EDocument/Standards/FatturaPA.php diff --git a/app/Services/EInvoicing/Standards/RoEInvoice.php b/app/Services/EDocument/Standards/RoEInvoice.php similarity index 100% rename from app/Services/EInvoicing/Standards/RoEInvoice.php rename to app/Services/EDocument/Standards/RoEInvoice.php diff --git a/app/Services/EInvoicing/Standards/ZugferdEInvoice.php b/app/Services/EDocument/Standards/ZugferdEDokument.php similarity index 99% rename from app/Services/EInvoicing/Standards/ZugferdEInvoice.php rename to app/Services/EDocument/Standards/ZugferdEDokument.php index c08cbe7d1fb7..ce295c74d3e7 100644 --- a/app/Services/EInvoicing/Standards/ZugferdEInvoice.php +++ b/app/Services/EDocument/Standards/ZugferdEDokument.php @@ -18,7 +18,7 @@ use horstoeko\zugferd\codelists\ZugferdDutyTaxFeeCategories; use horstoeko\zugferd\ZugferdDocumentBuilder; use horstoeko\zugferd\ZugferdProfiles; -class ZugferdEInvoice extends AbstractService +class ZugferdEDokument extends AbstractService { public ZugferdDocumentBuilder $xrechnung; diff --git a/app/Services/Invoice/InvoiceService.php b/app/Services/Invoice/InvoiceService.php index 5d13c90c473b..c6adb7457b2c 100644 --- a/app/Services/Invoice/InvoiceService.php +++ b/app/Services/Invoice/InvoiceService.php @@ -12,7 +12,7 @@ namespace App\Services\Invoice; use App\Events\Invoice\InvoiceWasArchived; -use App\Jobs\EInvoice\CreateEInvoice; +use App\Jobs\EDocument\CreateEDocument; use App\Jobs\Entity\CreateRawPdf; use App\Jobs\Inventory\AdjustProductInventory; use App\Libraries\Currency\Conversion\CurrencyApi; @@ -201,7 +201,7 @@ class InvoiceService public function getEInvoice($contact = null) { - return (new CreateEInvoice($this->invoice))->handle(); + return (new CreateEDocument($this->invoice))->handle(); } public function sendEmail($contact = null) diff --git a/app/Services/Pdf/PdfService.php b/app/Services/Pdf/PdfService.php index 81162798c0e5..4a579db80c52 100644 --- a/app/Services/Pdf/PdfService.php +++ b/app/Services/Pdf/PdfService.php @@ -11,7 +11,7 @@ namespace App\Services\Pdf; -use App\Jobs\EInvoice\CreateEInvoice; +use App\Jobs\EDocument\CreateEDocument; use App\Models\Company; use App\Models\CreditInvitation; use App\Models\Invoice; @@ -216,7 +216,7 @@ class PdfService { try { - $e_rechnung = (new CreateEInvoice($this->config->entity, true))->handle(); + $e_rechnung = (new CreateEDocument($this->config->entity, true))->handle(); $pdfBuilder = new ZugferdDocumentPdfBuilder($e_rechnung, $pdf); $pdfBuilder->generateDocument(); diff --git a/tests/Unit/EInvoiceTest.php b/tests/Unit/EInvoiceTest.php index 4b8977dc50e2..08c8edf34c91 100644 --- a/tests/Unit/EInvoiceTest.php +++ b/tests/Unit/EInvoiceTest.php @@ -9,7 +9,7 @@ * @license https://www.elastic.co/licensing/elastic-license */ -use App\Jobs\EInvoice\CreateEInvoice; +use App\Jobs\EDocument\CreateEDocument; use App\Jobs\Entity\CreateRawPdf; use horstoeko\zugferd\ZugferdDocumentReader; use Illuminate\Foundation\Testing\DatabaseTransactions; @@ -41,7 +41,7 @@ class EInvoiceTest extends TestCase $this->company->e_invoice_type = "EN16931"; $this->invoice->client->routing_id = 'DE123456789'; $this->invoice->client->save(); - $e_invoice = (new CreateEInvoice($this->invoice))->handle(); + $e_invoice = (new CreateEDocument($this->invoice))->handle(); $this->assertIsString($e_invoice); } @@ -54,7 +54,7 @@ class EInvoiceTest extends TestCase $this->invoice->client->routing_id = 'DE123456789'; $this->invoice->client->save(); - $e_invoice = (new CreateEInvoice($this->invoice))->handle(); + $e_invoice = (new CreateEDocument($this->invoice))->handle(); $document = ZugferdDocumentReader::readAndGuessFromContent($e_invoice); $document->getDocumentInformation($documentno, $documenttypecode, $documentdate, $documentcurrency, $taxcurrency, $taxname, $documentlangeuage, $rest); $this->assertEquals($this->invoice->number, $documentno); From 8c066989807a4fe08dd0367176a468ef35c7f47d Mon Sep 17 00:00:00 2001 From: Lars Kusch Date: Sun, 10 Mar 2024 15:23:52 +0100 Subject: [PATCH 03/23] Add new functions to Quote and PO-Service --- app/Models/Client.php | 2 +- app/Services/Invoice/InvoiceService.php | 4 +-- .../PurchaseOrder/PurchaseOrderService.php | 28 +++++++++++++++++++ app/Services/Quote/QuoteService.php | 27 ++++++++++++++++++ 4 files changed, 58 insertions(+), 3 deletions(-) diff --git a/app/Models/Client.php b/app/Models/Client.php index 61bda7230946..259b9c03b97b 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -754,7 +754,7 @@ class Client extends BaseModel implements HasLocalePreference return $this->company->company_key.'/'.$this->client_hash.'/'.$contact_key.'/invoices/'; } - public function e_invoice_filepath($invitation): string + public function e_document_filepath($invitation): string { $contact_key = $invitation->contact->contact_key; diff --git a/app/Services/Invoice/InvoiceService.php b/app/Services/Invoice/InvoiceService.php index c6adb7457b2c..34676eaca1cf 100644 --- a/app/Services/Invoice/InvoiceService.php +++ b/app/Services/Invoice/InvoiceService.php @@ -409,12 +409,12 @@ class InvoiceService $this->invoice->invitations->each(function ($invitation) { try { // if (Storage::disk(config('filesystems.default'))->exists($this->invoice->client->e_invoice_filepath($invitation).$this->invoice->getFileName("xml"))) { - Storage::disk(config('filesystems.default'))->delete($this->invoice->client->e_invoice_filepath($invitation).$this->invoice->getFileName("xml")); + Storage::disk(config('filesystems.default'))->delete($this->invoice->client->e_document_filepath($invitation).$this->invoice->getFileName("xml")); // } // if (Ninja::isHosted() && Storage::disk('public')->exists($this->invoice->client->e_invoice_filepath($invitation).$this->invoice->getFileName("xml"))) { if (Ninja::isHosted()) { - Storage::disk('public')->delete($this->invoice->client->e_invoice_filepath($invitation).$this->invoice->getFileName("xml")); + Storage::disk('public')->delete($this->invoice->client->e_document_filepath($invitation).$this->invoice->getFileName("xml")); } } catch (\Exception $e) { nlog($e->getMessage()); diff --git a/app/Services/PurchaseOrder/PurchaseOrderService.php b/app/Services/PurchaseOrder/PurchaseOrderService.php index b7bc07cb2c77..126b0c99c16d 100644 --- a/app/Services/PurchaseOrder/PurchaseOrderService.php +++ b/app/Services/PurchaseOrder/PurchaseOrderService.php @@ -11,7 +11,9 @@ namespace App\Services\PurchaseOrder; +use App\Jobs\EDocument\CreateEDocument; use App\Models\PurchaseOrder; +use App\Utils\Ninja; use App\Utils\Traits\MakesHash; class PurchaseOrderService @@ -75,6 +77,32 @@ class PurchaseOrderService return (new GetPurchaseOrderPdf($this->purchase_order, $contact))->run(); } + public function getEPurchaseOrder($contact = null) + { + return (new CreateEDocument($this->purchase_order))->handle(); + } + public function deleteEPurchaseOrder() + { + $this->purchase_order->load('invitations'); + + $this->purchase_order->invitations->each(function ($invitation) { + try { + // if (Storage::disk(config('filesystems.default'))->exists($this->invoice->client->e_invoice_filepath($invitation).$this->invoice->getFileName("xml"))) { + Storage::disk(config('filesystems.default'))->delete($this->purchase_order->client->e_document_filepath($invitation).$this->purchase_order->getFileName("xml")); + // } + + // if (Ninja::isHosted() && Storage::disk('public')->exists($this->invoice->client->e_invoice_filepath($invitation).$this->invoice->getFileName("xml"))) { + if (Ninja::isHosted()) { + Storage::disk('public')->delete($this->purchase_order->client->e_document_filepath($invitation).$this->purchase_order->getFileName("xml")); + } + } catch (\Exception $e) { + nlog($e->getMessage()); + } + }); + + return $this; + } + public function setStatus($status) { $this->purchase_order->status_id = $status; diff --git a/app/Services/Quote/QuoteService.php b/app/Services/Quote/QuoteService.php index ddfebbe00b0d..f2ee47666a67 100644 --- a/app/Services/Quote/QuoteService.php +++ b/app/Services/Quote/QuoteService.php @@ -13,6 +13,7 @@ namespace App\Services\Quote; use App\Events\Quote\QuoteWasApproved; use App\Exceptions\QuoteConversion; +use App\Jobs\EDocument\CreateEDocument; use App\Models\Project; use App\Models\Quote; use App\Repositories\QuoteRepository; @@ -72,6 +73,11 @@ class QuoteService return (new GetQuotePdf($this->quote, $contact))->run(); } + public function getEQuote($contact = null) + { + return (new CreateEDocument($this->quote))->handle(); + } + public function sendEmail($contact = null): self { $send_email = new SendEmail($this->quote, null, $contact); @@ -226,6 +232,27 @@ class QuoteService return $this; } + public function deleteEQuote() + { + $this->quote->load('invitations'); + + $this->quote->invitations->each(function ($invitation) { + try { + // if (Storage::disk(config('filesystems.default'))->exists($this->invoice->client->e_invoice_filepath($invitation).$this->invoice->getFileName("xml"))) { + Storage::disk(config('filesystems.default'))->delete($this->quote->client->e_document_filepath($invitation).$this->quote->getFileName("xml")); + // } + + // if (Ninja::isHosted() && Storage::disk('public')->exists($this->invoice->client->e_invoice_filepath($invitation).$this->invoice->getFileName("xml"))) { + if (Ninja::isHosted()) { + Storage::disk('public')->delete($this->quote->client->e_document_filepath($invitation).$this->quote->getFileName("xml")); + } + } catch (\Exception $e) { + nlog($e->getMessage()); + } + }); + + return $this; + } /** * Saves the quote. From da273d4ed59b7400c97dc06ee0c573473e96dd2d Mon Sep 17 00:00:00 2001 From: Lars Kusch Date: Sun, 10 Mar 2024 15:27:37 +0100 Subject: [PATCH 04/23] Add E-Document to ZIP-Download --- app/Jobs/PurchaseOrder/ZipPurchaseOrders.php | 5 +++++ app/Jobs/Quote/ZipQuotes.php | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/app/Jobs/PurchaseOrder/ZipPurchaseOrders.php b/app/Jobs/PurchaseOrder/ZipPurchaseOrders.php index 76a91d6beed3..1fddc7262d0c 100644 --- a/app/Jobs/PurchaseOrder/ZipPurchaseOrders.php +++ b/app/Jobs/PurchaseOrder/ZipPurchaseOrders.php @@ -67,6 +67,11 @@ class ZipPurchaseOrders implements ShouldQueue try { foreach ($invitations as $invitation) { + if ($invitation->purchase_order->client->getSetting('enable_e_invoice')) { + $xml = $invitation->purchase_order->service()->getEInvoice(); + $zipFile->addFromString($invitation->purchase_order->getFileName("xml"), $xml); + } + $file = (new CreateRawPdf($invitation))->handle(); $zipFile->addFromString($invitation->purchase_order->numberFormatter().".pdf", $file); diff --git a/app/Jobs/Quote/ZipQuotes.php b/app/Jobs/Quote/ZipQuotes.php index a39c7615a4ce..fad437718993 100644 --- a/app/Jobs/Quote/ZipQuotes.php +++ b/app/Jobs/Quote/ZipQuotes.php @@ -63,6 +63,10 @@ class ZipQuotes implements ShouldQueue try { foreach ($invitations as $invitation) { + if ($invitation->quote->client->getSetting('enable_e_invoice')) { + $xml = $invitation->quote->service()->getEInvoice(); + $zipFile->addFromString($invitation->quote->getFileName("xml"), $xml); + } $file = (new \App\Jobs\Entity\CreateRawPdf($invitation))->handle(); $zipFile->addFromString($invitation->quote->numberFormatter() . '.pdf', $file); } From 99a211b823fda724da1bbf182423fed5102469bb Mon Sep 17 00:00:00 2001 From: Lars Kusch Date: Sun, 10 Mar 2024 15:40:25 +0100 Subject: [PATCH 05/23] Added new routes and adapted controllers --- app/Http/Controllers/CreditController.php | 68 ++++++++++++++++++++++ app/Http/Controllers/QuoteController.php | 69 +++++++++++++++++++++++ app/Services/Credit/CreditService.php | 27 +++++++++ routes/client.php | 6 +- 4 files changed, 168 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/CreditController.php b/app/Http/Controllers/CreditController.php index 3eedd8fe2ffc..e0db0722c133 100644 --- a/app/Http/Controllers/CreditController.php +++ b/app/Http/Controllers/CreditController.php @@ -727,6 +727,74 @@ class CreditController extends BaseController }, $credit->numberFormatter() . '.pdf', $headers); } + /** + * @OA\Get( + * path="/api/v1/credit/{invitation_key}/download_e_credit", + * operationId="downloadXcredit", + * tags={"credit"}, + * summary="Download a specific x-credit by invitation key", + * description="Downloads a specific x-credit", + * @OA\Parameter(ref="#/components/parameters/X-API-TOKEN"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Parameter(ref="#/components/parameters/include"), + * @OA\Parameter( + * name="invitation_key", + * in="path", + * description="The credit Invitation Key", + * example="D2J234DFA", + * required=true, + * @OA\Schema( + * type="string", + * format="string", + * ), + * ), + * @OA\Response( + * response=200, + * description="Returns the x-credit pdf", + * @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"), + * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), + * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), + * ), + * @OA\Response( + * response=422, + * description="Validation error", + * @OA\JsonContent(ref="#/components/schemas/ValidationError"), + * + * ), + * @OA\Response( + * response="default", + * description="Unexpected Error", + * @OA\JsonContent(ref="#/components/schemas/Error"), + * ), + * ) + * @param $invitation_key + * @return \Symfony\Component\HttpFoundation\BinaryFileResponse + */ + public function downloadECredit($invitation_key) + { + $invitation = $this->credit_repository->getInvitationByKey($invitation_key); + + if (! $invitation) { + return response()->json(['message' => 'no record found'], 400); + } + + $contact = $invitation->contact; + $credit = $invitation->credit; + + $file = $credit->service()->getEInvoice($contact); + $file_name = $credit->getFileName("xml"); + + $headers = ['Content-Type' => 'application/xml']; + + if (request()->input('inline') == 'true') { + $headers = array_merge($headers, ['Content-Disposition' => 'inline']); + } + + return response()->streamDownload(function () use ($file) { + echo $file; + }, $file_name, $headers); + } + /** * Update the specified resource in storage. diff --git a/app/Http/Controllers/QuoteController.php b/app/Http/Controllers/QuoteController.php index 78a5bf625247..1ac67895f009 100644 --- a/app/Http/Controllers/QuoteController.php +++ b/app/Http/Controllers/QuoteController.php @@ -860,6 +860,75 @@ class QuoteController extends BaseController } + /** + * @OA\Get( + * path="/api/v1/invoice/{invitation_key}/download_e_quote", + * operationId="downloadXQuote", + * tags={"quotes"}, + * summary="Download a specific x-quote by invitation key", + * description="Downloads a specific x-quote", + * @OA\Parameter(ref="#/components/parameters/X-API-TOKEN"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Parameter(ref="#/components/parameters/include"), + * @OA\Parameter( + * name="invitation_key", + * in="path", + * description="The Quote Invitation Key", + * example="D2J234DFA", + * required=true, + * @OA\Schema( + * type="string", + * format="string", + * ), + * ), + * @OA\Response( + * response=200, + * description="Returns the x-quote pdf", + * @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"), + * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), + * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), + * ), + * @OA\Response( + * response=422, + * description="Validation error", + * @OA\JsonContent(ref="#/components/schemas/ValidationError"), + * + * ), + * @OA\Response( + * response="default", + * description="Unexpected Error", + * @OA\JsonContent(ref="#/components/schemas/Error"), + * ), + * ) + * @param $invitation_key + * @return \Symfony\Component\HttpFoundation\BinaryFileResponse + */ + public function downloadEQuote($invitation_key) + { + $invitation = $this->quote_repo->getInvitationByKey($invitation_key); + + if (! $invitation) { + return response()->json(['message' => 'no record found'], 400); + } + + $contact = $invitation->contact; + $quote = $invitation->quote; + + $file = $quote->service()->getEInvoice($contact); + $file_name = $quote->getFileName("xml"); + + $headers = ['Content-Type' => 'application/xml']; + + if (request()->input('inline') == 'true') { + $headers = array_merge($headers, ['Content-Disposition' => 'inline']); + } + + return response()->streamDownload(function () use ($file) { + echo $file; + }, $file_name, $headers); + } + + /** * Update the specified resource in storage. * diff --git a/app/Services/Credit/CreditService.php b/app/Services/Credit/CreditService.php index deb2f3dffe0b..77409f913e37 100644 --- a/app/Services/Credit/CreditService.php +++ b/app/Services/Credit/CreditService.php @@ -12,6 +12,7 @@ namespace App\Services\Credit; use App\Factory\PaymentFactory; +use App\Jobs\EDocument\CreateEDocument; use App\Models\Credit; use App\Models\Payment; use App\Models\PaymentType; @@ -37,6 +38,11 @@ class CreditService return (new GetCreditPdf($invitation))->run(); } + public function getECredit($contact = null) + { + return (new CreateEDocument($this->credit))->handle(); + } + /** * Applies the invoice number. * @return $this InvoiceService object @@ -232,6 +238,27 @@ class CreditService return $this; } + public function deleteECredit() + { + $this->credit->load('invitations'); + + $this->credit->invitations->each(function ($invitation) { + try { + // if (Storage::disk(config('filesystems.default'))->exists($this->invoice->client->e_invoice_filepath($invitation).$this->invoice->getFileName("xml"))) { + Storage::disk(config('filesystems.default'))->delete($this->credit->client->e_document_filepath($invitation).$this->credit->getFileName("xml")); + // } + + // if (Ninja::isHosted() && Storage::disk('public')->exists($this->invoice->client->e_invoice_filepath($invitation).$this->invoice->getFileName("xml"))) { + if (Ninja::isHosted()) { + Storage::disk('public')->delete($this->credit->client->e_document_filepath($invitation).$this->credit->getFileName("xml")); + } + } catch (\Exception $e) { + nlog($e->getMessage()); + } + }); + + return $this; + } public function triggeredActions($request) { $this->credit = (new TriggeredActions($this->credit, $request))->run(); diff --git a/routes/client.php b/routes/client.php index 0e3fdd729a89..78a7cc50bdbb 100644 --- a/routes/client.php +++ b/routes/client.php @@ -49,7 +49,7 @@ Route::group(['middleware' => ['auth:contact', 'locale', 'domain_db','check_clie Route::get('dashboard', [App\Http\Controllers\ClientPortal\DashboardController::class, 'index'])->name('dashboard'); // name = (dashboard. index / create / show / update / destroy / edit Route::get('plan', [App\Http\Controllers\ClientPortal\NinjaPlanController::class, 'plan'])->name('plan'); // name = (dashboard. index / create / show / update / destroy / edit - + Route::get('showBlob/{hash}', [App\Http\Controllers\ClientPortal\InvoiceController::class, 'showBlob'])->name('invoices.showBlob'); Route::get('invoices', [App\Http\Controllers\ClientPortal\InvoiceController::class, 'index'])->name('invoices.index')->middleware('portal_enabled'); Route::post('invoices/payment', [App\Http\Controllers\ClientPortal\InvoiceController::class, 'bulk'])->name('invoices.bulk'); @@ -131,7 +131,9 @@ Route::group(['middleware' => ['invite_db'], 'prefix' => 'client', 'as' => 'clie Route::get('invoice/{invitation_key}/download_pdf', [InvoiceController::class, 'downloadPdf'])->name('invoice.download_invitation_key')->middleware('token_auth'); Route::get('invoice/{invitation_key}/download_e_invoice', [InvoiceController::class, 'downloadEInvoice'])->name('invoice.download_e_invoice')->middleware('token_auth'); Route::get('quote/{invitation_key}/download_pdf', [QuoteController::class, 'downloadPdf'])->name('quote.download_invitation_key')->middleware('token_auth'); + Route::get('quote/{invitation_key}/download_e_quote', [QuoteController::class, "downloadEQuote"])->name()->name('invoice.download_e_quote')->middleware('token_auth'); Route::get('credit/{invitation_key}/download_pdf', [CreditController::class, 'downloadPdf'])->name('credit.download_invitation_key')->middleware('token_auth'); + Route::get('credit/{invitation_key}/download_e_credit', [CreditController::class, 'downloadECredit'])->name('credit.download_invitation_key')->middleware('token_auth'); Route::get('{entity}/{invitation_key}/download', [App\Http\Controllers\ClientPortal\InvitationController::class, 'routerForDownload'])->middleware('token_auth'); Route::get('pay/{invitation_key}', [App\Http\Controllers\ClientPortal\InvitationController::class, 'payInvoice'])->name('pay.invoice'); @@ -142,7 +144,7 @@ Route::group(['middleware' => ['invite_db'], 'prefix' => 'client', 'as' => 'clie }); Route::get('route/{hash}', function ($hash) { - + return redirect(decrypt($hash)); }); From bcd6148222fefa12f036c6014f27b2b870520595 Mon Sep 17 00:00:00 2001 From: Lars Kusch Date: Sun, 10 Mar 2024 16:02:15 +0100 Subject: [PATCH 06/23] Adjust Zugferd class to new types --- app/Jobs/EDocument/CreateEDocument.php | 28 ++-- .../EDocument/Standards/ZugferdEDokument.php | 137 ++++++++++-------- 2 files changed, 98 insertions(+), 67 deletions(-) diff --git a/app/Jobs/EDocument/CreateEDocument.php b/app/Jobs/EDocument/CreateEDocument.php index 07e967c0b399..a30246559979 100644 --- a/app/Jobs/EDocument/CreateEDocument.php +++ b/app/Jobs/EDocument/CreateEDocument.php @@ -11,6 +11,7 @@ namespace App\Jobs\EDocument; +use App\Models\Credit; use App\Models\Invoice; use App\Models\PurchaseOrder; use App\Models\Quote; @@ -70,16 +71,16 @@ class CreateEDocument implements ShouldQueue case "XInvoice-Extended": case "XInvoice-BasicWL": case "XInvoice-Basic": - $zugferd = (new ZugferdEDokument($this->invoice))->run(); + $zugferd = (new ZugferdEDokument($this->document))->run(); - return $this->returnObject ? $zugferd->xrechnung : $zugferd->getXml(); + return $this->returnObject ? $zugferd->xdocument : $zugferd->getXml(); 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(); + return (new FacturaEInvoice($this->document, str_replace("Facturae_", "", $e_document_type)))->run(); default: - $zugferd = (new ZugferdEDokument($this->invoice))->run(); + $zugferd = (new ZugferdEDokument($this->document))->run(); return $this->returnObject ? $zugferd : $zugferd->getXml(); @@ -97,15 +98,22 @@ class CreateEDocument implements ShouldQueue case "XInvoice-Extended": case "XInvoice-BasicWL": case "XInvoice-Basic": - $zugferd = (new ZugferdEDokument($this->invoice))->run(); - return $this->returnObject ? $zugferd->xrechnung : $zugferd->getXml(); + $zugferd = (new ZugferdEDokument($this->document))->run(); + return $this->returnObject ? $zugferd->xdocument : $zugferd->getXml(); default: - $zugferd = (new ZugferdEDokument($this->invoice))->run(); + $zugferd = (new ZugferdEDokument($this->document))->run(); return $this->returnObject ? $zugferd : $zugferd->getXml(); } } elseif ($this->document instanceof PurchaseOrder){ switch ($e_document_type){ + // No supported implementation yet. + default: + return ""; + } + } + elseif ($this->document instanceof Credit) { + switch ($e_document_type) { case "EN16931": case "XInvoice_3_0": case "XInvoice_2_3": @@ -116,10 +124,10 @@ class CreateEDocument implements ShouldQueue case "XInvoice-Extended": case "XInvoice-BasicWL": case "XInvoice-Basic": - $zugferd = (new ZugferdEDokument($this->invoice))->run(); - return $this->returnObject ? $zugferd->xrechnung : $zugferd->getXml(); + $zugferd = (new ZugferdEDokument($this->document))->run(); + return $this->returnObject ? $zugferd->xdocument : $zugferd->getXml(); default: - $zugferd = (new ZugferdEDokument($this->invoice))->run(); + $zugferd = (new ZugferdEDokument($this->document))->run(); return $this->returnObject ? $zugferd : $zugferd->getXml(); } } diff --git a/app/Services/EDocument/Standards/ZugferdEDokument.php b/app/Services/EDocument/Standards/ZugferdEDokument.php index ce295c74d3e7..0d415e73d7c3 100644 --- a/app/Services/EDocument/Standards/ZugferdEDokument.php +++ b/app/Services/EDocument/Standards/ZugferdEDokument.php @@ -11,8 +11,11 @@ namespace App\Services\EInvoicing\Standards; +use App\Models\Credit; use App\Models\Invoice; use App\Models\Product; +use App\Models\PurchaseOrder; +use App\Models\Quote; use App\Services\AbstractService; use horstoeko\zugferd\codelists\ZugferdDutyTaxFeeCategories; use horstoeko\zugferd\ZugferdDocumentBuilder; @@ -20,17 +23,17 @@ use horstoeko\zugferd\ZugferdProfiles; class ZugferdEDokument extends AbstractService { - public ZugferdDocumentBuilder $xrechnung; + public ZugferdDocumentBuilder $xdocument; - public function __construct(public Invoice $invoice, private readonly bool $returnObject = false, private array $tax_map = []) + public function __construct(public object $document, private readonly bool $returnObject = false, private array $tax_map = []) { } public function run(): self { - $company = $this->invoice->company; - $client = $this->invoice->client; + $company = $this->document->company; + $client = $this->document->client; $profile = $client->getSetting('e_invoice_type'); $profile = match ($profile) { @@ -46,123 +49,143 @@ class ZugferdEDokument extends AbstractService default => ZugferdProfiles::PROFILE_EN16931, }; - $this->xrechnung = ZugferdDocumentBuilder::CreateNew($profile); + $this->xdocument = ZugferdDocumentBuilder::CreateNew($profile); - $this->xrechnung - ->setDocumentSupplyChainEvent(date_create($this->invoice->date ?? now()->format('Y-m-d'))) + $this->xdocument + ->setDocumentSupplyChainEvent(date_create($this->document->date ?? now()->format('Y-m-d'))) ->setDocumentSeller($company->getSetting('name')) ->setDocumentSellerAddress($company->getSetting("address1"), $company->getSetting("address2"), "", $company->getSetting("postal_code"), $company->getSetting("city"), $company->country()->iso_3166_2, $company->getSetting("state")) - ->setDocumentSellerContact($this->invoice->user->present()->getFullName(), "", $this->invoice->user->present()->phone(), "", $this->invoice->user->email) + ->setDocumentSellerContact($this->document->user->present()->getFullName(), "", $this->document->user->present()->phone(), "", $this->document->user->email) ->setDocumentBuyer($client->present()->name(), $client->number) ->setDocumentBuyerAddress($client->address1, "", "", $client->postal_code, $client->city, $client->country->iso_3166_2, $client->state) ->setDocumentBuyerContact($client->present()->primary_contact_name(), "", $client->present()->phone(), "", $client->present()->email()) - ->addDocumentPaymentTerm(ctrans("texts.xinvoice_payable", ['payeddue' => date_create($this->invoice->date ?? now()->format('Y-m-d'))->diff(date_create($this->invoice->due_date ?? now()->format('Y-m-d')))->format("%d"), 'paydate' => $this->invoice->due_date])); + ->addDocumentPaymentTerm(ctrans("texts.xinvoice_payable", ['payeddue' => date_create($this->document->date ?? now()->format('Y-m-d'))->diff(date_create($this->document->due_date ?? now()->format('Y-m-d')))->format("%d"), 'paydate' => $this->document->due_date])); - if (!empty($this->invoice->public_notes)) { - $this->xrechnung->addDocumentNote($this->invoice->public_notes ?? ''); + if (!empty($this->document->public_notes)) { + $this->xdocument->addDocumentNote($this->document->public_notes ?? ''); } - if (empty($this->invoice->number)) { - $this->xrechnung->setDocumentInformation("DRAFT", "380", date_create($this->invoice->date ?? now()->format('Y-m-d')), $client->getCurrencyCode()); - } else { - $this->xrechnung->setDocumentInformation($this->invoice->number, "380", date_create($this->invoice->date ?? now()->format('Y-m-d')), $client->getCurrencyCode()); + // Document type + $document_class = get_class($this->document); + switch ($document_class){ + case Quote::class: + // Probably wrong file code https://github.com/horstoeko/zugferd/blob/master/src/codelists/ZugferdInvoiceType.php + if (empty($this->document->number)) { + $this->xdocument->setDocumentInformation("DRAFT", "84", date_create($this->document->date ?? now()->format('Y-m-d')), $client->getCurrencyCode()); + } else { + $this->xdocument->setDocumentInformation($this->document->number, "84", date_create($this->document->date ?? now()->format('Y-m-d')), $client->getCurrencyCode()); + }; + break; + case Invoice::class: + if (empty($this->document->number)) { + $this->xdocument->setDocumentInformation("DRAFT", "380", date_create($this->document->date ?? now()->format('Y-m-d')), $client->getCurrencyCode()); + } else { + $this->xdocument->setDocumentInformation($this->document->number, "380", date_create($this->document->date ?? now()->format('Y-m-d')), $client->getCurrencyCode()); + } + break; + case Credit::class: + if (empty($this->document->number)) { + $this->xdocument->setDocumentInformation("DRAFT", "389", date_create($this->document->date ?? now()->format('Y-m-d')), $client->getCurrencyCode()); + } else { + $this->xdocument->setDocumentInformation($this->document->number, "389", date_create($this->document->date ?? now()->format('Y-m-d')), $client->getCurrencyCode()); + } } - if (isset($this->invoice->po_number)) { - $this->xrechnung->setDocumentBuyerOrderReferencedDocument($this->invoice->po_number); + if (isset($this->document->po_number)) { + $this->xdocument->setDocumentBuyerOrderReferencedDocument($this->document->po_number); } if (empty($client->routing_id)) { - $this->xrechnung->setDocumentBuyerReference(ctrans("texts.xinvoice_no_buyers_reference")); + $this->xdocument->setDocumentBuyerReference(ctrans("texts.xinvoice_no_buyers_reference")); } else { - $this->xrechnung->setDocumentBuyerReference($client->routing_id); + $this->xdocument->setDocumentBuyerReference($client->routing_id); } if (isset($client->shipping_address1) && $client->shipping_country) { - $this->xrechnung->setDocumentShipToAddress($client->shipping_address1, $client->shipping_address2, "", $client->shipping_postal_code, $client->shipping_city, $client->shipping_country->iso_3166_2, $client->shipping_state); + $this->xdocument->setDocumentShipToAddress($client->shipping_address1, $client->shipping_address2, "", $client->shipping_postal_code, $client->shipping_city, $client->shipping_country->iso_3166_2, $client->shipping_state); } - $this->xrechnung->addDocumentPaymentMean(68, ctrans("texts.xinvoice_online_payment")); + $this->xdocument->addDocumentPaymentMean(68, ctrans("texts.xinvoice_online_payment")); if (str_contains($company->getSetting('vat_number'), "/")) { - $this->xrechnung->addDocumentSellerTaxRegistration("FC", $company->getSetting('vat_number')); + $this->xdocument->addDocumentSellerTaxRegistration("FC", $company->getSetting('vat_number')); } else { - $this->xrechnung->addDocumentSellerTaxRegistration("VA", $company->getSetting('vat_number')); + $this->xdocument->addDocumentSellerTaxRegistration("VA", $company->getSetting('vat_number')); } - $invoicing_data = $this->invoice->calc(); + $invoicing_data = $this->document->calc(); //Create line items and calculate taxes - foreach ($this->invoice->line_items as $index => $item) { + foreach ($this->document->line_items as $index => $item) { /** @var \App\DataMapper\InvoiceItem $item **/ - $this->xrechnung->addNewPosition($index) + $this->xdocument->addNewPosition($index) ->setDocumentPositionGrossPrice($item->gross_line_total) ->setDocumentPositionNetPrice($item->line_total); if (!empty($item->product_key)) { if (!empty($item->notes)) { - $this->xrechnung->setDocumentPositionProductDetails($item->product_key, $item->notes); + $this->xdocument->setDocumentPositionProductDetails($item->product_key, $item->notes); } else { - $this->xrechnung->setDocumentPositionProductDetails($item->product_key); + $this->xdocument->setDocumentPositionProductDetails($item->product_key); } } else { if (!empty($item->notes)) { - $this->xrechnung->setDocumentPositionProductDetails($item->notes); + $this->xdocument->setDocumentPositionProductDetails($item->notes); } else { - $this->xrechnung->setDocumentPositionProductDetails("no product name defined"); + $this->xdocument->setDocumentPositionProductDetails("no product name defined"); } } if (isset($item->task_id)) { - $this->xrechnung->setDocumentPositionQuantity($item->quantity, "HUR"); + $this->xdocument->setDocumentPositionQuantity($item->quantity, "HUR"); } else { - $this->xrechnung->setDocumentPositionQuantity($item->quantity, "H87"); + $this->xdocument->setDocumentPositionQuantity($item->quantity, "H87"); } $linenetamount = $item->line_total; if ($item->discount > 0) { - if ($this->invoice->is_amount_discount) { + if ($this->document->is_amount_discount) { $linenetamount -= $item->discount; } else { $linenetamount -= $linenetamount * ($item->discount / 100); } } - $this->xrechnung->setDocumentPositionLineSummation($linenetamount); + $this->xdocument->setDocumentPositionLineSummation($linenetamount); // According to european law, each line item can only have one tax rate if (!(empty($item->tax_name1) && empty($item->tax_name2) && empty($item->tax_name3))) { $taxtype = $this->getTaxType($item->tax_id); if (!empty($item->tax_name1)) { - $this->xrechnung->addDocumentPositionTax($taxtype, 'VAT', $item->tax_rate1); + $this->xdocument->addDocumentPositionTax($taxtype, 'VAT', $item->tax_rate1); $this->addtoTaxMap($taxtype, $linenetamount, $item->tax_rate1); } elseif (!empty($item->tax_name2)) { - $this->xrechnung->addDocumentPositionTax($taxtype, 'VAT', $item->tax_rate2); + $this->xdocument->addDocumentPositionTax($taxtype, 'VAT', $item->tax_rate2); $this->addtoTaxMap($taxtype, $linenetamount, $item->tax_rate2); } elseif (!empty($item->tax_name3)) { - $this->xrechnung->addDocumentPositionTax($taxtype, 'VAT', $item->tax_rate3); + $this->xdocument->addDocumentPositionTax($taxtype, 'VAT', $item->tax_rate3); $this->addtoTaxMap($taxtype, $linenetamount, $item->tax_rate3); } else { // nlog("Can't add correct tax position"); } } else { - if (!empty($this->invoice->tax_name1)) { - $taxtype = $this->getTaxType($this->invoice->tax_name1); - $this->xrechnung->addDocumentPositionTax($taxtype, 'VAT', $this->invoice->tax_rate1); - $this->addtoTaxMap($taxtype, $linenetamount, $this->invoice->tax_rate1); - } elseif (!empty($this->invoice->tax_name2)) { - $taxtype = $this->getTaxType($this->invoice->tax_name2); - $this->xrechnung->addDocumentPositionTax($taxtype, 'VAT', $this->invoice->tax_rate2); - $this->addtoTaxMap($taxtype, $linenetamount, $this->invoice->tax_rate2); - } elseif (!empty($this->invoice->tax_name3)) { - $taxtype = $this->getTaxType($this->invoice->tax_name3); - $this->xrechnung->addDocumentPositionTax($taxtype, 'VAT', $this->invoice->tax_rate3); - $this->addtoTaxMap($taxtype, $linenetamount, $this->invoice->tax_rate3); + if (!empty($this->document->tax_name1)) { + $taxtype = $this->getTaxType($this->document->tax_name1); + $this->xdocument->addDocumentPositionTax($taxtype, 'VAT', $this->document->tax_rate1); + $this->addtoTaxMap($taxtype, $linenetamount, $this->document->tax_rate1); + } elseif (!empty($this->document->tax_name2)) { + $taxtype = $this->getTaxType($this->document->tax_name2); + $this->xdocument->addDocumentPositionTax($taxtype, 'VAT', $this->document->tax_rate2); + $this->addtoTaxMap($taxtype, $linenetamount, $this->document->tax_rate2); + } elseif (!empty($this->document->tax_name3)) { + $taxtype = $this->getTaxType($this->document->tax_name3); + $this->xdocument->addDocumentPositionTax($taxtype, 'VAT', $this->document->tax_rate3); + $this->addtoTaxMap($taxtype, $linenetamount, $this->document->tax_rate3); } else { $taxtype = ZugferdDutyTaxFeeCategories::ZERO_RATED_GOODS; - $this->xrechnung->addDocumentPositionTax($taxtype, 'VAT', 0); + $this->xdocument->addDocumentPositionTax($taxtype, 'VAT', 0); $this->addtoTaxMap($taxtype, $linenetamount, 0); // nlog("Can't add correct tax position"); } } } - $this->xrechnung->setDocumentSummation($this->invoice->amount, $this->invoice->balance, $invoicing_data->getSubTotal(), $invoicing_data->getTotalSurcharges(), $invoicing_data->getTotalDiscount(), $invoicing_data->getSubTotal(), $invoicing_data->getItemTotalTaxes(), 0.0, $this->invoice->amount - $this->invoice->balance); + $this->xdocument->setDocumentSummation($this->document->amount, $this->document->balance, $invoicing_data->getSubTotal(), $invoicing_data->getTotalSurcharges(), $invoicing_data->getTotalDiscount(), $invoicing_data->getSubTotal(), $invoicing_data->getItemTotalTaxes(), 0.0, $this->document->amount - $this->document->balance); foreach ($this->tax_map as $item) { - $this->xrechnung->addDocumentTax($item["tax_type"], "VAT", $item["net_amount"], $item["tax_rate"] * $item["net_amount"], $item["tax_rate"] * 100); + $this->xdocument->addDocumentTax($item["tax_type"], "VAT", $item["net_amount"], $item["tax_rate"] * $item["net_amount"], $item["tax_rate"] * 100); } // The validity can be checked using https://portal3.gefeg.com/invoice/validation or https://e-rechnung.bayern.de/app/#/upload @@ -178,7 +201,7 @@ class ZugferdEDokument extends AbstractService */ public function getXml(): string { - return $this->xrechnung->getContent(); + return $this->xdocument->getContent(); } private function getTaxType($name): string @@ -204,13 +227,13 @@ class ZugferdEDokument extends AbstractService } $eu_states = ["AT", "BE", "BG", "HR", "CY", "CZ", "DK", "EE", "FI", "FR", "DE", "EL", "GR", "HU", "IE", "IT", "LV", "LT", "LU", "MT", "NL", "PL", "PT", "RO", "SK", "SI", "ES", "SE", "IS", "LI", "NO", "CH"]; if (empty($tax_type)) { - if ((in_array($this->invoice->company->country()->iso_3166_2, $eu_states) && in_array($this->invoice->client->country->iso_3166_2, $eu_states)) && $this->invoice->company->country()->iso_3166_2 != $this->invoice->client->country->iso_3166_2) { + if ((in_array($this->document->company->country()->iso_3166_2, $eu_states) && in_array($this->document->client->country->iso_3166_2, $eu_states)) && $this->document->company->country()->iso_3166_2 != $this->document->client->country->iso_3166_2) { $tax_type = ZugferdDutyTaxFeeCategories::VAT_EXEMPT_FOR_EEA_INTRACOMMUNITY_SUPPLY_OF_GOODS_AND_SERVICES; - } elseif (!in_array($this->invoice->client->country->iso_3166_2, $eu_states)) { + } elseif (!in_array($this->document->client->country->iso_3166_2, $eu_states)) { $tax_type = ZugferdDutyTaxFeeCategories::SERVICE_OUTSIDE_SCOPE_OF_TAX; - } elseif ($this->invoice->client->country->iso_3166_2 == "ES-CN") { + } elseif ($this->document->client->country->iso_3166_2 == "ES-CN") { $tax_type = ZugferdDutyTaxFeeCategories::CANARY_ISLANDS_GENERAL_INDIRECT_TAX; - } elseif (in_array($this->invoice->client->country->iso_3166_2, ["ES-CE", "ES-ML"])) { + } elseif (in_array($this->document->client->country->iso_3166_2, ["ES-CE", "ES-ML"])) { $tax_type = ZugferdDutyTaxFeeCategories::TAX_FOR_PRODUCTION_SERVICES_AND_IMPORTATION_IN_CEUTA_AND_MELILLA; } else { nlog("Unkown tax case for xinvoice"); From cd0884c77085ef97e10c63c05c36dc17b6495f1a Mon Sep 17 00:00:00 2001 From: Lars Kusch Date: Sun, 10 Mar 2024 16:24:28 +0100 Subject: [PATCH 07/23] Added download button in client portal for new types --- .../components/livewire/pdf-slot.blade.php | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/resources/views/portal/ninja2020/components/livewire/pdf-slot.blade.php b/resources/views/portal/ninja2020/components/livewire/pdf-slot.blade.php index 5159e3314c1e..314f69cca8ee 100644 --- a/resources/views/portal/ninja2020/components/livewire/pdf-slot.blade.php +++ b/resources/views/portal/ninja2020/components/livewire/pdf-slot.blade.php @@ -20,6 +20,40 @@ @endif + @if($entity_type == 'credit' && $settings->enable_e_invoice) + + @endif + @if($entity_type == 'quote' && $settings->enable_e_invoice) + + @endif +{{-- Not implemented yet--}} +{{-- @if($entity_type == 'purchase_order' && $settings->enable_e_invoice) + + @endif--}} @if($html_entity_option)