From b2569710b326bf7dfabc94e1d60ce0db5282e49a Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 22 Nov 2024 17:21:50 +1100 Subject: [PATCH 1/6] Fixes for tests --- tests/Unit/LicenseTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Unit/LicenseTest.php b/tests/Unit/LicenseTest.php index 58b6003814..48e6849ae9 100644 --- a/tests/Unit/LicenseTest.php +++ b/tests/Unit/LicenseTest.php @@ -63,12 +63,12 @@ class LicenseTest extends TestCase { $tes = [ [ - 'legal_entity_id' => rand(1,100), + 'legal_entity_id' => 22, 'company_key' => \Illuminate\Support\Str::random(32), 'received_documents' => [] ], [ - 'legal_entity_id' => rand(1,100), + 'legal_entity_id' => 33, 'company_key' => \Illuminate\Support\Str::random(32), 'received_documents' => [] ], From 9d6236f4a6b2b9a94b5a37b45a2f590b0b5df79e Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 23 Nov 2024 09:07:18 +1100 Subject: [PATCH 2/6] Support injecting payment means into zugferd documents --- app/Http/Controllers/EInvoiceController.php | 2 - .../EInvoice/Peppol/UpdateEntityRequest.php | 20 +++---- .../EDocument/Standards/ZugferdEDokument.php | 55 ++++++++++++++++--- 3 files changed, 56 insertions(+), 21 deletions(-) diff --git a/app/Http/Controllers/EInvoiceController.php b/app/Http/Controllers/EInvoiceController.php index a5fb9b8ccd..c9b915f4bb 100644 --- a/app/Http/Controllers/EInvoiceController.php +++ b/app/Http/Controllers/EInvoiceController.php @@ -66,8 +66,6 @@ class EInvoiceController extends BaseController $payment_means_array = $request->input('payment_means', []); - nlog($payment_means_array); - $einvoice->PaymentMeans = []; foreach ($payment_means_array as $payment_means) { diff --git a/app/Http/Requests/EInvoice/Peppol/UpdateEntityRequest.php b/app/Http/Requests/EInvoice/Peppol/UpdateEntityRequest.php index 8ea3a9a1d0..09e923702c 100644 --- a/app/Http/Requests/EInvoice/Peppol/UpdateEntityRequest.php +++ b/app/Http/Requests/EInvoice/Peppol/UpdateEntityRequest.php @@ -53,14 +53,14 @@ class UpdateEntityRequest extends FormRequest $this->replace($input); } - public function after(): array - { - return [ - function (Validator $validator) { - if ($this->input('acts_as_sender') === false && $this->input('acts_as_receiver') === false) { - $validator->errors()->add('acts_as_receiver', ctrans('texts.acts_as_must_be_true')); - } - } - ]; - } + // public function after(): array + // { + // return [ + // function (Validator $validator) { + // if ($this->input('acts_as_sender') === false && $this->input('acts_as_receiver') === false) { + // $validator->errors()->add('acts_as_receiver', ctrans('texts.acts_as_must_be_true')); + // } + // } + // ]; + // } } diff --git a/app/Services/EDocument/Standards/ZugferdEDokument.php b/app/Services/EDocument/Standards/ZugferdEDokument.php index a3f79ff1bf..149a887a9f 100644 --- a/app/Services/EDocument/Standards/ZugferdEDokument.php +++ b/app/Services/EDocument/Standards/ZugferdEDokument.php @@ -12,6 +12,7 @@ namespace App\Services\EDocument\Standards; use App\DataMapper\InvoiceItem; +use App\Models\Company; use App\Models\Credit; use App\Models\Invoice; use App\Models\Product; @@ -44,7 +45,6 @@ class ZugferdEDokument extends AbstractService /** @var \App\Models\Company $company */ $company = $this->document->company; - /** @var \App\Models\Client $client */ $client = $this->document->client; @@ -122,14 +122,8 @@ class ZugferdEDokument extends AbstractService 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); } - $custom_value1 = $company->settings->custom_value1; - //BR-DE-23 - If „Payment means type code“ (BT-81) contains a code for credit transfer (30, 58), „CREDIT TRANSFER“ (BG-17) shall be provided. - //Payment Means - Switcher - if (isset($custom_value1) && !empty($custom_value1) && ($custom_value1 == '30' || $custom_value1 == '58')) { - $this->xdocument->addDocumentPaymentMean(typecode: $company->settings->custom_value1, payeeIban: $company->settings->custom_value2, payeeAccountName: $company->settings->custom_value4, payeeBic: $company->settings->custom_value3); - } else { - $this->xdocument->addDocumentPaymentMean('68', ctrans("texts.xinvoice_online_payment")); - } + + $this->injectPaymentMeans($company); if (str_contains($company->getSetting('vat_number'), "/")) { $this->xdocument->addDocumentSellerTaxRegistration("FC", $company->getSetting('vat_number')); @@ -265,7 +259,50 @@ class ZugferdEDokument extends AbstractService return $this; } + + /** + * + * Expanded functionality to allow injecting UBL Payment Means + * into the document + * + * @return self + */ + private function injectPaymentMeans(Company $company): self + { + /**Check if the e_invoice object is populated */ + if(isset($company->e_invoice->Invoice->PaymentMeans) && ($pm = $company->e_invoice->Invoice->PaymentMeans[0] ?? false)){ + + switch ($pm->PaymentMeansCode->value ?? false) { + case '30': + case '58': + $iban = $pm->PayeeFinancialAccount->ID->value; + $name = $pm->PayeeFinancialAccount->Name ?? ''; + $bic = $pm->PayeeFinancialAccount->FinancialInstitutionBranch->FinancialInstitution->ID->value ?? ''; + $typecode = $pm->PaymentMeansCode->value; + + $this->xdocument->addDocumentPaymentMean(typecode: $typecode, payeeIban: $iban, payeeAccountName: $name, payeeBic: $bic); + + return $this; + + default: + # code... + break; + } + + } + + $custom_value1 = $company->settings->custom_value1; + //BR-DE-23 - If „Payment means type code“ (BT-81) contains a code for credit transfer (30, 58), „CREDIT TRANSFER“ (BG-17) shall be provided. + //Payment Means - Switcher + if (isset($custom_value1) && !empty($custom_value1) && ($custom_value1 == '30' || $custom_value1 == '58')) { + $this->xdocument->addDocumentPaymentMean(typecode: $company->settings->custom_value1, payeeIban: $company->settings->custom_value2, payeeAccountName: $company->settings->custom_value4, payeeBic: $company->settings->custom_value3); + } else { + $this->xdocument->addDocumentPaymentMean('68', ctrans("texts.xinvoice_online_payment")); + } + + return $this; + } /** * Returns the XML document * in string format From b42c35d4c326e9d9b1f9654b862acbb53b7defc0 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 23 Nov 2024 09:09:47 +1100 Subject: [PATCH 3/6] Fixes for status --- app/Import/Transformer/Csv/InvoiceTransformer.php | 4 +++- app/Import/Transformer/Csv/QuoteTransformer.php | 7 +++++-- app/Import/Transformer/Csv/RecurringInvoiceTransformer.php | 5 ++++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/app/Import/Transformer/Csv/InvoiceTransformer.php b/app/Import/Transformer/Csv/InvoiceTransformer.php index bd7d254307..97e7955187 100644 --- a/app/Import/Transformer/Csv/InvoiceTransformer.php +++ b/app/Import/Transformer/Csv/InvoiceTransformer.php @@ -14,12 +14,14 @@ namespace App\Import\Transformer\Csv; use App\Import\ImportException; use App\Import\Transformer\BaseTransformer; use App\Models\Invoice; +use App\Utils\Traits\CleanLineItems; /** * Class InvoiceTransformer. */ class InvoiceTransformer extends BaseTransformer { + use CleanLineItems; /** * @param $data * @@ -224,7 +226,7 @@ class InvoiceTransformer extends BaseTransformer ]; } - $transformed['line_items'] = $line_items; + $transformed['line_items'] = $this->cleanItems($line_items); return $transformed; } diff --git a/app/Import/Transformer/Csv/QuoteTransformer.php b/app/Import/Transformer/Csv/QuoteTransformer.php index 00c2cb54d9..f9ae382849 100644 --- a/app/Import/Transformer/Csv/QuoteTransformer.php +++ b/app/Import/Transformer/Csv/QuoteTransformer.php @@ -14,12 +14,15 @@ namespace App\Import\Transformer\Csv; use App\Import\ImportException; use App\Import\Transformer\BaseTransformer; use App\Models\Quote; +use App\Utils\Traits\CleanLineItems; /** * Class QuoteTransformer. */ class QuoteTransformer extends BaseTransformer { + use CleanLineItems; + /** * @param $data * @@ -120,7 +123,7 @@ class QuoteTransformer extends BaseTransformer $this->getString($quote_data, 'quote.status') )) ] ?? Quote::STATUS_SENT, - 'archived' => $status === 'archived', + // 'archived' => $status === 'archived', ]; /* If we can't find the client, then lets try and create a client */ @@ -221,7 +224,7 @@ class QuoteTransformer extends BaseTransformer 'type_id' => '1', //$this->getQuoteTypeId( $record, 'item.type_id' ), ]; } - $transformed['line_items'] = $line_items; + $transformed['line_items'] = $this->cleanItems($line_items); return $transformed; } diff --git a/app/Import/Transformer/Csv/RecurringInvoiceTransformer.php b/app/Import/Transformer/Csv/RecurringInvoiceTransformer.php index 74f1a9b05f..662388e8cf 100644 --- a/app/Import/Transformer/Csv/RecurringInvoiceTransformer.php +++ b/app/Import/Transformer/Csv/RecurringInvoiceTransformer.php @@ -15,12 +15,15 @@ use App\Import\ImportException; use App\Import\Transformer\BaseTransformer; use App\Models\Invoice; use App\Models\RecurringInvoice; +use App\Utils\Traits\CleanLineItems; /** * Class RecurringInvoiceTransformer. */ class RecurringInvoiceTransformer extends BaseTransformer { + use CleanLineItems; + /** * @param $data * @@ -187,7 +190,7 @@ class RecurringInvoiceTransformer extends BaseTransformer ]; } - $transformed['line_items'] = $line_items; + $transformed['line_items'] = $this->cleanItems($line_items); return $transformed; } From 763aa4ae29b53233136f7f6abd4baaa1d0422bb6 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 23 Nov 2024 09:57:50 +1100 Subject: [PATCH 4/6] Add sockets for async downloads --- app/Events/Socket/DownloadAvailable.php | 53 +++++++++++++++++++++++++ app/Jobs/Invoice/ZipInvoices.php | 32 +++++++++------ routes/channels.php | 5 +++ 3 files changed, 77 insertions(+), 13 deletions(-) create mode 100644 app/Events/Socket/DownloadAvailable.php diff --git a/app/Events/Socket/DownloadAvailable.php b/app/Events/Socket/DownloadAvailable.php new file mode 100644 index 0000000000..d2825b8066 --- /dev/null +++ b/app/Events/Socket/DownloadAvailable.php @@ -0,0 +1,53 @@ +user->account->key}-{$this->user->id}"), + ]; + } + + public function broadcastWith(): array + { + + ctrans('texts.document_download_subject'); + + return [ + 'message' => $this->message, + 'url' => $this->url, + ]; + } + +} diff --git a/app/Jobs/Invoice/ZipInvoices.php b/app/Jobs/Invoice/ZipInvoices.php index 1d7da9fc1e..142b853dbb 100644 --- a/app/Jobs/Invoice/ZipInvoices.php +++ b/app/Jobs/Invoice/ZipInvoices.php @@ -11,20 +11,22 @@ namespace App\Jobs\Invoice; -use App\Jobs\Mail\NinjaMailerJob; -use App\Jobs\Mail\NinjaMailerObject; -use App\Jobs\Util\UnlinkFile; -use App\Libraries\MultiDB; -use App\Mail\DownloadInvoices; +use App\Models\User; use App\Models\Company; use App\Models\Invoice; -use App\Models\User; +use App\Libraries\MultiDB; +use App\Jobs\Util\UnlinkFile; use Illuminate\Bus\Queueable; -use Illuminate\Contracts\Queue\ShouldQueue; -use Illuminate\Foundation\Bus\Dispatchable; -use Illuminate\Queue\InteractsWithQueue; +use App\Mail\DownloadInvoices; +use App\Jobs\Mail\NinjaMailerJob; +use Illuminate\Support\Facades\App; +use App\Jobs\Mail\NinjaMailerObject; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\Storage; +use App\Events\Socket\DownloadAvailable; +use Illuminate\Queue\InteractsWithQueue; +use Illuminate\Contracts\Queue\ShouldQueue; +use Illuminate\Foundation\Bus\Dispatchable; class ZipInvoices implements ShouldQueue { @@ -55,11 +57,10 @@ class ZipInvoices implements ShouldQueue public function handle(): void { MultiDB::setDb($this->company->db); - + App::setLocale($this->company->locale()); + $settings = $this->company->settings; - nlog(count($this->invoices)); - $this->invoices = Invoice::withTrashed() ->where('company_id', $this->company->id) ->whereIn('id', $this->invoices) @@ -99,9 +100,10 @@ class ZipInvoices implements ShouldQueue } Storage::put($path.$file_name, $zipFile->outputAsString()); + $storage_url = Storage::url($path.$file_name); $nmo = new NinjaMailerObject(); - $nmo->mailable = new DownloadInvoices(Storage::url($path.$file_name), $this->company); + $nmo->mailable = new DownloadInvoices($storage_url, $this->company); $nmo->to_user = $this->user; $nmo->settings = $settings; $nmo->company = $this->company; @@ -110,6 +112,10 @@ class ZipInvoices implements ShouldQueue UnlinkFile::dispatch(config('filesystems.default'), $path.$file_name)->delay(now()->addHours(1)); + $message = count($this->invoices). " ". ctrans('texts.invoices'). " " . ctrans('texts.download_files'); + + broadcast(new DownloadAvailable($storage_url, $message, $this->company->locale(), $this->user)); + } catch (\PhpZip\Exception\ZipException $e) { nlog('could not make zip => '.$e->getMessage()); } finally { diff --git a/routes/channels.php b/routes/channels.php index bc3fb0d9d5..0a5e594034 100644 --- a/routes/channels.php +++ b/routes/channels.php @@ -14,3 +14,8 @@ Broadcast::channel('company-{company_key}', function (\App\Models\User $user, string $company_key) { return $user->company()->company_key === $company_key; }); + +Broadcast::channel('user-{account_key}-{user_id}', function (\App\Models\User $user, string $account_key, string $user_id) { + return $user->account->key === $account_key && $user->id === (int)$user_id; +}); + From 04d73d3e56fc72e8c6154aa3dc8d49465405be1c Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 23 Nov 2024 14:02:31 +1100 Subject: [PATCH 5/6] Broadcast on download event --- app/Events/Socket/DownloadAvailable.php | 3 +-- app/Jobs/Invoice/ZipInvoices.php | 5 +++-- lang/en/texts.php | 1 + 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/Events/Socket/DownloadAvailable.php b/app/Events/Socket/DownloadAvailable.php index d2825b8066..5e258b8073 100644 --- a/app/Events/Socket/DownloadAvailable.php +++ b/app/Events/Socket/DownloadAvailable.php @@ -29,7 +29,6 @@ class DownloadAvailable implements ShouldBroadcast public function __construct(public string $url, public string $message, public User $user) { - } public function broadcastOn() @@ -43,7 +42,7 @@ class DownloadAvailable implements ShouldBroadcast { ctrans('texts.document_download_subject'); - + return [ 'message' => $this->message, 'url' => $this->url, diff --git a/app/Jobs/Invoice/ZipInvoices.php b/app/Jobs/Invoice/ZipInvoices.php index 142b853dbb..ae26fcc90a 100644 --- a/app/Jobs/Invoice/ZipInvoices.php +++ b/app/Jobs/Invoice/ZipInvoices.php @@ -112,9 +112,10 @@ class ZipInvoices implements ShouldQueue UnlinkFile::dispatch(config('filesystems.default'), $path.$file_name)->delay(now()->addHours(1)); - $message = count($this->invoices). " ". ctrans('texts.invoices'). " " . ctrans('texts.download_files'); + $message = count($this->invoices). " ". ctrans('texts.invoices'); + $message = ctrans('texts.download_ready', ['message' => $message]); - broadcast(new DownloadAvailable($storage_url, $message, $this->company->locale(), $this->user)); + broadcast(new DownloadAvailable($storage_url, $message, $this->user)); } catch (\PhpZip\Exception\ZipException $e) { nlog('could not make zip => '.$e->getMessage()); diff --git a/lang/en/texts.php b/lang/en/texts.php index be96601400..4aaaef9eef 100644 --- a/lang/en/texts.php +++ b/lang/en/texts.php @@ -5474,6 +5474,7 @@ $lang = array( 'delete_identifier' => 'Delete identifier', 'delete_identifier_description' => 'Deleting this identifier will remove it from the system. Make sure this is the desired action before proceeding.', 'einvoice_something_went_wrong' => 'Oops! Something went wrong. Contact us at contact@invoiceninja.com for more information.', + 'download_ready' => 'Your Download is now ready! [ :message ]', ); return $lang; From 32ca5a1badebf7c67f369dcc0ec3d5a3d9abaa29 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 23 Nov 2024 15:36:38 +1100 Subject: [PATCH 6/6] v5.10.55 --- VERSION.txt | 2 +- config/ninja.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/VERSION.txt b/VERSION.txt index 45b100f3b1..19ffb306f7 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -5.10.54 \ No newline at end of file +5.10.55 \ No newline at end of file diff --git a/config/ninja.php b/config/ninja.php index 60c4c42af3..776a084dfb 100644 --- a/config/ninja.php +++ b/config/ninja.php @@ -17,8 +17,8 @@ return [ 'require_https' => env('REQUIRE_HTTPS', true), 'app_url' => rtrim(env('APP_URL', ''), '/'), 'app_domain' => env('APP_DOMAIN', 'invoicing.co'), - 'app_version' => env('APP_VERSION', '5.10.54'), - 'app_tag' => env('APP_TAG', '5.10.54'), + 'app_version' => env('APP_VERSION', '5.10.55'), + 'app_tag' => env('APP_TAG', '5.10.55'), 'minimum_client_version' => '5.0.16', 'terms_version' => '1.0.1', 'api_secret' => env('API_SECRET', false),