From 33dd437d96063ed205892633951eac83a9dd974c Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 16 Aug 2023 15:38:08 +1000 Subject: [PATCH 01/11] Minor fixes for return types for e-invoices --- app/DataMapper/InvoiceItem.php | 5 +++ app/Jobs/Invoice/CreateEInvoice.php | 2 +- app/Jobs/Invoice/MergeEInvoice.php | 36 ++++++++++--------- .../Invoice/EInvoice/ZugferdEInvoice.php | 5 ++- 4 files changed, 29 insertions(+), 19 deletions(-) diff --git a/app/DataMapper/InvoiceItem.php b/app/DataMapper/InvoiceItem.php index 49571d668fdf..cdb1953c9be4 100644 --- a/app/DataMapper/InvoiceItem.php +++ b/app/DataMapper/InvoiceItem.php @@ -61,8 +61,13 @@ class InvoiceItem public $tax_id = ''; + public $task_id = ''; + + public $expense_id = ''; public static $casts = [ + 'task_id' => 'string', + 'expense_id' => 'string', 'tax_id' => 'string', 'type_id' => 'string', 'quantity' => 'float', diff --git a/app/Jobs/Invoice/CreateEInvoice.php b/app/Jobs/Invoice/CreateEInvoice.php index 017c8fcac448..3988c688b4c8 100644 --- a/app/Jobs/Invoice/CreateEInvoice.php +++ b/app/Jobs/Invoice/CreateEInvoice.php @@ -38,7 +38,7 @@ class CreateEInvoice implements ShouldQueue * Execute the job. * * - * @return string + * @return string|ZugferdDocumentBuilder */ public function handle(): string|ZugferdDocumentBuilder { diff --git a/app/Jobs/Invoice/MergeEInvoice.php b/app/Jobs/Invoice/MergeEInvoice.php index 1a33db8be1db..cae7d110682b 100644 --- a/app/Jobs/Invoice/MergeEInvoice.php +++ b/app/Jobs/Invoice/MergeEInvoice.php @@ -46,23 +46,25 @@ class MergeEInvoice implements ShouldQueue */ private function embedEInvoiceZuGFerD(): void { - $filepath_pdf = !empty($this->pdf_path) ? $this->pdf_path : $this->invoice->service()->getInvoicePdf(); - $disk = config('filesystems.default'); - $e_rechnung = (new CreateEInvoice($this->invoice, true))->handle(); - if (!empty($this->pdf_path)){ - $realpath_pdf = $filepath_pdf; - } - else { - $realpath_pdf = Storage::disk($disk)->path($filepath_pdf); - } - if (file_exists($realpath_pdf)){ - $pdfBuilder = new ZugferdDocumentPdfBuilder($e_rechnung, $realpath_pdf); - $pdfBuilder->generateDocument(); - $pdfBuilder->saveDocument($realpath_pdf); - } - else{ - nlog("E_Invoice Merge failed - file to merge not found"); - } + try { + $filepath_pdf = !empty($this->pdf_path) ? $this->pdf_path : $this->invoice->service()->getInvoicePdf(); + $disk = config('filesystems.default'); + $e_rechnung = (new CreateEInvoice($this->invoice, true))->handle(); + if (!empty($this->pdf_path)) { + $realpath_pdf = $filepath_pdf; + } else { + $realpath_pdf = Storage::disk($disk)->path($filepath_pdf); + } + if (file_exists($realpath_pdf)) { + $pdfBuilder = new ZugferdDocumentPdfBuilder($e_rechnung, $realpath_pdf); + $pdfBuilder->generateDocument(); + $pdfBuilder->saveDocument($realpath_pdf); + } else { + nlog("E_Invoice Merge failed - file to merge not found"); + } + } catch (\Exception $e) { + nlog("E_Invoice Merge failed - " . $e->getMessage()); + } } } diff --git a/app/Services/Invoice/EInvoice/ZugferdEInvoice.php b/app/Services/Invoice/EInvoice/ZugferdEInvoice.php index 37d6ce32fe65..3baf14128f8b 100644 --- a/app/Services/Invoice/EInvoice/ZugferdEInvoice.php +++ b/app/Services/Invoice/EInvoice/ZugferdEInvoice.php @@ -178,7 +178,10 @@ class ZugferdEInvoice extends AbstractService if ($this->returnObject){ return $xrechnung; } - return $client->e_invoice_filepath($this->invoice->invitations->first()) . $this->invoice->getFileName("xml"); + + throw new \Exception("Invalid e invoice object"); + + // return $client->e_invoice_filepath($this->invoice->invitations->first()) . $this->invoice->getFileName("xml"); } private function getTaxType($name): string From 2ba713eeb84a7b3c70afb13ce8cde9e79a691527 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 16 Aug 2023 19:55:35 +1000 Subject: [PATCH 02/11] Refactor for einvoicing --- app/Http/Controllers/InvoiceController.php | 5 +- app/Http/Livewire/PdfSlot.php | 5 +- app/Jobs/Entity/CreateEntityPdf.php | 117 +++++++++++++----- app/Jobs/Entity/CreateRawPdf.php | 110 +++++++++++----- app/Jobs/Invoice/CreateEInvoice.php | 10 +- app/Jobs/Invoice/MergeEInvoice.php | 70 ----------- app/Jobs/Invoice/ZipInvoices.php | 22 +--- app/Models/BaseModel.php | 1 + app/Services/Email/EmailDefaults.php | 6 +- .../Invoice/EInvoice/ZugferdEInvoice.php | 88 ++++++------- app/Services/Invoice/GetInvoiceEInvoice.php | 52 -------- app/Services/Invoice/GetInvoicePdf.php | 6 +- app/Services/Invoice/InvoiceService.php | 21 ++-- 13 files changed, 242 insertions(+), 271 deletions(-) delete mode 100644 app/Jobs/Invoice/MergeEInvoice.php delete mode 100644 app/Services/Invoice/GetInvoiceEInvoice.php diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php index ec86da941082..626c22df9acc 100644 --- a/app/Http/Controllers/InvoiceController.php +++ b/app/Http/Controllers/InvoiceController.php @@ -850,6 +850,7 @@ class InvoiceController extends BaseController $invoice = $invitation->invoice; $file = $invoice->service()->getEInvoice($contact); + $file_name = $invoice->getFileName("xml"); $headers = ['Content-Type' => 'application/xml']; @@ -858,8 +859,8 @@ class InvoiceController extends BaseController } return response()->streamDownload(function () use ($file) { - echo Storage::get($file); - }, basename($file), $headers); + echo $file; + }, $file_name, $headers); } /** diff --git a/app/Http/Livewire/PdfSlot.php b/app/Http/Livewire/PdfSlot.php index 0990ac3ec76d..3a44b5b3a8bd 100644 --- a/app/Http/Livewire/PdfSlot.php +++ b/app/Http/Livewire/PdfSlot.php @@ -12,7 +12,6 @@ namespace App\Http\Livewire; -use App\Services\Invoice\GetInvoiceEInvoice; use App\Utils\Number; use Livewire\Component; use App\Utils\HtmlEngine; @@ -22,10 +21,12 @@ use App\Models\QuoteInvitation; use App\Utils\VendorHtmlEngine; use App\Models\CreditInvitation; use App\Models\InvoiceInvitation; +use App\Jobs\Invoice\CreateEInvoice; use Illuminate\Support\Facades\Cache; use App\Models\PurchaseOrderInvitation; use App\Models\RecurringInvoiceInvitation; use App\Jobs\Vendor\CreatePurchaseOrderPdf; +use App\Services\Invoice\GetInvoiceEInvoice; use App\Services\PdfMaker\Designs\Utilities\DesignHelpers; class PdfSlot extends Component @@ -102,7 +103,7 @@ class PdfSlot extends Component $file_name = $this->entity->numberFormatter().'.xml'; - $file = (new GetInvoiceEInvoice($this->entity))->run(); + $file = (new CreateEInvoice($this->entity))->handle(); $headers = ['Content-Type' => 'application/xml']; diff --git a/app/Jobs/Entity/CreateEntityPdf.php b/app/Jobs/Entity/CreateEntityPdf.php index 6fdf3d7f309e..7ef89d2c3e81 100644 --- a/app/Jobs/Entity/CreateEntityPdf.php +++ b/app/Jobs/Entity/CreateEntityPdf.php @@ -11,38 +11,38 @@ namespace App\Jobs\Entity; -use App\Exceptions\FilePermissionsFailure; -use App\Jobs\Invoice\CreateEInvoice; -use App\Jobs\Invoice\MergeEInvoice; -use App\Libraries\MultiDB; +use App\Utils\Ninja; +use App\Models\Quote; use App\Models\Credit; -use App\Models\CreditInvitation; use App\Models\Design; use App\Models\Invoice; -use App\Models\InvoiceInvitation; -use App\Models\Quote; +use App\Utils\HtmlEngine; +use App\Libraries\MultiDB; +use Illuminate\Bus\Queueable; use App\Models\QuoteInvitation; +use App\Utils\Traits\MakesHash; +use App\Models\CreditInvitation; use App\Models\RecurringInvoice; +use App\Utils\PhantomJS\Phantom; +use App\Models\InvoiceInvitation; +use App\Utils\HostedPDF\NinjaPdf; +use App\Utils\Traits\Pdf\PdfMaker; +use Illuminate\Support\Facades\App; +use App\Jobs\Invoice\CreateEInvoice; +use App\Utils\Traits\NumberFormatter; +use App\Utils\Traits\MakesInvoiceHtml; +use Illuminate\Queue\SerializesModels; +use App\Utils\Traits\Pdf\PageNumbering; +use Illuminate\Support\Facades\Storage; +use Illuminate\Queue\InteractsWithQueue; +use App\Exceptions\FilePermissionsFailure; use App\Models\RecurringInvoiceInvitation; +use Illuminate\Contracts\Queue\ShouldQueue; +use Illuminate\Foundation\Bus\Dispatchable; +use horstoeko\zugferd\ZugferdDocumentPdfBuilder; use App\Services\PdfMaker\Design as PdfDesignModel; use App\Services\PdfMaker\Design as PdfMakerDesign; use App\Services\PdfMaker\PdfMaker as PdfMakerService; -use App\Utils\HostedPDF\NinjaPdf; -use App\Utils\HtmlEngine; -use App\Utils\Ninja; -use App\Utils\PhantomJS\Phantom; -use App\Utils\Traits\MakesHash; -use App\Utils\Traits\MakesInvoiceHtml; -use App\Utils\Traits\NumberFormatter; -use App\Utils\Traits\Pdf\PageNumbering; -use App\Utils\Traits\Pdf\PdfMaker; -use Illuminate\Bus\Queueable; -use Illuminate\Contracts\Queue\ShouldQueue; -use Illuminate\Foundation\Bus\Dispatchable; -use Illuminate\Queue\InteractsWithQueue; -use Illuminate\Queue\SerializesModels; -use Illuminate\Support\Facades\App; -use Illuminate\Support\Facades\Storage; class CreateEntityPdf implements ShouldQueue { @@ -117,7 +117,8 @@ class CreateEntityPdf implements ShouldQueue } $entity_design_id = ''; - + $path = ''; + if ($this->entity instanceof Invoice) { $path = $this->client->invoice_filepath($this->invitation); $entity_design_id = 'invoice_design_id'; @@ -207,6 +208,11 @@ class CreateEntityPdf implements ShouldQueue info($maker->getCompiledHTML()); } + if($this->entity_string == "invoice" && $this->client->getSetting('enable_e_invoice')) + { + $pdf = $this->checkEInvoice($pdf); + } + if ($pdf) { try { Storage::disk($this->disk)->put($file_path, $pdf); @@ -214,11 +220,7 @@ class CreateEntityPdf implements ShouldQueue throw new FilePermissionsFailure($e->getMessage()); } } - if ($this->entity_string == "invoice" && $this->client->getSetting('enable_e_invoice')){ - (new CreateEInvoice($this->entity))->handle(); - (new MergeEInvoice($this->entity))->handle(); - - } + $this->invitation = null; // $this->entity = null; $this->company = null; @@ -227,10 +229,65 @@ class CreateEntityPdf implements ShouldQueue $maker = null; $state = null; - return $file_path; } + /** + * Switch to determine if we need to embed the xml into the PDF itself + * + * @param string $pdf + * @return string + */ + private function checkEInvoice(string $pdf): string + { + if(!$this->entity instanceof Invoice) + return $pdf; + + $e_invoice_type = $this->entity->client->getSetting('e_invoice_type'); + + switch ($e_invoice_type) { + case "EN16931": + 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": + return $this->embedEInvoiceZuGFerD($pdf) ?? $pdf; + //case "Facturae_3.2": + //case "Facturae_3.2.1": + //case "Facturae_3.2.2": + // + default: + return $pdf; + } + + } + + /** + * Embed the .xml file into the PDF + * + * @param string $pdf + * @return string + */ + private function embedEInvoiceZuGFerD(string $pdf): string + { + try { + + $e_rechnung = (new CreateEInvoice($this->entity, true))->handle(); + $pdfBuilder = new ZugferdDocumentPdfBuilder($e_rechnung, $pdf); + $pdfBuilder->generateDocument(); + return $pdfBuilder->downloadString(basename($this->entity->getFileName())); + + } catch (\Exception $e) { + nlog("E_Invoice Merge failed - " . $e->getMessage()); + } + + return $pdf; + } + + public function failed($e) { } diff --git a/app/Jobs/Entity/CreateRawPdf.php b/app/Jobs/Entity/CreateRawPdf.php index 8c3b4d77f7f0..921879021c61 100644 --- a/app/Jobs/Entity/CreateRawPdf.php +++ b/app/Jobs/Entity/CreateRawPdf.php @@ -11,42 +11,43 @@ namespace App\Jobs\Entity; -use App\Exceptions\FilePermissionsFailure; -use App\Jobs\Invoice\MergeEInvoice; -use App\Libraries\MultiDB; +use App\Utils\Ninja; +use App\Models\Quote; use App\Models\Credit; -use App\Models\CreditInvitation; use App\Models\Design; use App\Models\Invoice; -use App\Models\InvoiceInvitation; -use App\Models\Quote; +use App\Utils\HtmlEngine; +use App\Libraries\MultiDB; +use Illuminate\Bus\Queueable; use App\Models\QuoteInvitation; +use App\Utils\Traits\MakesHash; +use App\Models\CreditInvitation; use App\Models\RecurringInvoice; +use App\Utils\PhantomJS\Phantom; +use App\Models\InvoiceInvitation; +use App\Utils\HostedPDF\NinjaPdf; +use App\Utils\Traits\Pdf\PdfMaker; +use Illuminate\Support\Facades\App; +use App\Jobs\Invoice\CreateEInvoice; +use App\Utils\Traits\NumberFormatter; +use App\Utils\Traits\MakesInvoiceHtml; +use Illuminate\Queue\SerializesModels; +use App\Utils\Traits\Pdf\PageNumbering; +use Illuminate\Queue\InteractsWithQueue; +use App\Exceptions\FilePermissionsFailure; use App\Models\RecurringInvoiceInvitation; +use Illuminate\Contracts\Queue\ShouldQueue; +use Illuminate\Foundation\Bus\Dispatchable; +use horstoeko\zugferd\ZugferdDocumentPdfBuilder; use App\Services\PdfMaker\Design as PdfDesignModel; use App\Services\PdfMaker\Design as PdfMakerDesign; use App\Services\PdfMaker\PdfMaker as PdfMakerService; -use App\Utils\HostedPDF\NinjaPdf; -use App\Utils\HtmlEngine; -use App\Utils\Ninja; -use App\Utils\PhantomJS\Phantom; -use App\Utils\Traits\MakesHash; -use App\Utils\Traits\MakesInvoiceHtml; -use App\Utils\Traits\NumberFormatter; -use App\Utils\Traits\Pdf\PageNumbering; -use App\Utils\Traits\Pdf\PdfMaker; -use Illuminate\Bus\Queueable; -use Illuminate\Contracts\Queue\ShouldQueue; -use Illuminate\Foundation\Bus\Dispatchable; -use Illuminate\Queue\InteractsWithQueue; -use Illuminate\Queue\SerializesModels; -use Illuminate\Support\Facades\App; class CreateRawPdf implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, NumberFormatter, MakesInvoiceHtml, PdfMaker, MakesHash, PageNumbering; - public $entity; + public Invoice | Credit | Quote | RecurringInvoice $entity; public $company; @@ -104,6 +105,7 @@ class CreateRawPdf implements ShouldQueue } $entity_design_id = ''; + $path = ''; if ($this->entity instanceof Invoice) { $path = $this->entity->client->invoice_filepath($this->invitation); @@ -203,17 +205,67 @@ class CreateRawPdf implements ShouldQueue if ($pdf) { $maker =null; $state = null; - if ($this->invitation->invoice->client->getSetting('enable_e_invoice') && $this->entity_string == "invoice"){ - $filename = tempnam(sys_get_temp_dir(), 'InvoiceNinja').".pdf"; - file_put_contents($filename, $pdf); - (new \App\Services\Invoice\MergeEInvoice($this->invitation->invoice, $filename))->run(); - return file_get_contents($filename); - }; - return $pdf; + + return $this->checkEInvoice($pdf); } throw new FilePermissionsFailure('Unable to generate the raw PDF'); } + + /** + * Switch to determine if we need to embed the xml into the PDF itself + * + * @param string $pdf + * @return string + */ + private function checkEInvoice(string $pdf): string + { + if(!$this->entity instanceof Invoice) + return $pdf; + + $e_invoice_type = $this->entity->client->getSetting('e_invoice_type'); + + switch ($e_invoice_type) { + case "EN16931": + 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": + return $this->embedEInvoiceZuGFerD($pdf) ?? $pdf; + //case "Facturae_3.2": + //case "Facturae_3.2.1": + //case "Facturae_3.2.2": + // + default: + return $pdf; + } + + } + + /** + * Embed the .xml file into the PDF + * + * @param string $pdf + * @return string + */ + private function embedEInvoiceZuGFerD(string $pdf): string + { + try { + + $e_rechnung = (new CreateEInvoice($this->entity, true))->handle(); + $pdfBuilder = new ZugferdDocumentPdfBuilder($e_rechnung, $pdf); + $pdfBuilder->generateDocument(); + return $pdfBuilder->downloadString(basename($this->entity->getFileName())); + + } catch (\Exception $e) { + nlog("E_Invoice Merge failed - " . $e->getMessage()); + } + + return $pdf; + } public function failed($e) { diff --git a/app/Jobs/Invoice/CreateEInvoice.php b/app/Jobs/Invoice/CreateEInvoice.php index 3988c688b4c8..c68662c4b108 100644 --- a/app/Jobs/Invoice/CreateEInvoice.php +++ b/app/Jobs/Invoice/CreateEInvoice.php @@ -37,7 +37,6 @@ class CreateEInvoice implements ShouldQueue /** * Execute the job. * - * * @return string|ZugferdDocumentBuilder */ public function handle(): string|ZugferdDocumentBuilder @@ -64,13 +63,18 @@ class CreateEInvoice implements ShouldQueue case "XInvoice-Extended": case "XInvoice-BasicWL": case "XInvoice-Basic": - return (new ZugferdEInvoice($this->invoice, $this->returnObject))->run(); + $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: - return (new ZugferdEInvoice($this->invoice, $this->returnObject))->run(); + + $zugferd = (new ZugferdEInvoice($this->invoice))->run(); + + return $this->returnObject ? $zugferd : $zugferd->getXml(); } diff --git a/app/Jobs/Invoice/MergeEInvoice.php b/app/Jobs/Invoice/MergeEInvoice.php deleted file mode 100644 index cae7d110682b..000000000000 --- a/app/Jobs/Invoice/MergeEInvoice.php +++ /dev/null @@ -1,70 +0,0 @@ -invoice->client->getSetting('e_invoice_type'); - switch ($e_invoice_type) { - case "EN16931": - 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": - $this->embedEInvoiceZuGFerD(); - //case "Facturae_3.2": - //case "Facturae_3.2.1": - //case "Facturae_3.2.2": - // - default: - $this->embedEInvoiceZuGFerD(); - break; - } - } - - /** - * @throws \Exception - */ - private function embedEInvoiceZuGFerD(): void - { - try { - $filepath_pdf = !empty($this->pdf_path) ? $this->pdf_path : $this->invoice->service()->getInvoicePdf(); - $disk = config('filesystems.default'); - $e_rechnung = (new CreateEInvoice($this->invoice, true))->handle(); - if (!empty($this->pdf_path)) { - $realpath_pdf = $filepath_pdf; - } else { - $realpath_pdf = Storage::disk($disk)->path($filepath_pdf); - } - if (file_exists($realpath_pdf)) { - $pdfBuilder = new ZugferdDocumentPdfBuilder($e_rechnung, $realpath_pdf); - $pdfBuilder->generateDocument(); - $pdfBuilder->saveDocument($realpath_pdf); - } else { - nlog("E_Invoice Merge failed - file to merge not found"); - } - - } catch (\Exception $e) { - nlog("E_Invoice Merge failed - " . $e->getMessage()); - } - } -} diff --git a/app/Jobs/Invoice/ZipInvoices.php b/app/Jobs/Invoice/ZipInvoices.php index c257e647ccb8..25722b6f6fb2 100644 --- a/app/Jobs/Invoice/ZipInvoices.php +++ b/app/Jobs/Invoice/ZipInvoices.php @@ -73,28 +73,12 @@ class ZipInvoices implements ShouldQueue $invitation = $this->invoices->first()->invitations->first(); $path = $this->invoices->first()->client->invoice_filepath($invitation); - $this->invoices->each(function ($invoice) { - (new CreateEntityPdf($invoice->invitations()->first()))->handle(); - if ($invoice->client->getSetting('enable_e_invoice')){ - (new CreateEInvoice($invoice))->handle(); - (new MergeEInvoice($invoice))->handle(); - } - }); - try { foreach ($this->invoices as $invoice) { - $file = $invoice->service()->getInvoicePdf(); - $zip_file_name = basename($file); - $zipFile->addFromString($zip_file_name, Storage::get($file)); - - if($invoice->client->getSetting('enable_e_invoice')){ - - $xinvoice = $invoice->service()->getEInvoice(); - $xinvoice_zip_file_name = basename($xinvoice); - $zipFile->addFromString($xinvoice_zip_file_name, Storage::get($xinvoice)); - - } + $file = $invoice->service()->getRawInvoicePdf(); + $zip_file_name = $invoice->getFileName(); + $zipFile->addFromString($zip_file_name, $file); } Storage::put($path.$file_name, $zipFile->outputAsString()); diff --git a/app/Models/BaseModel.php b/app/Models/BaseModel.php index 7f8c0d4bca69..bd55d0684203 100644 --- a/app/Models/BaseModel.php +++ b/app/Models/BaseModel.php @@ -276,6 +276,7 @@ class BaseModel extends Model /** * Returns the base64 encoded PDF string of the entity + * @deprecated - unused implementation */ public function fullscreenPdfViewer($invitation = null): string { diff --git a/app/Services/Email/EmailDefaults.php b/app/Services/Email/EmailDefaults.php index a374ddfa353c..e2d833fa9e31 100644 --- a/app/Services/Email/EmailDefaults.php +++ b/app/Services/Email/EmailDefaults.php @@ -322,10 +322,10 @@ class EmailDefaults } /** E-Invoice xml file */ if ($this->email->email_object->settings->enable_e_invoice && $this->email->email_object->entity instanceof Invoice) { - $xinvoice_path = $this->email->email_object->entity->service()->getEInvoice(); + $xml_string = $this->email->email_object->entity->service()->getEInvoice(); - if(Storage::disk(config('filesystems.default'))->exists($xinvoice_path)) - $this->email->email_object->attachments = array_merge($this->email->email_object->attachments, [['file' => base64_encode(Storage::get($xinvoice_path)), 'name' => explode(".", $this->email->email_object->entity->getFileName('xml'))[0]."-e_invoice.xml"]]); + if($xml_string) + $this->email->email_object->attachments = array_merge($this->email->email_object->attachments, [['file' => base64_encode($xml_string), 'name' => explode(".", $this->email->email_object->entity->getFileName('xml'))[0]."-e_invoice.xml"]]); } if (!$this->email->email_object->settings->document_email_attachment || !$this->email->company->account->hasFeature(Account::FEATURE_DOCUMENTS)) { diff --git a/app/Services/Invoice/EInvoice/ZugferdEInvoice.php b/app/Services/Invoice/EInvoice/ZugferdEInvoice.php index 3baf14128f8b..0410422a4d7e 100644 --- a/app/Services/Invoice/EInvoice/ZugferdEInvoice.php +++ b/app/Services/Invoice/EInvoice/ZugferdEInvoice.php @@ -22,12 +22,13 @@ use horstoeko\zugferd\codelists\ZugferdDutyTaxFeeCategories; class ZugferdEInvoice extends AbstractService { + public ZugferdDocumentBuilder $xrechnung; public function __construct(public Invoice $invoice, private readonly bool $returnObject = false, private array $tax_map = []) { } - public function run(): string|ZugferdDocumentBuilder + public function run(): self { $company = $this->invoice->company; @@ -45,10 +46,9 @@ class ZugferdEInvoice extends AbstractService default => ZugferdProfiles::PROFILE_EN16931, }; + $this->xrechnung = ZugferdDocumentBuilder::CreateNew($profile); - $xrechnung = ZugferdDocumentBuilder::CreateNew($profile); - - $xrechnung + $this->xrechnung ->setDocumentSupplyChainEvent(date_create($this->invoice->date)) ->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")) @@ -59,32 +59,32 @@ class ZugferdEInvoice extends AbstractService ->addDocumentPaymentTerm(ctrans("texts.xinvoice_payable", ['payeddue' => date_create($this->invoice->date)->diff(date_create($this->invoice->due_date))->format("%d"), 'paydate' => $this->invoice->due_date])); if (!empty($this->invoice->public_notes)) { - $xrechnung->addDocumentNote($this->invoice->public_notes); + $this->xrechnung->addDocumentNote($this->invoice->public_notes); } if (empty($this->invoice->number)){ - $xrechnung->setDocumentInformation("DRAFT", "380", date_create($this->invoice->date), $this->invoice->client->getCurrencyCode()); + $this->xrechnung->setDocumentInformation("DRAFT", "380", date_create($this->invoice->date), $this->invoice->client->getCurrencyCode()); } else { - $xrechnung->setDocumentInformation($this->invoice->number, "380", date_create($this->invoice->date), $this->invoice->client->getCurrencyCode()); + $this->xrechnung->setDocumentInformation($this->invoice->number, "380", date_create($this->invoice->date), $this->invoice->client->getCurrencyCode()); } if (!empty($this->invoice->po_number)) { - $xrechnung->setDocumentBuyerOrderReferencedDocument($this->invoice->po_number); + $this->xrechnung->setDocumentBuyerOrderReferencedDocument($this->invoice->po_number); } if (empty($client->routing_id)) { - $xrechnung->setDocumentBuyerReference(ctrans("texts.xinvoice_no_buyers_reference")); + $this->xrechnung->setDocumentBuyerReference(ctrans("texts.xinvoice_no_buyers_reference")); } else { - $xrechnung->setDocumentBuyerReference($client->routing_id); + $this->xrechnung->setDocumentBuyerReference($client->routing_id); } if (!empty($client->shipping_address1)){ - $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->xrechnung->setDocumentShipToAddress($client->shipping_address1, $client->shipping_address2, "", $client->shipping_postal_code, $client->shipping_city, $client->shipping_country->iso_3166_2, $client->shipping_state); } - $xrechnung->addDocumentPaymentMean(68, ctrans("texts.xinvoice_online_payment")); + $this->xrechnung->addDocumentPaymentMean(68, ctrans("texts.xinvoice_online_payment")); if (str_contains($company->getSetting('vat_number'), "/")) { - $xrechnung->addDocumentSellerTaxRegistration("FC", $company->getSetting('vat_number')); + $this->xrechnung->addDocumentSellerTaxRegistration("FC", $company->getSetting('vat_number')); } else { - $xrechnung->addDocumentSellerTaxRegistration("VA", $company->getSetting('vat_number')); + $this->xrechnung->addDocumentSellerTaxRegistration("VA", $company->getSetting('vat_number')); } $invoicing_data = $this->invoice->calc(); @@ -92,29 +92,29 @@ class ZugferdEInvoice extends AbstractService //Create line items and calculate taxes foreach ($this->invoice->line_items as $index => $item) { /** @var \App\DataMapper\InvoiceItem $item **/ - $xrechnung->addNewPosition($index) + $this->xrechnung->addNewPosition($index) ->setDocumentPositionGrossPrice($item->gross_line_total) ->setDocumentPositionNetPrice($item->line_total); if (!empty($item->product_key)){ if (!empty($item->notes)){ - $xrechnung->setDocumentPositionProductDetails($item->product_key, $item->notes); + $this->xrechnung->setDocumentPositionProductDetails($item->product_key, $item->notes); } else { - $xrechnung->setDocumentPositionProductDetails($item->product_key); + $this->xrechnung->setDocumentPositionProductDetails($item->product_key); } } else { if (!empty($item->notes)){ - $xrechnung->setDocumentPositionProductDetails($item->notes); + $this->xrechnung->setDocumentPositionProductDetails($item->notes); } else { - $xrechnung->setDocumentPositionProductDetails("no product name defined"); + $this->xrechnung->setDocumentPositionProductDetails("no product name defined"); } } if (isset($item->task_id)) { - $xrechnung->setDocumentPositionQuantity($item->quantity, "HUR"); + $this->xrechnung->setDocumentPositionQuantity($item->quantity, "HUR"); } else { - $xrechnung->setDocumentPositionQuantity($item->quantity, "H87"); + $this->xrechnung->setDocumentPositionQuantity($item->quantity, "H87"); } $linenetamount = $item->line_total; if ($item->discount > 0) { @@ -124,18 +124,18 @@ class ZugferdEInvoice extends AbstractService $linenetamount -= $linenetamount * ($item->discount / 100); } } - $xrechnung->setDocumentPositionLineSummation($linenetamount); + $this->xrechnung->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)) { - $xrechnung->addDocumentPositionTax($taxtype, 'VAT', $item->tax_rate1); + $this->xrechnung->addDocumentPositionTax($taxtype, 'VAT', $item->tax_rate1); $this->addtoTaxMap($taxtype, $linenetamount, $item->tax_rate1); } elseif (!empty($item->tax_name2)) { - $xrechnung->addDocumentPositionTax($taxtype, 'VAT', $item->tax_rate2); + $this->xrechnung->addDocumentPositionTax($taxtype, 'VAT', $item->tax_rate2); $this->addtoTaxMap($taxtype, $linenetamount, $item->tax_rate2); } elseif (!empty($item->tax_name3)) { - $xrechnung->addDocumentPositionTax($taxtype, 'VAT', $item->tax_rate3); + $this->xrechnung->addDocumentPositionTax($taxtype, 'VAT', $item->tax_rate3); $this->addtoTaxMap($taxtype, $linenetamount, $item->tax_rate3); } else { nlog("Can't add correct tax position"); @@ -143,45 +143,45 @@ class ZugferdEInvoice extends AbstractService } else { if (!empty($this->invoice->tax_name1)) { $taxtype = $this->getTaxType($this->invoice->tax_name1); - $xrechnung->addDocumentPositionTax($taxtype, 'VAT', $this->invoice->tax_rate1); + $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); - $xrechnung->addDocumentPositionTax($taxtype, 'VAT', $this->invoice->tax_rate2); + $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); - $xrechnung->addDocumentPositionTax($taxtype, 'VAT', $this->invoice->tax_rate3); + $this->xrechnung->addDocumentPositionTax($taxtype, 'VAT', $this->invoice->tax_rate3); $this->addtoTaxMap($taxtype, $linenetamount, $this->invoice->tax_rate3); } else { $taxtype = ZugferdDutyTaxFeeCategories::ZERO_RATED_GOODS; - $xrechnung->addDocumentPositionTax($taxtype, 'VAT', 0); + $this->xrechnung->addDocumentPositionTax($taxtype, 'VAT', 0); $this->addtoTaxMap($taxtype, $linenetamount, 0); nlog("Can't add correct tax position"); } } } - $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->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); foreach ($this->tax_map as $item){ - $xrechnung->addDocumentTax($item["tax_type"], "VAT", $item["net_amount"], $item["tax_rate"]*$item["net_amount"], $item["tax_rate"]*100); - } - $disk = config('filesystems.default'); - - if (!Storage::disk($disk)->exists($client->e_invoice_filepath($this->invoice->invitations->first()))) { - Storage::makeDirectory($client->e_invoice_filepath($this->invoice->invitations->first())); + $this->xrechnung->addDocumentTax($item["tax_type"], "VAT", $item["net_amount"], $item["tax_rate"]*$item["net_amount"], $item["tax_rate"]*100); } - $xrechnung->writeFile(Storage::disk($disk)->path($client->e_invoice_filepath($this->invoice->invitations->first()) . $this->invoice->getFileName("xml"))); // The validity can be checked using https://portal3.gefeg.com/invoice/validation or https://e-rechnung.bayern.de/app/#/upload - if ($this->returnObject){ - return $xrechnung; - } - - throw new \Exception("Invalid e invoice object"); - - // return $client->e_invoice_filepath($this->invoice->invitations->first()) . $this->invoice->getFileName("xml"); + return $this; + + } + + /** + * Returns the XML document + * in string format + * + * @return string + */ + public function getXml(): string + { + return $this->xrechnung->getContent(); } private function getTaxType($name): string diff --git a/app/Services/Invoice/GetInvoiceEInvoice.php b/app/Services/Invoice/GetInvoiceEInvoice.php deleted file mode 100644 index 0434b6917fd0..000000000000 --- a/app/Services/Invoice/GetInvoiceEInvoice.php +++ /dev/null @@ -1,52 +0,0 @@ -contact) { - $this->contact = $this->invoice->client->primary_contact()->first() ?: $this->invoice->client->contacts()->first(); - } - - $invitation = $this->invoice->invitations->where('client_contact_id', $this->contact->id)->first(); - - if (! $invitation) { - $invitation = $this->invoice->invitations->first(); - } - - $file_path = $this->invoice->client->e_invoice_filepath($this->invoice->invitations->first()). $this->invoice->getFileName("xml"); - - // $disk = 'public'; - $disk = config('filesystems.default'); - - $file = Storage::disk($disk)->exists($file_path); - - if (! $file) { - $file_path = (new CreateEInvoice($this->invoice))->handle(); - (new \App\Jobs\Invoice\MergeEInvoice($this->invoice))->handle(); - - } - return $file_path; - } -} diff --git a/app/Services/Invoice/GetInvoicePdf.php b/app/Services/Invoice/GetInvoicePdf.php index 8044e8a290d6..9211a15bb8fd 100644 --- a/app/Services/Invoice/GetInvoicePdf.php +++ b/app/Services/Invoice/GetInvoicePdf.php @@ -13,7 +13,6 @@ namespace App\Services\Invoice; use App\Jobs\Entity\CreateEntityPdf; use App\Jobs\Invoice\CreateEInvoice; -use App\Jobs\Invoice\MergeEInvoice; use App\Models\ClientContact; use App\Models\Invoice; use App\Services\AbstractService; @@ -49,10 +48,7 @@ class GetInvoicePdf extends AbstractService if (! $file) { $file_path = (new CreateEntityPdf($invitation))->handle(); } - if ($this->invoice->client->getSetting('enable_e_invoice')){ - (new CreateEInvoice($this->invoice))->handle(); - (new MergeEInvoice($this->invoice, $file_path))->handle(); - } + return $file_path; } } diff --git a/app/Services/Invoice/InvoiceService.php b/app/Services/Invoice/InvoiceService.php index d350d28a597c..148f0cae88d5 100644 --- a/app/Services/Invoice/InvoiceService.php +++ b/app/Services/Invoice/InvoiceService.php @@ -13,6 +13,7 @@ namespace App\Services\Invoice; use App\Events\Invoice\InvoiceWasArchived; use App\Jobs\Entity\CreateEntityPdf; +use App\Jobs\Entity\CreateRawPdf; use App\Jobs\Inventory\AdjustProductInventory; use App\Jobs\Invoice\CreateEInvoice; use App\Libraries\Currency\Conversion\CurrencyApi; @@ -187,6 +188,13 @@ class InvoiceService return (new GetInvoicePdf($this->invoice, $contact))->run(); } + public function getRawInvoicePdf($contact = null) + { + $invitation = $contact ? $this->invoice->invitations()->where('contact_id', $contact->id)->first() : $this->invoice->invitations()->first(); + + return (new CreateRawPdf($invitation, $invitation->company->db))->handle(); + } + public function getInvoiceDeliveryNote(Invoice $invoice, \App\Models\ClientContact $contact = null) { return (new GenerateDeliveryNote($invoice, $contact))->run(); @@ -194,13 +202,9 @@ class InvoiceService public function getEInvoice($contact = null) { - return (new GetInvoiceEInvoice($this->invoice, $contact))->run(); + return (new CreateEInvoice($this->invoice))->handle(); } - public function mergeEInvoice($contact = null): void - { - (new MergeEInvoice($this->invoice, $contact))->run(); - } public function sendEmail($contact = null) { $send_email = new SendEmail($this->invoice, null, $contact); @@ -464,13 +468,6 @@ class InvoiceService if ($force) { $this->invoice->invitations->each(function ($invitation) { (new CreateEntityPdf($invitation))->handle(); - - if ($invitation->invoice->client->getSetting('enable_e_invoice') && $invitation instanceof InvoiceInvitation) - { - (new CreateEInvoice($invitation->invoice))->handle(); - (new MergeEInvoice($invitation->invoice))->run(); - } - }); return $this; From 8f5a63eb946006972a11c0687eeb0b2452c11835 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 16 Aug 2023 19:57:03 +1000 Subject: [PATCH 03/11] Cleanup --- app/Http/Livewire/PdfSlot.php | 1 - app/Services/Invoice/MergeEInvoice.php | 29 -------------------------- 2 files changed, 30 deletions(-) delete mode 100644 app/Services/Invoice/MergeEInvoice.php diff --git a/app/Http/Livewire/PdfSlot.php b/app/Http/Livewire/PdfSlot.php index 3a44b5b3a8bd..a97444ba89ee 100644 --- a/app/Http/Livewire/PdfSlot.php +++ b/app/Http/Livewire/PdfSlot.php @@ -26,7 +26,6 @@ use Illuminate\Support\Facades\Cache; use App\Models\PurchaseOrderInvitation; use App\Models\RecurringInvoiceInvitation; use App\Jobs\Vendor\CreatePurchaseOrderPdf; -use App\Services\Invoice\GetInvoiceEInvoice; use App\Services\PdfMaker\Designs\Utilities\DesignHelpers; class PdfSlot extends Component diff --git a/app/Services/Invoice/MergeEInvoice.php b/app/Services/Invoice/MergeEInvoice.php deleted file mode 100644 index ad80c49cd821..000000000000 --- a/app/Services/Invoice/MergeEInvoice.php +++ /dev/null @@ -1,29 +0,0 @@ -pdf_path)) { - (new \App\Jobs\Invoice\MergeEInvoice($this->invoice, $this->pdf_path))->handle(); - } - else { - (new \App\Jobs\Invoice\MergeEInvoice($this->invoice))->handle(); - } - - } -} From 822aed5a6117e2836ef1526c352eb9aed6c93e81 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 16 Aug 2023 20:52:01 +1000 Subject: [PATCH 04/11] Fixes for einvoicing --- composer.json | 10 +++++-- composer.lock | 83 ++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 73 insertions(+), 20 deletions(-) diff --git a/composer.json b/composer.json index 0d071ca1da10..d6ae389afaff 100644 --- a/composer.json +++ b/composer.json @@ -53,7 +53,7 @@ "halaxa/json-machine": "^0.7.0", "hashids/hashids": "^4.0", "hedii/laravel-gelf-logger": "^7.0", - "horstoeko/zugferd": "^1", + "horstoeko/zugferd": "dev-master", "imdhemy/laravel-purchases": "^1.7", "intervention/image": "^2.5", "invoiceninja/inspector": "^1.0", @@ -170,5 +170,11 @@ } }, "minimum-stability": "dev", - "prefer-stable": true + "prefer-stable": true, + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/turbo124/zugferd.git" + } + ] } diff --git a/composer.lock b/composer.lock index 3d3cb2598a93..6e9124a5e1b7 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "9d7348352c913eb82fcca2e67670e1f8", + "content-hash": "b48cfe4230f3fd18612bc2852ae2e029", "packages": [ { "name": "adrienrn/php-mimetyper", @@ -3344,16 +3344,16 @@ }, { "name": "horstoeko/zugferd", - "version": "v1.0.20", + "version": "dev-master", "source": { "type": "git", - "url": "https://github.com/horstoeko/zugferd.git", - "reference": "725c130ec0a09d72cf54b3d819840f3959a5502c" + "url": "https://github.com/turbo124/zugferd.git", + "reference": "2d27efab66d948266dd439db5caeeb8ed1756b4c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/horstoeko/zugferd/zipball/725c130ec0a09d72cf54b3d819840f3959a5502c", - "reference": "725c130ec0a09d72cf54b3d819840f3959a5502c", + "url": "https://api.github.com/repos/turbo124/zugferd/zipball/2d27efab66d948266dd439db5caeeb8ed1756b4c", + "reference": "2d27efab66d948266dd439db5caeeb8ed1756b4c", "shasum": "" }, "require": { @@ -3379,17 +3379,60 @@ "sebastian/phpcpd": "^6", "squizlabs/php_codesniffer": "^3" }, + "default-branch": true, "type": "package", "autoload": { "psr-4": { "horstoeko\\zugferd\\": "src", + "horstoeko\\zugferd\\rsm\\": "src/entities/rsm", "horstoeko\\zugferd\\qdt\\": "src/entities/qdt", "horstoeko\\zugferd\\ram\\": "src/entities/ram", - "horstoeko\\zugferd\\rsm\\": "src/entities/rsm", "horstoeko\\zugferd\\udt\\": "src/entities/udt" } }, - "notification-url": "https://packagist.org/downloads/", + "autoload-dev": { + "psr-4": { + "horstoeko\\zugferd\\tests\\": "tests" + } + }, + "scripts": { + "tests": [ + "./vendor/bin/phpunit ./tests/" + ], + "testsreal": [ + "./vendor/bin/phpunit --configuration ./build/phpunit.xml" + ], + "phpcs": [ + "./vendor/bin/phpcs --standard=./build/phpcsrules.xml --extensions=php --ignore=autoload.php ./src ./tests" + ], + "phpcs12": [ + "./vendor/bin/phpcs --standard=./build/phpcsrules_psr12.xml --extensions=php --ignore=autoload.php ./src ./tests" + ], + "phpcbf": [ + "./vendor/bin/phpcbf -q ./src ./tests" + ], + "phpcbf1": [ + "./vendor/bin/phpcbf --standard=./build/phpcsrules_psr1.xml -q ./src ./tests" + ], + "phpcbf2": [ + "./vendor/bin/phpcbf --standard=./build/phpcsrules_psr2.xml -q ./src ./tests" + ], + "phpcbf12": [ + "./vendor/bin/phpcbf --standard=./build/phpcsrules_psr12.xml -q ./src ./tests" + ], + "phpcbfsq": [ + "./vendor/bin/phpcbf --standard=./build/phpcsrules_squiz.xml -q ./src ./tests" + ], + "phpstan": [ + "./vendor/bin/phpstan analyze -c ./build/phpstan.neon --autoload-file=vendor/autoload.php --no-interaction --no-progress --xdebug" + ], + "phpstan_cs": [ + "./vendor/bin/phpstan analyze -c ./build/phpstan.neon --autoload-file=vendor/autoload.php --no-interaction --no-progress --error-format=checkstyle --xdebug" + ], + "makedoc": [ + "phing -f ./build.xml projectdoc" + ] + }, "license": [ "MIT" ], @@ -3403,17 +3446,16 @@ "description": "A library for creating and reading european electronic invoices", "homepage": "https://github.com/horstoeko/zugferd", "keywords": [ - "ZUGFeRD", "electronic", "factur-x", "invoice", - "xrechnung" + "xrechnung", + "zugferd" ], "support": { - "issues": "https://github.com/horstoeko/zugferd/issues", - "source": "https://github.com/horstoeko/zugferd/tree/v1.0.20" + "source": "https://github.com/turbo124/zugferd/tree/add_string_output_for_pdf" }, - "time": "2023-07-04T15:21:32+00:00" + "time": "2023-08-16T10:23:41+00:00" }, { "name": "http-interop/http-factory-guzzle", @@ -6248,25 +6290,29 @@ }, { "name": "nesbot/carbon", - "version": "2.68.1", + "version": "2.69.0", "source": { "type": "git", "url": "https://github.com/briannesbitt/Carbon.git", - "reference": "4f991ed2a403c85efbc4f23eb4030063fdbe01da" + "reference": "4308217830e4ca445583a37d1bf4aff4153fa81c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/4f991ed2a403c85efbc4f23eb4030063fdbe01da", - "reference": "4f991ed2a403c85efbc4f23eb4030063fdbe01da", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/4308217830e4ca445583a37d1bf4aff4153fa81c", + "reference": "4308217830e4ca445583a37d1bf4aff4153fa81c", "shasum": "" }, "require": { "ext-json": "*", "php": "^7.1.8 || ^8.0", + "psr/clock": "^1.0", "symfony/polyfill-mbstring": "^1.0", "symfony/polyfill-php80": "^1.16", "symfony/translation": "^3.4 || ^4.0 || ^5.0 || ^6.0" }, + "provide": { + "psr/clock-implementation": "1.0" + }, "require-dev": { "doctrine/dbal": "^2.0 || ^3.1.4", "doctrine/orm": "^2.7", @@ -6346,7 +6392,7 @@ "type": "tidelift" } ], - "time": "2023-06-20T18:29:04+00:00" + "time": "2023-08-03T09:00:52+00:00" }, { "name": "nette/schema", @@ -18319,6 +18365,7 @@ "aliases": [], "minimum-stability": "dev", "stability-flags": { + "horstoeko/zugferd": 20, "webpatser/laravel-countries": 20 }, "prefer-stable": true, From 00ff163d4fe427dbcbceebc35b7f139a5ae90b66 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 16 Aug 2023 20:59:42 +1000 Subject: [PATCH 05/11] Fixes for tests --- tests/Unit/EInvoiceTest.php | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/tests/Unit/EInvoiceTest.php b/tests/Unit/EInvoiceTest.php index 41349c264b04..59c2024017a1 100644 --- a/tests/Unit/EInvoiceTest.php +++ b/tests/Unit/EInvoiceTest.php @@ -43,9 +43,7 @@ class EInvoiceTest extends TestCase $this->invoice->client->routing_id = 'DE123456789'; $this->invoice->client->save(); $e_invoice = (new CreateEInvoice($this->invoice))->handle(); - (new \App\Jobs\Invoice\MergeEInvoice($this->invoice))->handle(); - $this->assertNotNull($e_invoice); - $this->assertTrue(Storage::exists($e_invoice)); + $this->assertIsString($e_invoice); } /** @@ -58,7 +56,7 @@ class EInvoiceTest extends TestCase $this->invoice->client->save(); $e_invoice = (new CreateEInvoice($this->invoice))->handle(); - $document = ZugferdDocumentReader::readAndGuessFromFile(Storage::path($e_invoice)); + $document = ZugferdDocumentReader::readAndGuessFromContent($e_invoice); $document->getDocumentInformation($documentno, $documenttypecode, $documentdate, $documentcurrency, $taxcurrency, $taxname, $documentlangeuage, $rest); $this->assertEquals($this->invoice->number, $documentno); } @@ -69,9 +67,7 @@ class EInvoiceTest extends TestCase public function checkEmbededPDFFile() { $pdf = (new CreateEntityPdf($this->invoice->invitations()->first()))->handle(); - (new CreateEInvoice($this->invoice))->handle(); - (new \App\Jobs\Invoice\MergeEInvoice($this->invoice))->handle(); - $document = ZugferdDocumentReader::readAndGuessFromFile($pdf); + $document = ZugferdDocumentReader::readAndGuessFromContent($pdf); $document->getDocumentInformation($documentno, $documenttypecode, $documentdate, $documentcurrency, $taxcurrency, $taxname, $documentlangeuage, $rest); $this->assertEquals($this->invoice->number, $documentno); } From 18f3b6522f97519134e2f42a0d94e33d91343f94 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 16 Aug 2023 22:05:35 +1000 Subject: [PATCH 06/11] Fixes for adding back xml e invoices when zipping in bulk --- app/Jobs/Invoice/ZipInvoices.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/Jobs/Invoice/ZipInvoices.php b/app/Jobs/Invoice/ZipInvoices.php index 25722b6f6fb2..dc5b39cf56f5 100644 --- a/app/Jobs/Invoice/ZipInvoices.php +++ b/app/Jobs/Invoice/ZipInvoices.php @@ -75,9 +75,16 @@ class ZipInvoices implements ShouldQueue try { + foreach ($this->invoices as $invoice) { + + if ($invoice->client->getSetting('enable_e_invoice')) { + $xml = $invoice->service()->getEInvoice(); + $zipFile->addFromString($invoice->getFileName("xml"), $xml); + } + $file = $invoice->service()->getRawInvoicePdf(); - $zip_file_name = $invoice->getFileName(); +$zip_file_name = $invoice->getFileName(); $zipFile->addFromString($zip_file_name, $file); } From 156aa5cc55748fb6c70eea12a9e28322594385a5 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 17 Aug 2023 08:18:15 +1000 Subject: [PATCH 07/11] Updates for composer --- composer.json | 10 +--- composer.lock | 127 +++++++++++++++++--------------------------------- 2 files changed, 46 insertions(+), 91 deletions(-) diff --git a/composer.json b/composer.json index d6ae389afaff..0d071ca1da10 100644 --- a/composer.json +++ b/composer.json @@ -53,7 +53,7 @@ "halaxa/json-machine": "^0.7.0", "hashids/hashids": "^4.0", "hedii/laravel-gelf-logger": "^7.0", - "horstoeko/zugferd": "dev-master", + "horstoeko/zugferd": "^1", "imdhemy/laravel-purchases": "^1.7", "intervention/image": "^2.5", "invoiceninja/inspector": "^1.0", @@ -170,11 +170,5 @@ } }, "minimum-stability": "dev", - "prefer-stable": true, - "repositories": [ - { - "type": "vcs", - "url": "https://github.com/turbo124/zugferd.git" - } - ] + "prefer-stable": true } diff --git a/composer.lock b/composer.lock index 6e9124a5e1b7..2be5001245e6 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b48cfe4230f3fd18612bc2852ae2e029", + "content-hash": "9d7348352c913eb82fcca2e67670e1f8", "packages": [ { "name": "adrienrn/php-mimetyper", @@ -424,16 +424,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.278.3", + "version": "3.279.0", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "596534c0627d8b38597061341e99b460437d1a16" + "reference": "7b3d38cfccd393add0ea0ce281de91846967c61e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/596534c0627d8b38597061341e99b460437d1a16", - "reference": "596534c0627d8b38597061341e99b460437d1a16", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/7b3d38cfccd393add0ea0ce281de91846967c61e", + "reference": "7b3d38cfccd393add0ea0ce281de91846967c61e", "shasum": "" }, "require": { @@ -442,11 +442,11 @@ "ext-pcre": "*", "ext-simplexml": "*", "guzzlehttp/guzzle": "^6.5.8 || ^7.4.5", - "guzzlehttp/promises": "^1.4.0", + "guzzlehttp/promises": "^1.4.0 || ^2.0", "guzzlehttp/psr7": "^1.9.1 || ^2.4.5", "mtdowling/jmespath.php": "^2.6", - "php": ">=5.5", - "psr/http-message": "^1.0" + "php": ">=7.2.5", + "psr/http-message": "^1.0 || ^2.0" }, "require-dev": { "andrewsville/php-token-reflection": "^1.4", @@ -461,7 +461,7 @@ "ext-sockets": "*", "nette/neon": "^2.3", "paragonie/random_compat": ">= 2", - "phpunit/phpunit": "^4.8.35 || ^5.6.3 || ^9.5", + "phpunit/phpunit": "^5.6.3 || ^8.5 || ^9.5", "psr/cache": "^1.0", "psr/simple-cache": "^1.0", "sebastian/comparator": "^1.2.3 || ^4.0", @@ -513,9 +513,9 @@ "support": { "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80", "issues": "https://github.com/aws/aws-sdk-php/issues", - "source": "https://github.com/aws/aws-sdk-php/tree/3.278.3" + "source": "https://github.com/aws/aws-sdk-php/tree/3.279.0" }, - "time": "2023-08-15T18:07:55+00:00" + "time": "2023-08-16T18:18:34+00:00" }, { "name": "bacon/bacon-qr-code", @@ -2832,29 +2832,33 @@ }, { "name": "guzzlehttp/promises", - "version": "1.5.3", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "67ab6e18aaa14d753cc148911d273f6e6cb6721e" + "reference": "111166291a0f8130081195ac4556a5587d7f1b5d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/67ab6e18aaa14d753cc148911d273f6e6cb6721e", - "reference": "67ab6e18aaa14d753cc148911d273f6e6cb6721e", + "url": "https://api.github.com/repos/guzzle/promises/zipball/111166291a0f8130081195ac4556a5587d7f1b5d", + "reference": "111166291a0f8130081195ac4556a5587d7f1b5d", "shasum": "" }, "require": { - "php": ">=5.5" + "php": "^7.2.5 || ^8.0" }, "require-dev": { - "symfony/phpunit-bridge": "^4.4 || ^5.1" + "bamarni/composer-bin-plugin": "^1.8.1", + "phpunit/phpunit": "^8.5.29 || ^9.5.23" }, "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, "autoload": { - "files": [ - "src/functions_include.php" - ], "psr-4": { "GuzzleHttp\\Promise\\": "src/" } @@ -2891,7 +2895,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/1.5.3" + "source": "https://github.com/guzzle/promises/tree/2.0.1" }, "funding": [ { @@ -2907,7 +2911,7 @@ "type": "tidelift" } ], - "time": "2023-05-21T12:31:43+00:00" + "time": "2023-08-03T15:11:55+00:00" }, { "name": "guzzlehttp/psr7", @@ -3344,16 +3348,16 @@ }, { "name": "horstoeko/zugferd", - "version": "dev-master", + "version": "v1.0.23", "source": { "type": "git", - "url": "https://github.com/turbo124/zugferd.git", - "reference": "2d27efab66d948266dd439db5caeeb8ed1756b4c" + "url": "https://github.com/horstoeko/zugferd.git", + "reference": "bb55417be4c4de8deb0113e832feeaf7b4d3984e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/turbo124/zugferd/zipball/2d27efab66d948266dd439db5caeeb8ed1756b4c", - "reference": "2d27efab66d948266dd439db5caeeb8ed1756b4c", + "url": "https://api.github.com/repos/horstoeko/zugferd/zipball/bb55417be4c4de8deb0113e832feeaf7b4d3984e", + "reference": "bb55417be4c4de8deb0113e832feeaf7b4d3984e", "shasum": "" }, "require": { @@ -3379,60 +3383,17 @@ "sebastian/phpcpd": "^6", "squizlabs/php_codesniffer": "^3" }, - "default-branch": true, "type": "package", "autoload": { "psr-4": { "horstoeko\\zugferd\\": "src", - "horstoeko\\zugferd\\rsm\\": "src/entities/rsm", "horstoeko\\zugferd\\qdt\\": "src/entities/qdt", "horstoeko\\zugferd\\ram\\": "src/entities/ram", + "horstoeko\\zugferd\\rsm\\": "src/entities/rsm", "horstoeko\\zugferd\\udt\\": "src/entities/udt" } }, - "autoload-dev": { - "psr-4": { - "horstoeko\\zugferd\\tests\\": "tests" - } - }, - "scripts": { - "tests": [ - "./vendor/bin/phpunit ./tests/" - ], - "testsreal": [ - "./vendor/bin/phpunit --configuration ./build/phpunit.xml" - ], - "phpcs": [ - "./vendor/bin/phpcs --standard=./build/phpcsrules.xml --extensions=php --ignore=autoload.php ./src ./tests" - ], - "phpcs12": [ - "./vendor/bin/phpcs --standard=./build/phpcsrules_psr12.xml --extensions=php --ignore=autoload.php ./src ./tests" - ], - "phpcbf": [ - "./vendor/bin/phpcbf -q ./src ./tests" - ], - "phpcbf1": [ - "./vendor/bin/phpcbf --standard=./build/phpcsrules_psr1.xml -q ./src ./tests" - ], - "phpcbf2": [ - "./vendor/bin/phpcbf --standard=./build/phpcsrules_psr2.xml -q ./src ./tests" - ], - "phpcbf12": [ - "./vendor/bin/phpcbf --standard=./build/phpcsrules_psr12.xml -q ./src ./tests" - ], - "phpcbfsq": [ - "./vendor/bin/phpcbf --standard=./build/phpcsrules_squiz.xml -q ./src ./tests" - ], - "phpstan": [ - "./vendor/bin/phpstan analyze -c ./build/phpstan.neon --autoload-file=vendor/autoload.php --no-interaction --no-progress --xdebug" - ], - "phpstan_cs": [ - "./vendor/bin/phpstan analyze -c ./build/phpstan.neon --autoload-file=vendor/autoload.php --no-interaction --no-progress --error-format=checkstyle --xdebug" - ], - "makedoc": [ - "phing -f ./build.xml projectdoc" - ] - }, + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -3446,16 +3407,17 @@ "description": "A library for creating and reading european electronic invoices", "homepage": "https://github.com/horstoeko/zugferd", "keywords": [ + "ZUGFeRD", "electronic", "factur-x", "invoice", - "xrechnung", - "zugferd" + "xrechnung" ], "support": { - "source": "https://github.com/turbo124/zugferd/tree/add_string_output_for_pdf" + "issues": "https://github.com/horstoeko/zugferd/issues", + "source": "https://github.com/horstoeko/zugferd/tree/v1.0.23" }, - "time": "2023-08-16T10:23:41+00:00" + "time": "2023-08-16T17:39:36+00:00" }, { "name": "http-interop/http-factory-guzzle", @@ -5902,16 +5864,16 @@ }, { "name": "moneyphp/money", - "version": "v4.1.1", + "version": "v4.2.0", "source": { "type": "git", "url": "https://github.com/moneyphp/money.git", - "reference": "9682220995ffd396843be5b4ee1e5f2c2d6ecee2" + "reference": "f660ab7f1d7a4c2ffdd30f50c55ed2c95c26fc3f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/moneyphp/money/zipball/9682220995ffd396843be5b4ee1e5f2c2d6ecee2", - "reference": "9682220995ffd396843be5b4ee1e5f2c2d6ecee2", + "url": "https://api.github.com/repos/moneyphp/money/zipball/f660ab7f1d7a4c2ffdd30f50c55ed2c95c26fc3f", + "reference": "f660ab7f1d7a4c2ffdd30f50c55ed2c95c26fc3f", "shasum": "" }, "require": { @@ -5985,9 +5947,9 @@ ], "support": { "issues": "https://github.com/moneyphp/money/issues", - "source": "https://github.com/moneyphp/money/tree/v4.1.1" + "source": "https://github.com/moneyphp/money/tree/v4.2.0" }, - "time": "2023-04-11T09:18:34+00:00" + "time": "2023-08-16T14:31:24+00:00" }, { "name": "monolog/monolog", @@ -18365,7 +18327,6 @@ "aliases": [], "minimum-stability": "dev", "stability-flags": { - "horstoeko/zugferd": 20, "webpatser/laravel-countries": 20 }, "prefer-stable": true, From 035b683eb74958baa35eaa51103a378a22446369 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 17 Aug 2023 08:58:10 +1000 Subject: [PATCH 08/11] Fixes for types --- app/Console/Commands/CheckData.php | 1 + .../Requests/Shop/StoreShopClientRequest.php | 2 +- app/Utils/Traits/Inviteable.php | 6 ++--- phpstan.neon | 1 + .../components/livewire/pdf-slot.blade.php | 22 ++++++++++--------- 5 files changed, 18 insertions(+), 14 deletions(-) diff --git a/app/Console/Commands/CheckData.php b/app/Console/Commands/CheckData.php index 8f49531ea467..7d931c1022fa 100644 --- a/app/Console/Commands/CheckData.php +++ b/app/Console/Commands/CheckData.php @@ -35,6 +35,7 @@ use App\Models\BankTransaction; use App\Models\QuoteInvitation; use Illuminate\Console\Command; use App\Models\CreditInvitation; +use App\Models\RecurringInvoice; use App\Models\InvoiceInvitation; use App\DataMapper\ClientSettings; use Illuminate\Support\Facades\DB; diff --git a/app/Http/Requests/Shop/StoreShopClientRequest.php b/app/Http/Requests/Shop/StoreShopClientRequest.php index beffeeac2f1d..140dc08a904b 100644 --- a/app/Http/Requests/Shop/StoreShopClientRequest.php +++ b/app/Http/Requests/Shop/StoreShopClientRequest.php @@ -108,7 +108,7 @@ class StoreShopClientRequest extends Request $settings->currency_id = $this->getCurrencyCode($input['currency_code']); } - $input['settings'] = $settings; + $input['settings'] = (array)$settings; if (isset($input['contacts'])) { foreach ($input['contacts'] as $key => $contact) { diff --git a/app/Utils/Traits/Inviteable.php b/app/Utils/Traits/Inviteable.php index ed7c7056845c..13a161462aae 100644 --- a/app/Utils/Traits/Inviteable.php +++ b/app/Utils/Traits/Inviteable.php @@ -79,7 +79,7 @@ trait Inviteable if (Ninja::isHosted()) { $domain = $this->company->domain(); } else { - $domain = strlen($this->company->portal_domain) > 5 ? $this->company->portal_domain : config('ninja.app_url'); + $domain = strlen($this->company->portal_domain ?? '') > 5 ? $this->company->portal_domain : config('ninja.app_url'); } $entity_type = Str::snake(class_basename($this->entityType())); @@ -94,7 +94,7 @@ trait Inviteable if (Ninja::isHosted()) { $domain = $this->company->domain(); } else { - $domain = strlen($this->company->portal_domain) > 5 ? $this->company->portal_domain : config('ninja.app_url'); + $domain = strlen($this->company->portal_domain ?? '') > 5 ? $this->company->portal_domain : config('ninja.app_url'); } switch ($this->company->portal_mode) { @@ -119,7 +119,7 @@ trait Inviteable if (Ninja::isHosted()) { $domain = $this->company->domain(); } else { - $domain = strlen($this->company->portal_domain) > 5 ? $this->company->portal_domain : config('ninja.app_url'); + $domain = strlen($this->company->portal_domain ?? '') > 5 ? $this->company->portal_domain : config('ninja.app_url'); } switch ($this->company->portal_mode) { diff --git a/phpstan.neon b/phpstan.neon index ff93e96cc5b3..6bc4f9205594 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -13,6 +13,7 @@ parameters: - 'app/DataMapper/Analytics/*' - 'app/PaymentDrivers/Authorize/*' - 'app/Utils/Traits/*' + - 'resources/views/*' universalObjectCratesClasses: - App\DataMapper\Tax\RuleInterface - App\DataMapper\FeesAndLimits 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 290de387db96..d15f748c6e23 100644 --- a/resources/views/portal/ninja2020/components/livewire/pdf-slot.blade.php +++ b/resources/views/portal/ninja2020/components/livewire/pdf-slot.blade.php @@ -1,5 +1,5 @@
-
+
- + @if($settings->enable_e_invoice && $entity_type == 'invoice') + + @endif