mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-06-23 20:00:33 -04:00
commit
ff94e9881a
62
app/Jobs/EDocument/MergeEDocument.php
Normal file
62
app/Jobs/EDocument/MergeEDocument.php
Normal file
@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs\EDocument;
|
||||
|
||||
use App\Models\Invoice;
|
||||
use App\Models\PurchaseOrder;
|
||||
use horstoeko\zugferd\ZugferdDocumentPdfBuilder;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
|
||||
class MergeEDocument implements ShouldQueue
|
||||
{
|
||||
use Dispatchable;
|
||||
use InteractsWithQueue;
|
||||
use Queueable;
|
||||
use SerializesModels;
|
||||
|
||||
public $deleteWhenMissingModels = true;
|
||||
|
||||
public function __construct(private mixed $document, private string $pdf_file)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function handle(): string
|
||||
{
|
||||
$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";
|
||||
|
||||
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, true))->handle();
|
||||
return(new ZugferdDocumentPdfBuilder($xml, $this->pdf_file))->generateDocument()->downloadString("Invoice.pdf");
|
||||
default:
|
||||
return $this->pdf_file;
|
||||
|
||||
}
|
||||
}
|
||||
else{
|
||||
return $this->pdf_file;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
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;
|
||||
@ -95,6 +96,9 @@ class CreateRawPdf
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FilePermissionsFailure
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
/** Testing this override to improve PDF generation performance */
|
||||
@ -104,11 +108,15 @@ class CreateRawPdf
|
||||
"{$this->entity_string}s" => [$this->entity],
|
||||
]);
|
||||
|
||||
$pdf = $ps->boot()->getPdf();
|
||||
nlog("pdf timer = ". $ps->execution_time);
|
||||
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)
|
||||
|
@ -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;
|
||||
@ -56,6 +56,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())
|
||||
@ -71,13 +72,15 @@ 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)) {
|
||||
$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());
|
||||
}
|
||||
@ -85,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());
|
||||
}
|
||||
@ -92,11 +96,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);
|
||||
@ -113,15 +119,18 @@ 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();
|
||||
|
||||
//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)
|
||||
->setDocumentPositionGrossPrice($item->gross_line_total+$item->discount)
|
||||
->setDocumentPositionNetPrice($item->line_total);
|
||||
if (!empty($item->product_key)) {
|
||||
if (!empty($item->notes)) {
|
||||
@ -141,56 +150,90 @@ class ZugferdEDokument extends AbstractService
|
||||
} else {
|
||||
$this->xdocument->setDocumentPositionQuantity($item->quantity, "H87");
|
||||
}
|
||||
$linenetamount = $item->line_total;
|
||||
$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->addDocumentPositionGrossPriceAllowanceCharge( abs($line_discount), false);
|
||||
}
|
||||
$this->xdocument->setDocumentPositionLineSummation($linenetamount);
|
||||
|
||||
$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);
|
||||
if (!empty($item->tax_name1)) {
|
||||
$this->xdocument->addDocumentPositionTax($taxtype, 'VAT', $item->tax_rate1);
|
||||
$this->addtoTaxMap($taxtype, $linenetamount, $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_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)) {
|
||||
$this->xdocument->addDocumentPositionTax($taxtype, 'VAT', $item->tax_rate2);
|
||||
$this->addtoTaxMap($taxtype, $linenetamount, $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_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)) {
|
||||
$this->xdocument->addDocumentPositionTax($taxtype, 'VAT', $item->tax_rate3);
|
||||
$this->addtoTaxMap($taxtype, $linenetamount, $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_tax_info'));
|
||||
} else {
|
||||
$this->xdocument->addDocumentPositionTax($taxtype, 'VAT', $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");
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($this->document->is_amount_discount) {
|
||||
$document_discount = abs($this->document->discount);
|
||||
} else {
|
||||
$document_discount = $this->document->amount * $this->document->discount / 100;
|
||||
}
|
||||
|
||||
$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(), $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){
|
||||
$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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// The validity can be checked using https://portal3.gefeg.com/invoice/validation or https://e-rechnung.bayern.de/app/#/upload
|
||||
|
Loading…
x
Reference in New Issue
Block a user