From ab45e86b47782bcef23bf376142e4e4b3d43c5b9 Mon Sep 17 00:00:00 2001 From: Lars Kusch Date: Wed, 15 May 2024 15:56:05 +0200 Subject: [PATCH 01/11] Added more required fields to e-invoice --- app/Services/EDocument/Standards/ZugferdEDokument.php | 11 ++++++++++- lang/en/texts.php | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/app/Services/EDocument/Standards/ZugferdEDokument.php b/app/Services/EDocument/Standards/ZugferdEDokument.php index 412825109496..6a0489e85faf 100644 --- a/app/Services/EDocument/Standards/ZugferdEDokument.php +++ b/app/Services/EDocument/Standards/ZugferdEDokument.php @@ -113,6 +113,10 @@ class ZugferdEDokument extends AbstractService $this->xdocument->addDocumentSellerTaxRegistration("FC", $company->getSetting('vat_number')); } else { $this->xdocument->addDocumentSellerTaxRegistration("VA", $company->getSetting('vat_number')); + + } + if (!empty($client->vat_number)){ + $this->xdocument->addDocumentBuyerTaxRegistration("VA", $client->vat_number); } $invoicing_data = $this->document->calc(); @@ -149,7 +153,7 @@ class ZugferdEDokument extends AbstractService $linenetamount -= $linenetamount * ($item->discount / 100); } } - $this->xdocument->setDocumentPositionLineSummation($linenetamount); + $this->xdocument->setDocumentPositionLineSummation($linenetamount, $item->line_total-$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); @@ -190,7 +194,12 @@ class ZugferdEDokument extends AbstractService $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) { + if ($item["tax_type"] == ZugferdDutyTaxFeeCategories::VAT_EXEMPT_FOR_EEA_INTRACOMMUNITY_SUPPLY_OF_GOODS_AND_SERVICES){ + $this->xdocument->addDocumentTax($item["tax_type"], "VAT", $item["net_amount"], $item["tax_rate"] * $item["net_amount"], $item["tax_rate"] * 100, ctrans('texts.intracommunity_suply')); + } else { $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 diff --git a/lang/en/texts.php b/lang/en/texts.php index 63654d484928..366d14bef7f1 100644 --- a/lang/en/texts.php +++ b/lang/en/texts.php @@ -5104,6 +5104,7 @@ $lang = array( 'triangular_tax_info' => 'Intra-community triangular transaction', 'intracommunity_tax_info' => 'Tax-free intra-community delivery', 'reverse_tax_info' => 'Please note that this supply is subject to reverse charge', + 'intracommunity_suply'=>'Intra-community supply', 'currency_nicaraguan_cordoba' => 'Nicaraguan Córdoba', 'public' => 'Public', 'private' => 'Private', From 45c3e42bff556932160fe145b01c92078fc3c766 Mon Sep 17 00:00:00 2001 From: Lars Kusch Date: Wed, 15 May 2024 16:28:36 +0200 Subject: [PATCH 02/11] More fixes to intra-community-delivery --- .../EDocument/Standards/ZugferdEDokument.php | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/app/Services/EDocument/Standards/ZugferdEDokument.php b/app/Services/EDocument/Standards/ZugferdEDokument.php index 6a0489e85faf..121de4b253c6 100644 --- a/app/Services/EDocument/Standards/ZugferdEDokument.php +++ b/app/Services/EDocument/Standards/ZugferdEDokument.php @@ -125,7 +125,7 @@ class ZugferdEDokument extends AbstractService foreach ($this->document->line_items as $index => $item) { /** @var \App\DataMapper\InvoiceItem $item **/ $this->xdocument->addNewPosition($index) - ->setDocumentPositionGrossPrice($item->gross_line_total) + ->setDocumentPositionGrossPrice($item->gross_line_total+$item->discount) ->setDocumentPositionNetPrice($item->line_total); if (!empty($item->product_key)) { if (!empty($item->notes)) { @@ -146,25 +146,37 @@ class ZugferdEDokument extends AbstractService $this->xdocument->setDocumentPositionQuantity($item->quantity, "H87"); } $linenetamount = $item->line_total; - if ($item->discount > 0) { +/* if ($item->discount > 0) { if ($this->document->is_amount_discount) { $linenetamount -= $item->discount; } else { $linenetamount -= $linenetamount * ($item->discount / 100); } - } - $this->xdocument->setDocumentPositionLineSummation($linenetamount, $item->line_total-$linenetamount); + }*/ + $this->xdocument->setDocumentPositionLineSummation($linenetamount, $item->discount); // 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->xdocument->addDocumentPositionTax($taxtype, 'VAT', $item->tax_rate1); + if ($taxtype == ZugferdDutyTaxFeeCategories::VAT_EXEMPT_FOR_EEA_INTRACOMMUNITY_SUPPLY_OF_GOODS_AND_SERVICES){ + $this->xdocument->addDocumentPositionTax($taxtype, 'VAT', $item->tax_rate1, exemptionReason: ctrans('texts.intracommunity_suply')); + } else { + $this->xdocument->addDocumentPositionTax($taxtype, 'VAT', $item->tax_rate1); + } $this->addtoTaxMap($taxtype, $linenetamount, $item->tax_rate1); } elseif (!empty($item->tax_name2)) { - $this->xdocument->addDocumentPositionTax($taxtype, 'VAT', $item->tax_rate2); + if ($taxtype == ZugferdDutyTaxFeeCategories::VAT_EXEMPT_FOR_EEA_INTRACOMMUNITY_SUPPLY_OF_GOODS_AND_SERVICES){ + $this->xdocument->addDocumentPositionTax($taxtype, 'VAT', $item->tax_rate2, exemptionReason: ctrans('texts.intracommunity_suply')); + } else { + $this->xdocument->addDocumentPositionTax($taxtype, 'VAT', $item->tax_rate2); + } $this->addtoTaxMap($taxtype, $linenetamount, $item->tax_rate2); } elseif (!empty($item->tax_name3)) { - $this->xdocument->addDocumentPositionTax($taxtype, 'VAT', $item->tax_rate3); + if ($taxtype == ZugferdDutyTaxFeeCategories::VAT_EXEMPT_FOR_EEA_INTRACOMMUNITY_SUPPLY_OF_GOODS_AND_SERVICES){ + $this->xdocument->addDocumentPositionTax($taxtype, 'VAT', $item->tax_rate3, exemptionReason: ctrans('texts.intracommunity_suply')); + } else { + $this->xdocument->addDocumentPositionTax($taxtype, 'VAT', $item->tax_rate3); + } $this->addtoTaxMap($taxtype, $linenetamount, $item->tax_rate3); } else { // nlog("Can't add correct tax position"); From 8e7c117bd9f10145e2a46f304a3d48627a4b821a Mon Sep 17 00:00:00 2001 From: Lars Kusch Date: Wed, 15 May 2024 16:58:21 +0200 Subject: [PATCH 03/11] More fixes for item discounts --- .../EDocument/Standards/ZugferdEDokument.php | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/app/Services/EDocument/Standards/ZugferdEDokument.php b/app/Services/EDocument/Standards/ZugferdEDokument.php index 121de4b253c6..773411499b07 100644 --- a/app/Services/EDocument/Standards/ZugferdEDokument.php +++ b/app/Services/EDocument/Standards/ZugferdEDokument.php @@ -11,10 +11,10 @@ namespace App\Services\EDocument\Standards; +use App\DataMapper\InvoiceItem; 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; @@ -120,10 +120,10 @@ class ZugferdEDokument extends AbstractService } $invoicing_data = $this->document->calc(); - + $total_position_discount = 0.0; //Create line items and calculate taxes foreach ($this->document->line_items as $index => $item) { - /** @var \App\DataMapper\InvoiceItem $item **/ + /** @var InvoiceItem $item **/ $this->xdocument->addNewPosition($index) ->setDocumentPositionGrossPrice($item->gross_line_total+$item->discount) ->setDocumentPositionNetPrice($item->line_total); @@ -145,15 +145,19 @@ class ZugferdEDokument extends AbstractService } else { $this->xdocument->setDocumentPositionQuantity($item->quantity, "H87"); } - $linenetamount = $item->line_total; -/* if ($item->discount > 0) { + $line_discount = 0.0; + if ($item->discount > 0) { if ($this->document->is_amount_discount) { - $linenetamount -= $item->discount; + $line_discount -= $item->discount; } else { - $linenetamount -= $linenetamount * ($item->discount / 100); + $line_discount -= $item->line_total * ($item->discount / 100); } - }*/ - $this->xdocument->setDocumentPositionLineSummation($linenetamount, $item->discount); + $this->xdocument->addDocumentPositionAllowanceCharge( abs($line_discount), false); + $total_position_discount += abs($line_discount); + } + + + $this->xdocument->setDocumentPositionLineSummation($item->line_total, $item->discount); // 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); @@ -163,47 +167,47 @@ class ZugferdEDokument extends AbstractService } else { $this->xdocument->addDocumentPositionTax($taxtype, 'VAT', $item->tax_rate1); } - $this->addtoTaxMap($taxtype, $linenetamount, $item->tax_rate1); + $this->addtoTaxMap($taxtype, $item->line_total, $item->tax_rate1); } elseif (!empty($item->tax_name2)) { if ($taxtype == ZugferdDutyTaxFeeCategories::VAT_EXEMPT_FOR_EEA_INTRACOMMUNITY_SUPPLY_OF_GOODS_AND_SERVICES){ $this->xdocument->addDocumentPositionTax($taxtype, 'VAT', $item->tax_rate2, exemptionReason: ctrans('texts.intracommunity_suply')); } else { $this->xdocument->addDocumentPositionTax($taxtype, 'VAT', $item->tax_rate2); } - $this->addtoTaxMap($taxtype, $linenetamount, $item->tax_rate2); + $this->addtoTaxMap($taxtype, $item->line_total, $item->tax_rate2); } elseif (!empty($item->tax_name3)) { if ($taxtype == ZugferdDutyTaxFeeCategories::VAT_EXEMPT_FOR_EEA_INTRACOMMUNITY_SUPPLY_OF_GOODS_AND_SERVICES){ $this->xdocument->addDocumentPositionTax($taxtype, 'VAT', $item->tax_rate3, exemptionReason: ctrans('texts.intracommunity_suply')); } else { $this->xdocument->addDocumentPositionTax($taxtype, 'VAT', $item->tax_rate3); } - $this->addtoTaxMap($taxtype, $linenetamount, $item->tax_rate3); + $this->addtoTaxMap($taxtype, $item->line_total, $item->tax_rate3); } else { - // nlog("Can't add correct tax position"); + nlog("Can't add correct tax position"); } } else { 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); + $this->addtoTaxMap($taxtype, $item->line_total, $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); + $this->addtoTaxMap($taxtype, $item->line_total, $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); + $this->addtoTaxMap($taxtype, $item->line_total, $this->document->tax_rate3); } else { $taxtype = ZugferdDutyTaxFeeCategories::ZERO_RATED_GOODS; $this->xdocument->addDocumentPositionTax($taxtype, 'VAT', 0); - $this->addtoTaxMap($taxtype, $linenetamount, 0); + $this->addtoTaxMap($taxtype, $item->line_total, 0); // nlog("Can't add correct tax position"); } } } - $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); + $this->xdocument->setDocumentSummation($this->document->amount, $this->document->balance, $invoicing_data->getSubTotal(), $invoicing_data->getTotalSurcharges(), $invoicing_data->getTotalDiscount()+$total_position_discount, $invoicing_data->getSubTotal(), $invoicing_data->getItemTotalTaxes(), 0.0, $this->document->amount - $this->document->balance); foreach ($this->tax_map as $item) { if ($item["tax_type"] == ZugferdDutyTaxFeeCategories::VAT_EXEMPT_FOR_EEA_INTRACOMMUNITY_SUPPLY_OF_GOODS_AND_SERVICES){ From e7e29f3092d8b13fc80074e8eac4987151d35d06 Mon Sep 17 00:00:00 2001 From: Lars Kusch Date: Wed, 15 May 2024 21:21:21 +0200 Subject: [PATCH 04/11] Added further routing information --- .../EDocument/Standards/ZugferdEDokument.php | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/app/Services/EDocument/Standards/ZugferdEDokument.php b/app/Services/EDocument/Standards/ZugferdEDokument.php index 773411499b07..b192cd58e48e 100644 --- a/app/Services/EDocument/Standards/ZugferdEDokument.php +++ b/app/Services/EDocument/Standards/ZugferdEDokument.php @@ -20,6 +20,7 @@ use App\Services\AbstractService; use horstoeko\zugferd\codelists\ZugferdDutyTaxFeeCategories; use horstoeko\zugferd\ZugferdDocumentBuilder; use horstoeko\zugferd\ZugferdProfiles; +use horstoeko\zugferd\codelists\ZugferdSchemeIdentifiers; class ZugferdEDokument extends AbstractService { @@ -56,6 +57,7 @@ class ZugferdEDokument extends AbstractService ->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->document->user->present()->getFullName(), "", $this->document->user->present()->phone(), "", $this->document->user->email) + ->setDocumentSellerCommunication("EM", $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()) @@ -78,6 +80,7 @@ class ZugferdEDokument extends AbstractService 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()); + $this->xdocument->setIsTestDocument(); } else { $this->xdocument->setDocumentInformation($this->document->number, "380", date_create($this->document->date ?? now()->format('Y-m-d')), $client->getCurrencyCode()); } @@ -92,11 +95,13 @@ class ZugferdEDokument extends AbstractService if (isset($this->document->po_number)) { $this->xdocument->setDocumentBuyerOrderReferencedDocument($this->document->po_number); } - if (empty($client->routing_id)) { - $this->xdocument->setDocumentBuyerReference(ctrans("texts.xinvoice_no_buyers_reference")); + + $this->xdocument->setDocumentBuyerReference(ctrans("texts.xinvoice_no_buyers_reference")) + ->setDocumentSellerCommunication("EM", $client->present()->email()); } else { - $this->xdocument->setDocumentBuyerReference($client->routing_id); + $this->xdocument->setDocumentBuyerReference($client->routing_id) + ->setDocumentBuyerCommunication("0204", $client->routing_id); } if (isset($client->shipping_address1) && $client->shipping_country) { $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); @@ -152,11 +157,10 @@ class ZugferdEDokument extends AbstractService } else { $line_discount -= $item->line_total * ($item->discount / 100); } - $this->xdocument->addDocumentPositionAllowanceCharge( abs($line_discount), false); + $this->xdocument->addDocumentPositionGrossPriceAllowanceCharge( abs($line_discount), false); $total_position_discount += abs($line_discount); } - $this->xdocument->setDocumentPositionLineSummation($item->line_total, $item->discount); // 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))) { @@ -206,8 +210,12 @@ class ZugferdEDokument extends AbstractService } } } - - $this->xdocument->setDocumentSummation($this->document->amount, $this->document->balance, $invoicing_data->getSubTotal(), $invoicing_data->getTotalSurcharges(), $invoicing_data->getTotalDiscount()+$total_position_discount, $invoicing_data->getSubTotal(), $invoicing_data->getItemTotalTaxes(), 0.0, $this->document->amount - $this->document->balance); + if ($this->document->is_amount_discount) { + $this->xdocument->addDocumentAllowanceCharge($this->document->discount, false, ZugferdDutyTaxFeeCategories::EXEMPT_FROM_TAX, "VAT", "0"); + } else { + $this->xdocument->addDocumentAllowanceCharge($this->document->amount * $this->document->discount / 100, false, ZugferdDutyTaxFeeCategories::EXEMPT_FROM_TAX, "VAT", "19"); + } + $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) { if ($item["tax_type"] == ZugferdDutyTaxFeeCategories::VAT_EXEMPT_FOR_EEA_INTRACOMMUNITY_SUPPLY_OF_GOODS_AND_SERVICES){ From c9213c1a807edbc98e06e218e294cd29a2665f38 Mon Sep 17 00:00:00 2001 From: Lars Kusch Date: Wed, 15 May 2024 21:34:31 +0200 Subject: [PATCH 05/11] Global discount at invoice level --- app/Services/EDocument/Standards/ZugferdEDokument.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/Services/EDocument/Standards/ZugferdEDokument.php b/app/Services/EDocument/Standards/ZugferdEDokument.php index b192cd58e48e..a51e5f5675a1 100644 --- a/app/Services/EDocument/Standards/ZugferdEDokument.php +++ b/app/Services/EDocument/Standards/ZugferdEDokument.php @@ -212,10 +212,11 @@ class ZugferdEDokument extends AbstractService } if ($this->document->is_amount_discount) { $this->xdocument->addDocumentAllowanceCharge($this->document->discount, false, ZugferdDutyTaxFeeCategories::EXEMPT_FROM_TAX, "VAT", "0"); + $this->xdocument->setDocumentSummation($this->document->amount, $this->document->balance, $invoicing_data->getSubTotal(), $invoicing_data->getTotalSurcharges(), $this->document->discount, $invoicing_data->getSubTotal(), $invoicing_data->getItemTotalTaxes(), 0.0, $this->document->amount - $this->document->balance); } else { $this->xdocument->addDocumentAllowanceCharge($this->document->amount * $this->document->discount / 100, false, ZugferdDutyTaxFeeCategories::EXEMPT_FROM_TAX, "VAT", "19"); + $this->xdocument->setDocumentSummation($this->document->amount, $this->document->balance, $invoicing_data->getSubTotal(), $invoicing_data->getTotalSurcharges(), $this->document->amount * $this->document->discount / 100, $invoicing_data->getSubTotal(), $invoicing_data->getItemTotalTaxes(), 0.0, $this->document->amount - $this->document->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) { if ($item["tax_type"] == ZugferdDutyTaxFeeCategories::VAT_EXEMPT_FOR_EEA_INTRACOMMUNITY_SUPPLY_OF_GOODS_AND_SERVICES){ From 9fcde1ffc8d891c0c1ce70185c99e85230940278 Mon Sep 17 00:00:00 2001 From: Lars Kusch Date: Wed, 15 May 2024 22:00:44 +0200 Subject: [PATCH 06/11] Readded merged pdf with xml-file --- app/Jobs/EDocument/MergeEDocument.php | 65 +++++++++++++++++++ app/Jobs/Entity/CreateRawPdf.php | 8 ++- .../EDocument/Standards/ZugferdEDokument.php | 1 - 3 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 app/Jobs/EDocument/MergeEDocument.php diff --git a/app/Jobs/EDocument/MergeEDocument.php b/app/Jobs/EDocument/MergeEDocument.php new file mode 100644 index 000000000000..2642cb0ea6bc --- /dev/null +++ b/app/Jobs/EDocument/MergeEDocument.php @@ -0,0 +1,65 @@ +document instanceof PurchaseOrder) ? $this->document->vendor : $this->document->client; + + $e_document_type = strlen($settings_entity->getSetting('e_invoice_type')) > 2 ? $settings_entity->getSetting('e_invoice_type') : "XInvoice_3_0"; + $e_quote_type = strlen($settings_entity->getSetting('e_quote_type')) > 2 ? $settings_entity->getSetting('e_quote_type') : "OrderX_Extended"; + + 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": + $xml = (new CreateEDocument($this->document))->handle(); + (new ZugferdDocumentPdfBuilder($xml, $this->pdf_file))->generateDocument()->saveDocument($mergeToPdf); + return $mergeToPdf; + default: + return $this->pdf_file; + + } + } + else{ + return $this->pdf_file; + } + } +} + diff --git a/app/Jobs/Entity/CreateRawPdf.php b/app/Jobs/Entity/CreateRawPdf.php index ac4320f1a89d..684860e492e1 100644 --- a/app/Jobs/Entity/CreateRawPdf.php +++ b/app/Jobs/Entity/CreateRawPdf.php @@ -12,9 +12,11 @@ namespace App\Jobs\Entity; use App\Exceptions\FilePermissionsFailure; +use App\Jobs\EDocument\MergeEDocument; use App\Models\Credit; use App\Models\CreditInvitation; use App\Models\Invoice; +use App\Models\Company; use App\Models\InvoiceInvitation; use App\Models\PurchaseOrder; use App\Models\PurchaseOrderInvitation; @@ -39,7 +41,7 @@ class CreateRawPdf public Invoice | Credit | Quote | RecurringInvoice | PurchaseOrder $entity; - public $company; + public Company $company; public $contact; @@ -105,7 +107,11 @@ class CreateRawPdf ]); $pdf = $ps->boot()->getPdf(); + nlog("pdf timer = ". $ps->execution_time); + if ($this->company->getSetting("enable_e_invoice")){ + $pdf = (new MergeEDocument($this->entity))->handle(); + } return $pdf; throw new FilePermissionsFailure('Unable to generate the raw PDF'); diff --git a/app/Services/EDocument/Standards/ZugferdEDokument.php b/app/Services/EDocument/Standards/ZugferdEDokument.php index a51e5f5675a1..3c445a37843b 100644 --- a/app/Services/EDocument/Standards/ZugferdEDokument.php +++ b/app/Services/EDocument/Standards/ZugferdEDokument.php @@ -20,7 +20,6 @@ use App\Services\AbstractService; use horstoeko\zugferd\codelists\ZugferdDutyTaxFeeCategories; use horstoeko\zugferd\ZugferdDocumentBuilder; use horstoeko\zugferd\ZugferdProfiles; -use horstoeko\zugferd\codelists\ZugferdSchemeIdentifiers; class ZugferdEDokument extends AbstractService { From b3127a3d2e3eb5cd604c127d9e80cdab5dc8ba66 Mon Sep 17 00:00:00 2001 From: Lars Kusch Date: Thu, 16 May 2024 07:54:03 +0200 Subject: [PATCH 07/11] Fixes --- app/Jobs/EDocument/MergeEDocument.php | 8 +++----- app/Jobs/Entity/CreateRawPdf.php | 7 +++---- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/app/Jobs/EDocument/MergeEDocument.php b/app/Jobs/EDocument/MergeEDocument.php index 2642cb0ea6bc..2618111fd105 100644 --- a/app/Jobs/EDocument/MergeEDocument.php +++ b/app/Jobs/EDocument/MergeEDocument.php @@ -20,7 +20,7 @@ class MergeEDocument implements ShouldQueue public $deleteWhenMissingModels = true; - public function __construct(private object $document, private object $pdf_file) + public function __construct(private object $document, private string $pdf_file) { } @@ -35,7 +35,6 @@ class MergeEDocument implements ShouldQueue $settings_entity = ($this->document instanceof PurchaseOrder) ? $this->document->vendor : $this->document->client; $e_document_type = strlen($settings_entity->getSetting('e_invoice_type')) > 2 ? $settings_entity->getSetting('e_invoice_type') : "XInvoice_3_0"; - $e_quote_type = strlen($settings_entity->getSetting('e_quote_type')) > 2 ? $settings_entity->getSetting('e_quote_type') : "OrderX_Extended"; if ($this->document instanceof Invoice){ switch ($e_document_type) { @@ -49,9 +48,8 @@ class MergeEDocument implements ShouldQueue case "XInvoice-Extended": case "XInvoice-BasicWL": case "XInvoice-Basic": - $xml = (new CreateEDocument($this->document))->handle(); - (new ZugferdDocumentPdfBuilder($xml, $this->pdf_file))->generateDocument()->saveDocument($mergeToPdf); - return $mergeToPdf; + $xml = (new CreateEDocument($this->document, true))->handle(); + return(new ZugferdDocumentPdfBuilder($xml, $this->pdf_file))->generateDocument()->downloadString("Invoice.pdf"); default: return $this->pdf_file; diff --git a/app/Jobs/Entity/CreateRawPdf.php b/app/Jobs/Entity/CreateRawPdf.php index 684860e492e1..d5026bacfd92 100644 --- a/app/Jobs/Entity/CreateRawPdf.php +++ b/app/Jobs/Entity/CreateRawPdf.php @@ -16,7 +16,6 @@ use App\Jobs\EDocument\MergeEDocument; use App\Models\Credit; use App\Models\CreditInvitation; use App\Models\Invoice; -use App\Models\Company; use App\Models\InvoiceInvitation; use App\Models\PurchaseOrder; use App\Models\PurchaseOrderInvitation; @@ -41,7 +40,7 @@ class CreateRawPdf public Invoice | Credit | Quote | RecurringInvoice | PurchaseOrder $entity; - public Company $company; + public $company; public $contact; @@ -109,8 +108,8 @@ class CreateRawPdf $pdf = $ps->boot()->getPdf(); nlog("pdf timer = ". $ps->execution_time); - if ($this->company->getSetting("enable_e_invoice")){ - $pdf = (new MergeEDocument($this->entity))->handle(); + if ($this->entity_string == "invoice" && $this->entity->company->getSetting("enable_e_invoice")){ + $pdf = (new MergeEDocument($this->entity, $pdf))->handle(); } return $pdf; From f1e4d3e8f11ee7ad00612564ea629949162b9238 Mon Sep 17 00:00:00 2001 From: Lars Kusch Date: Thu, 16 May 2024 08:10:07 +0200 Subject: [PATCH 08/11] Fixes for global discounts --- .../EDocument/Standards/ZugferdEDokument.php | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/app/Services/EDocument/Standards/ZugferdEDokument.php b/app/Services/EDocument/Standards/ZugferdEDokument.php index 3c445a37843b..ce5263ebbafc 100644 --- a/app/Services/EDocument/Standards/ZugferdEDokument.php +++ b/app/Services/EDocument/Standards/ZugferdEDokument.php @@ -160,7 +160,7 @@ class ZugferdEDokument extends AbstractService $total_position_discount += abs($line_discount); } - $this->xdocument->setDocumentPositionLineSummation($item->line_total, $item->discount); + $this->xdocument->setDocumentPositionLineSummation($item->line_total); // 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); @@ -210,14 +210,21 @@ class ZugferdEDokument extends AbstractService } } if ($this->document->is_amount_discount) { - $this->xdocument->addDocumentAllowanceCharge($this->document->discount, false, ZugferdDutyTaxFeeCategories::EXEMPT_FROM_TAX, "VAT", "0"); - $this->xdocument->setDocumentSummation($this->document->amount, $this->document->balance, $invoicing_data->getSubTotal(), $invoicing_data->getTotalSurcharges(), $this->document->discount, $invoicing_data->getSubTotal(), $invoicing_data->getItemTotalTaxes(), 0.0, $this->document->amount - $this->document->balance); + $document_discount = abs($this->document->discount); } else { - $this->xdocument->addDocumentAllowanceCharge($this->document->amount * $this->document->discount / 100, false, ZugferdDutyTaxFeeCategories::EXEMPT_FROM_TAX, "VAT", "19"); - $this->xdocument->setDocumentSummation($this->document->amount, $this->document->balance, $invoicing_data->getSubTotal(), $invoicing_data->getTotalSurcharges(), $this->document->amount * $this->document->discount / 100, $invoicing_data->getSubTotal(), $invoicing_data->getItemTotalTaxes(), 0.0, $this->document->amount - $this->document->balance); - } - + $document_discount = $this->document->amount * $this->document->discount / 100; + } + $this->xdocument->addDocumentAllowanceCharge($document_discount, false, ZugferdDutyTaxFeeCategories::STANDARD_RATE, "VAT", "19"); + $this->xdocument->setDocumentSummation($this->document->amount, $this->document->balance, $invoicing_data->getSubTotal(), $invoicing_data->getTotalSurcharges(), $document_discount, $invoicing_data->getSubTotal()-$document_discount, $invoicing_data->getItemTotalTaxes(), 0.0, $this->document->amount - $this->document->balance); foreach ($this->tax_map as $item) { + if ($document_discount > 0){ + if ($item["net_amount"] >= $document_discount) { + $item["net_amount"] -= $document_discount; + } else { + $document_discount -= $item["net_amount"]; + $item["net_amount"] = 0; + } + } if ($item["tax_type"] == ZugferdDutyTaxFeeCategories::VAT_EXEMPT_FOR_EEA_INTRACOMMUNITY_SUPPLY_OF_GOODS_AND_SERVICES){ $this->xdocument->addDocumentTax($item["tax_type"], "VAT", $item["net_amount"], $item["tax_rate"] * $item["net_amount"], $item["tax_rate"] * 100, ctrans('texts.intracommunity_suply')); } else { From 439c02cb8bf79a168da439a5e255f3473e939deb Mon Sep 17 00:00:00 2001 From: Lars Kusch Date: Fri, 17 May 2024 07:59:25 +0200 Subject: [PATCH 09/11] Fixes --- app/Jobs/EDocument/MergeEDocument.php | 1 - app/Services/EDocument/Standards/ZugferdEDokument.php | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/app/Jobs/EDocument/MergeEDocument.php b/app/Jobs/EDocument/MergeEDocument.php index 2618111fd105..b8377c0bb9d1 100644 --- a/app/Jobs/EDocument/MergeEDocument.php +++ b/app/Jobs/EDocument/MergeEDocument.php @@ -31,7 +31,6 @@ class MergeEDocument implements ShouldQueue */ public function handle(): string { - global $mergeToPdf; $settings_entity = ($this->document instanceof PurchaseOrder) ? $this->document->vendor : $this->document->client; $e_document_type = strlen($settings_entity->getSetting('e_invoice_type')) > 2 ? $settings_entity->getSetting('e_invoice_type') : "XInvoice_3_0"; diff --git a/app/Services/EDocument/Standards/ZugferdEDokument.php b/app/Services/EDocument/Standards/ZugferdEDokument.php index ce5263ebbafc..afcc3c349ce6 100644 --- a/app/Services/EDocument/Standards/ZugferdEDokument.php +++ b/app/Services/EDocument/Standards/ZugferdEDokument.php @@ -166,21 +166,21 @@ class ZugferdEDokument extends AbstractService $taxtype = $this->getTaxType($item->tax_id); if (!empty($item->tax_name1)) { if ($taxtype == ZugferdDutyTaxFeeCategories::VAT_EXEMPT_FOR_EEA_INTRACOMMUNITY_SUPPLY_OF_GOODS_AND_SERVICES){ - $this->xdocument->addDocumentPositionTax($taxtype, 'VAT', $item->tax_rate1, exemptionReason: ctrans('texts.intracommunity_suply')); + $this->xdocument->addDocumentPositionTax($taxtype, 'VAT', $item->tax_rate1, exemptionReason: ctrans('texts.intracommunity_tax_info')); } else { $this->xdocument->addDocumentPositionTax($taxtype, 'VAT', $item->tax_rate1); } $this->addtoTaxMap($taxtype, $item->line_total, $item->tax_rate1); } elseif (!empty($item->tax_name2)) { if ($taxtype == ZugferdDutyTaxFeeCategories::VAT_EXEMPT_FOR_EEA_INTRACOMMUNITY_SUPPLY_OF_GOODS_AND_SERVICES){ - $this->xdocument->addDocumentPositionTax($taxtype, 'VAT', $item->tax_rate2, exemptionReason: ctrans('texts.intracommunity_suply')); + $this->xdocument->addDocumentPositionTax($taxtype, 'VAT', $item->tax_rate2, exemptionReason: ctrans('texts.intracommunity_tax_info')); } else { $this->xdocument->addDocumentPositionTax($taxtype, 'VAT', $item->tax_rate2); } $this->addtoTaxMap($taxtype, $item->line_total, $item->tax_rate2); } elseif (!empty($item->tax_name3)) { if ($taxtype == ZugferdDutyTaxFeeCategories::VAT_EXEMPT_FOR_EEA_INTRACOMMUNITY_SUPPLY_OF_GOODS_AND_SERVICES){ - $this->xdocument->addDocumentPositionTax($taxtype, 'VAT', $item->tax_rate3, exemptionReason: ctrans('texts.intracommunity_suply')); + $this->xdocument->addDocumentPositionTax($taxtype, 'VAT', $item->tax_rate3, exemptionReason: ctrans('texts.intracommunity_tax_info')); } else { $this->xdocument->addDocumentPositionTax($taxtype, 'VAT', $item->tax_rate3); } @@ -226,7 +226,7 @@ class ZugferdEDokument extends AbstractService } } if ($item["tax_type"] == ZugferdDutyTaxFeeCategories::VAT_EXEMPT_FOR_EEA_INTRACOMMUNITY_SUPPLY_OF_GOODS_AND_SERVICES){ - $this->xdocument->addDocumentTax($item["tax_type"], "VAT", $item["net_amount"], $item["tax_rate"] * $item["net_amount"], $item["tax_rate"] * 100, ctrans('texts.intracommunity_suply')); + $this->xdocument->addDocumentTax($item["tax_type"], "VAT", $item["net_amount"], $item["tax_rate"] * $item["net_amount"], $item["tax_rate"] * 100, ctrans('texts.intracommunity_tax_info')); } else { $this->xdocument->addDocumentTax($item["tax_type"], "VAT", $item["net_amount"], $item["tax_rate"] * $item["net_amount"], $item["tax_rate"] * 100); } From 7abf67cce543a4ffdcfc497dfd3645c1172f470c Mon Sep 17 00:00:00 2001 From: Lars Kusch Date: Fri, 17 May 2024 07:59:44 +0200 Subject: [PATCH 10/11] Fixes --- lang/en/texts.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lang/en/texts.php b/lang/en/texts.php index 366d14bef7f1..63654d484928 100644 --- a/lang/en/texts.php +++ b/lang/en/texts.php @@ -5104,7 +5104,6 @@ $lang = array( 'triangular_tax_info' => 'Intra-community triangular transaction', 'intracommunity_tax_info' => 'Tax-free intra-community delivery', 'reverse_tax_info' => 'Please note that this supply is subject to reverse charge', - 'intracommunity_suply'=>'Intra-community supply', 'currency_nicaraguan_cordoba' => 'Nicaraguan Córdoba', 'public' => 'Public', 'private' => 'Private', From 3709039b087e1d2a9cab425b3a208e34896732f2 Mon Sep 17 00:00:00 2001 From: Lars Kusch Date: Mon, 20 May 2024 21:25:30 +0200 Subject: [PATCH 11/11] Last fixes --- app/Jobs/EDocument/MergeEDocument.php | 2 +- app/Jobs/Entity/CreateRawPdf.php | 15 +++++++++------ .../EDocument/Standards/ZugferdEDokument.php | 11 +++++++---- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/app/Jobs/EDocument/MergeEDocument.php b/app/Jobs/EDocument/MergeEDocument.php index b8377c0bb9d1..08eaf171124a 100644 --- a/app/Jobs/EDocument/MergeEDocument.php +++ b/app/Jobs/EDocument/MergeEDocument.php @@ -20,7 +20,7 @@ class MergeEDocument implements ShouldQueue public $deleteWhenMissingModels = true; - public function __construct(private object $document, private string $pdf_file) + public function __construct(private mixed $document, private string $pdf_file) { } diff --git a/app/Jobs/Entity/CreateRawPdf.php b/app/Jobs/Entity/CreateRawPdf.php index d5026bacfd92..e48b37a8763a 100644 --- a/app/Jobs/Entity/CreateRawPdf.php +++ b/app/Jobs/Entity/CreateRawPdf.php @@ -96,6 +96,9 @@ class CreateRawPdf } + /** + * @throws FilePermissionsFailure + */ public function handle() { /** Testing this override to improve PDF generation performance */ @@ -105,15 +108,15 @@ class CreateRawPdf "{$this->entity_string}s" => [$this->entity], ]); - $pdf = $ps->boot()->getPdf(); - - nlog("pdf timer = ". $ps->execution_time); - if ($this->entity_string == "invoice" && $this->entity->company->getSetting("enable_e_invoice")){ + try { + $pdf = $ps->boot()->getPdf(); + } catch (\Exception) { + throw new FilePermissionsFailure('Unable to generate the raw PDF'); + } + if ($this->entity_string == "invoice" && $this->entity->getSetting("merge_e_invoice_to_pdf")){ $pdf = (new MergeEDocument($this->entity, $pdf))->handle(); } return $pdf; - - throw new FilePermissionsFailure('Unable to generate the raw PDF'); } public function failed($e) diff --git a/app/Services/EDocument/Standards/ZugferdEDokument.php b/app/Services/EDocument/Standards/ZugferdEDokument.php index afcc3c349ce6..9638563cf77c 100644 --- a/app/Services/EDocument/Standards/ZugferdEDokument.php +++ b/app/Services/EDocument/Standards/ZugferdEDokument.php @@ -72,9 +72,10 @@ class ZugferdEDokument extends AbstractService // 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()); + $this->xdocument->setIsTestDocument(); } 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)) { @@ -87,6 +88,7 @@ class ZugferdEDokument extends AbstractService 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()); + $this->xdocument->setIsTestDocument(); } else { $this->xdocument->setDocumentInformation($this->document->number, "389", date_create($this->document->date ?? now()->format('Y-m-d')), $client->getCurrencyCode()); } @@ -124,7 +126,6 @@ class ZugferdEDokument extends AbstractService } $invoicing_data = $this->document->calc(); - $total_position_discount = 0.0; //Create line items and calculate taxes foreach ($this->document->line_items as $index => $item) { /** @var InvoiceItem $item **/ @@ -157,7 +158,6 @@ class ZugferdEDokument extends AbstractService $line_discount -= $item->line_total * ($item->discount / 100); } $this->xdocument->addDocumentPositionGrossPriceAllowanceCharge( abs($line_discount), false); - $total_position_discount += abs($line_discount); } $this->xdocument->setDocumentPositionLineSummation($item->line_total); @@ -214,15 +214,18 @@ class ZugferdEDokument extends AbstractService } else { $document_discount = $this->document->amount * $this->document->discount / 100; } - $this->xdocument->addDocumentAllowanceCharge($document_discount, false, ZugferdDutyTaxFeeCategories::STANDARD_RATE, "VAT", "19"); + $this->xdocument->setDocumentSummation($this->document->amount, $this->document->balance, $invoicing_data->getSubTotal(), $invoicing_data->getTotalSurcharges(), $document_discount, $invoicing_data->getSubTotal()-$document_discount, $invoicing_data->getItemTotalTaxes(), 0.0, $this->document->amount - $this->document->balance); foreach ($this->tax_map as $item) { if ($document_discount > 0){ if ($item["net_amount"] >= $document_discount) { $item["net_amount"] -= $document_discount; + $this->xdocument->addDocumentAllowanceCharge($document_discount, false, $item["tax_type"], "VAT", $item["tax_rate"] * 100); } else { $document_discount -= $item["net_amount"]; + $this->xdocument->addDocumentAllowanceCharge($item["net_amount"], false, $item["tax_type"], "VAT", $item["tax_rate"] * 100); $item["net_amount"] = 0; + } } if ($item["tax_type"] == ZugferdDutyTaxFeeCategories::VAT_EXEMPT_FOR_EEA_INTRACOMMUNITY_SUPPLY_OF_GOODS_AND_SERVICES){