From 737e18aa420504b7728289b781cee1951c766840 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 16 Sep 2024 13:12:46 +1000 Subject: [PATCH] Split einvoice gateways --- .../EDocument/Gateway/MutatorInterface.php | 63 ++ .../EDocument/Gateway/MutatorUtil.php | 135 +++ .../EDocument/Gateway/Qvalia/Mutator.php | 163 +++ .../EDocument/Gateway/Qvalia/Qvalia.php | 2 + .../EDocument/Gateway/Storecove/Mutator.php | 622 +++++++++++ .../EDocument/Gateway/Storecove/Storecove.php | 3 + app/Services/EDocument/Standards/Peppol.php | 981 ++---------------- 7 files changed, 1082 insertions(+), 887 deletions(-) create mode 100644 app/Services/EDocument/Gateway/MutatorInterface.php create mode 100644 app/Services/EDocument/Gateway/MutatorUtil.php create mode 100644 app/Services/EDocument/Gateway/Qvalia/Mutator.php create mode 100644 app/Services/EDocument/Gateway/Storecove/Mutator.php diff --git a/app/Services/EDocument/Gateway/MutatorInterface.php b/app/Services/EDocument/Gateway/MutatorInterface.php new file mode 100644 index 000000000000..f308dbaa8f18 --- /dev/null +++ b/app/Services/EDocument/Gateway/MutatorInterface.php @@ -0,0 +1,63 @@ +mutator->p_invoice->PaymentMeans)) { + return $this; + } elseif($paymentMeans = $this->getSetting('Invoice.PaymentMeans')) { + $this->mutator->p_invoice->PaymentMeans = is_array($paymentMeans) ? $paymentMeans : [$paymentMeans]; + return $this; + } + + return $this->checkRequired($required, "Payment Means"); + + } + + + /** + * getClientSetting + * + * @param string $property_path + * @return mixed + */ + public function getClientSetting(string $property_path): mixed + { + return PropertyResolver::resolve($this->mutator->_client_settings, $property_path); + } + + /** + * getCompanySetting + * + * @param string $property_path + * @return mixed + */ + public function getCompanySetting(string $property_path): mixed + { + return PropertyResolver::resolve($this->mutator->_company_settings, $property_path); + } + + /** + * getSetting + * + * Attempts to harvest and return a preconfigured prop from company / client / invoice settings + * + * @param string $property_path + * @return mixed + */ + public function getSetting(string $property_path): mixed + { + + if($prop_value = PropertyResolver::resolve($this->mutator->p_invoice, $property_path)) { + return $prop_value; + } elseif($prop_value = PropertyResolver::resolve($this->mutator->_client_settings, $property_path)) { + return $prop_value; + } elseif($prop_value = PropertyResolver::resolve($this->mutator->_company_settings, $property_path)) { + return $prop_value; + } + return null; + + } + + /** + * Check Required + * + * Throws if a required field is missing. + * + * @param bool $required + * @param string $section + * @return self + */ + public function checkRequired(bool $required, string $section): self + { + return $required ? throw new PeppolValidationException("e-invoice generation halted:: {$section} required", $section, 400) : $this; + } + + /** + * setCustomerAssignedAccountId + * + * Sets the client id_number CAN rely on settings + * + * @param bool $required + * @return self + */ + public function setCustomerAssignedAccountId(bool $required = false): self + { + //@phpstan-ignore-next-line + if(isset($this->mutator->p_invoice->AccountingCustomerParty->CustomerAssignedAccountID)) { + return $this; + } elseif($customer_assigned_account_id = $this->getSetting('Invoice.AccountingCustomerParty.CustomerAssignedAccountID')) { + + $this->mutator->p_invoice->AccountingCustomerParty->CustomerAssignedAccountID = $customer_assigned_account_id; + return $this; + } elseif(strlen($this->mutator->invoice->client->id_number ?? '') > 1) { + + $customer_assigned_account_id = new CustomerAssignedAccountID(); + $customer_assigned_account_id->value = $this->mutator->invoice->client->id_number; + + $this->mutator->p_invoice->AccountingCustomerParty->CustomerAssignedAccountID = $customer_assigned_account_id; + return $this; + } + + //@phpstan-ignore-next-line + return $this->checkRequired($required, 'Client ID Number'); + + } + +} diff --git a/app/Services/EDocument/Gateway/Qvalia/Mutator.php b/app/Services/EDocument/Gateway/Qvalia/Mutator.php new file mode 100644 index 000000000000..96eb44badbd2 --- /dev/null +++ b/app/Services/EDocument/Gateway/Qvalia/Mutator.php @@ -0,0 +1,163 @@ +invoice = $invoice; + return $this; + } + + public function setPeppol($p_invoice): self + { + $this->p_invoice = $p_invoice; + return $this; + } + + public function getPeppol(): mixed + { + return $this->p_invoice; + } + + public function setClientSettings($client_settings): self + { + $this->_client_settings = $client_settings; + return $this; + } + + public function setCompanySettings($company_settings): self + { + $this->_company_settings = $company_settings; + return $this; + } + + /** + * senderSpecificLevelMutators + * + * Runs sender level specific requirements for the e-invoice, + * + * ie, mutations that are required by the senders country. + * + * @return self + */ + public function senderSpecificLevelMutators(): self + { + + if(method_exists($this, $this->invoice->company->country()->iso_3166_2)) { + $this->{$this->invoice->company->country()->iso_3166_2}(); + } + + return $this; + } + + /** + * receiverSpecificLevelMutators + * + * Runs receiver level specific requirements for the e-invoice + * + * ie mutations that are required by the receiving country + * @return self + */ + public function receiverSpecificLevelMutators(): self + { + + if(method_exists($this, "client_{$this->invoice->company->country()->iso_3166_2}")) { + $this->{"client_{$this->invoice->company->country()->iso_3166_2}"}(); + } + + return $this; + } + + // Country-specific methods + public function DE(): self + { + return $this; + } + public function CH(): self + { + return $this; + } + public function AT(): self + { + return $this; + } + public function AU(): self + { + return $this; + } + public function ES(): self + { + return $this; + } + public function FI(): self + { + return $this; + } + public function FR(): self + { + return $this; + } + public function IT(): self + { + return $this; + } + public function client_IT(): self + { + return $this; + } + public function MY(): self + { + return $this; + } + public function NL(): self + { + return $this; + } + public function NZ(): self + { + return $this; + } + public function PL(): self + { + return $this; + } + public function RO(): self + { + return $this; + } + public function SG(): self + { + return $this; + } + public function SE(): self + { + return $this; + } + +} diff --git a/app/Services/EDocument/Gateway/Qvalia/Qvalia.php b/app/Services/EDocument/Gateway/Qvalia/Qvalia.php index 3a41a1ea86ca..5d5c88f86c11 100644 --- a/app/Services/EDocument/Gateway/Qvalia/Qvalia.php +++ b/app/Services/EDocument/Gateway/Qvalia/Qvalia.php @@ -53,6 +53,7 @@ class Qvalia public Invoice $invoice; + public Mutator $mutator; //integrationid - returned in headers public function __construct() @@ -60,6 +61,7 @@ class Qvalia $this->init(); $this->partner = new Partner($this); $this->invoice = new Invoice($this); + $this->mutator = new Mutator($this); } private function init(): self diff --git a/app/Services/EDocument/Gateway/Storecove/Mutator.php b/app/Services/EDocument/Gateway/Storecove/Mutator.php new file mode 100644 index 000000000000..fe451de98371 --- /dev/null +++ b/app/Services/EDocument/Gateway/Storecove/Mutator.php @@ -0,0 +1,622 @@ +mutator_util = new MutatorUtil($this); + } + + /** + * setInvoice + * + * @param mixed $invoice + * @return self + */ + public function setInvoice($invoice): self + { + $this->invoice = $invoice; + return $this; + } + + /** + * setPeppol + * + * @param mixed $p_invoice + * @return self + */ + public function setPeppol($p_invoice): self + { + $this->p_invoice = $p_invoice; + return $this; + } + + /** + * getPeppol + * + * @return mixed + */ + public function getPeppol(): mixed + { + return $this->p_invoice; + } + + /** + * setClientSettings + * + * @param mixed $client_settings + * @return self + */ + public function setClientSettings($client_settings): self + { + $this->_client_settings = $client_settings; + return $this; + } + + /** + * setCompanySettings + * + * @param mixed $company_settings + * @return self + */ + public function setCompanySettings($company_settings): self + { + $this->_company_settings = $company_settings; + return $this; + } + + /** + * senderSpecificLevelMutators + * + * Runs sender level specific requirements for the e-invoice, + * + * ie, mutations that are required by the senders country. + * + * @return self + */ + public function senderSpecificLevelMutators(): self + { + + if(method_exists($this, $this->invoice->company->country()->iso_3166_2)) { + $this->{$this->invoice->company->country()->iso_3166_2}(); + } + + return $this; + } + + /** + * receiverSpecificLevelMutators + * + * Runs receiver level specific requirements for the e-invoice + * + * ie mutations that are required by the receiving country + * @return self + */ + public function receiverSpecificLevelMutators(): self + { + + if(method_exists($this, "client_{$this->invoice->company->country()->iso_3166_2}")) { + $this->{"client_{$this->invoice->company->country()->iso_3166_2}"}(); + } + + return $this; + } + + /** + * DE + * + * @Completed + * @Tested + * + * @return self + */ + public function DE(): self + { + + $this->mutator_util->setPaymentMeans(true); + + return $this; + } + + /** + * CH + * + * @Completed + * + * Completed - QR-Bill to be implemented at a later date. + * @return self + */ + public function CH(): self + { + return $this; + } + + /** + * AT + * + * @Pending + * + * Need to ensure when sending to government entities that we route appropriately + * Also need to ensure customerAssignedAccountIdValue is set so that the sender can be resolved. + * + * Need a way to define if the client is a government entity. + * + * @return self + */ + public function AT(): self + { + //special fields for sending to AT:GOV + + if($this->invoice->client->classification == 'government') { + //routing "b" for production "test" for test environment + $this->setStorecoveMeta($this->buildRouting(["scheme" => 'AT:GOV', "id" => 'b'])); + + //for government clients this must be set. + $this->mutator_util->setCustomerAssignedAccountId(true); + } + + return $this; + } + + public function AU(): self + { + + //if payment means are included, they must be the same `type` + return $this; + } + + /** + * ES + * + * @Pending + * B2G configuration + * B2G Testing + * + * testing. // routing identifier - 293098 + * + * @return self + */ + public function ES(): self + { + + if(!isset($this->invoice->due_date)) { + $this->p_invoice->DueDate = new \DateTime($this->invoice->date); + } + + if($this->invoice->client->classification == 'business' && $this->invoice->company->getSetting('classification') == 'business') { + //must have a paymentmeans as credit_transfer + $this->mutator_util->setPaymentMeans(true); + } + + // For B2G, provide three ES:FACE identifiers in the routing object, + // as well as the ES:VAT tax identifier in the accountingCustomerParty.publicIdentifiers. + // The invoice will then be routed through the FACe network. The three required ES:FACE identifiers are as follows: + // "routing": { + // "eIdentifiers":[ + // { + // "scheme": "ES:FACE", + // "id": "L01234567", + // "role": "ES-01-FISCAL" + // }, + // { + // "scheme": "ES:FACE", + // "id": "L01234567", + // "role": "ES-02-RECEPTOR" + // }, + // { + // "scheme": "ES:FACE", + // "id": "L01234567", + // "role": "ES-03-PAGADOR" + // } + // ] + // } + + return $this; + } + + /** + * FI + * + * @return self + */ + public function FI(): self + { + + // For Finvoice, provide an FI:OPID routing identifier and an FI:OVT legal identifier. + // An FI:VAT is recommended. In many cases (depending on the sender/receiver country and the type of service/goods) + // an FI:VAT is required. So we recommend always including this. + + return $this; + } + + /** + * FR + * @Pending - clarification on codes needed + * + * @return self + */ + public function FR(): self + { + + // When sending invoices to the French government (Chorus Pro): + // All invoices have to be routed to SIRET 0009:11000201100044. There is no test environment for sending to public entities. + // The SIRET / 0009 identifier of the final recipient is to be included in the invoice.accountingCustomerParty.publicIdentifiers array. + + if($this->invoice->client->classification == 'government') { + //route to SIRET 0009:11000201100044 + $this->setStorecoveMeta($this->buildRouting([ + ["scheme" => 'FR:SIRET', "id" => '11000201100044'] + + // ["scheme" => 'FR:SIRET', "id" => '0009:11000201100044'] + ])); + + // The SIRET / 0009 identifier of the final recipient is to be included in the invoice.accountingCustomerParty.publicIdentifiers array. + $this->mutator_util->setCustomerAssignedAccountId(true); + + } + + if(strlen($this->invoice->client->id_number ?? '') == 9) { + //SIREN + $this->setStorecoveMeta($this->buildRouting([ + ["scheme" => 'FR:SIRET', "id" => "{$this->invoice->client->id_number}"] + + // ["scheme" => 'FR:SIRET', "id" => "0002:{$this->invoice->client->id_number}"] + ])); + } else { + //SIRET + $this->setStorecoveMeta($this->buildRouting([ + ["scheme" => 'FR:SIRET', "id" => "{$this->invoice->client->id_number}"] + + // ["scheme" => 'FR:SIRET', "id" => "0009:{$this->invoice->client->id_number}"] + ])); + } + + return $this; + } + + /** + * IT + * + * @return self + */ + public function IT(): self + { + + // IT Sender, IT Receiver, B2B/B2G + // Provide the receiver IT:VAT and the receiver IT:CUUO (codice destinatario) + if(in_array($this->invoice->client->classification, ['business','government']) && $this->invoice->company->country()->iso_3166_2 == 'IT') { + + $this->setStorecoveMeta($this->buildRouting([ + ["scheme" => 'IT:IVA', "id" => $this->invoice->client->vat_number], + ["scheme" => 'IT:CUUO', "id" => $this->invoice->client->routing_id] + ])); + + return $this; + } + + // IT Sender, IT Receiver, B2C + // Provide the receiver IT:CF and the receiver IT:CUUO (codice destinatario) + if($this->invoice->client->classification == 'individual' && $this->invoice->company->country()->iso_3166_2 == 'IT') { + + $this->setStorecoveMeta($this->buildRouting([ + ["scheme" => 'IT:CF', "id" => $this->invoice->client->vat_number], + // ["scheme" => 'IT:CUUO', "id" => $this->invoice->client->routing_id] + ])); + + $this->setEmailRouting($this->invoice->client->present()->email()); + + return $this; + } + + // IT Sender, non-IT Receiver + // Provide the receiver tax identifier and any routing identifier applicable to the receiving country (see Receiver Identifiers). + if($this->invoice->client->country->iso_3166_2 != 'IT' && $this->invoice->company->country()->iso_3166_2 == 'IT') { + + $code = $this->getClientRoutingCode(); + + nlog("foreign receiver"); + $this->setStorecoveMeta($this->buildRouting([ + ["scheme" => $code, "id" => $this->invoice->client->vat_number] + ])); + + return $this; + } + + return $this; + } + + /** + * client_IT + * + * @return self + */ + public function client_IT(): self + { + + // non-IT Sender, IT Receiver, B2C + // Provide the receiver IT:CF and an optional email. The invoice will be eReported and sent via email. Note that this cannot be a PEC email address. + if(in_array($this->invoice->client->classification, ['individual']) && $this->invoice->company->country()->iso_3166_2 != 'IT') { + + return $this; + } + + // non-IT Sender, IT Receiver, B2B/B2G + // Provide the receiver IT:VAT and the receiver IT:CUUO (codice destinatario) + + return $this; + + } + + /** + * MY + * + * @return self + */ + public function MY(): self + { + //way too much to digest here, delayed. + return $this; + } + + /** + * NL + * + * @return self + */ + public function NL(): self + { + + // When sending to public entities, the invoice.accountingSupplierParty.party.contact.email is mandatory. + + // Dutch senders and receivers require a legal identifier. For companies, this is NL:KVK, for public entities this is NL:OINO. + + return $this; + } + + /** + * NZ + * + * @return self + */ + public function NZ(): self + { + // New Zealand uses a GLN to identify businesses. In addition, when sending invoices to a New Zealand customer, make sure you include the pseudo identifier NZ:GST as their tax identifier. + return $this; + } + + /** + * PL + * + * @return self + */ + public function PL(): self + { + + // Because using this network is not yet mandatory, the default workflow is to not use this network. Therefore, you have to force its use, as follows: + + // "routing": { + // "eIdentifiers": [ + // { + // "scheme": "PL:VAT", + // "id": "PL0101010101" + // } + // ], + // "networks": [ + // { + // "application": "pl-ksef", + // "settings": { + // "enabled": true + // } + // } + // ] + // } + // Note this will only work if your LegalEntity has been setup for this network. + + return $this; + } + + /** + * RO + * + * @return self + */ + public function RO(): self + { + // Because using this network is not yet mandatory, the default workflow is to not use this network. Therefore, you have to force its use, as follows: + $meta = ["networks" => [ + [ + "application" => "ro-anaf", + "settings" => [ + "enabled" => true + ], + ], + ]]; + + $this->setStorecoveMeta($meta); + + $this->setStorecoveMeta($this->buildRouting([ + ["scheme" => 'RO:VAT', "id" => $this->invoice->client->vat_number], + ])); + + $ro = new RO($this->invoice); + + $client_state = $this->mutator_util->getClientSetting('Invoice.AccountingSupplierParty.Party.PostalAddress.Address.CountrySubentity'); + $client_city = $this->mutator_util->getClientSetting('Invoice.AccountingCustomerParty.Party.PostalAddress.Address.CityName'); + + $resolved_state = $ro->getStateCode($client_state); + $resolved_city = $ro->getSectorCode($client_city); + + $this->p_invoice->AccountingCustomerParty->Party->PostalAddress->CountrySubentity = $resolved_state; + $this->p_invoice->AccountingCustomerParty->Party->PostalAddress->CityName = $resolved_city; + $this->p_invoice->AccountingCustomerParty->Party->PhysicalLocation->Address->CountrySubentity = $resolved_state; + $this->p_invoice->AccountingCustomerParty->Party->PhysicalLocation->Address->CityName = $resolved_city; + + return $this; + } + + /** + * SG + * + * @return self + */ + public function SG(): self + { + //delayed - stage 2 + return $this; + } + + //Sweden + public function SE(): self + { + // Deliver invoices to the "Svefaktura" co-operation of local Swedish service providers. + // Routing is through the SE:ORGNR together with a network specification: + + // "routing": { + // "eIdentifiers": [ + // { + // "scheme": "SE:ORGNR", + // "id": "0012345678" + // } + // ], + // "networks": [ + // { + // "application": "svefaktura", + // "settings": { + // "enabled": true + // } + // } + // ] + // } + // Use of the "Svefaktura" co-operation can also be induced by specifying an operator id, as follows: + + // "routing": { + // "eIdentifiers": [ + // { + // "scheme": "SE:ORGNR", + // "id": "0012345678" + // }, + // { + // "scheme": "SE:OPID", + // "id": "1234567890" + // } + // ] + // } + return $this; + } + + + /////////////// Storecove Helpers /////////////// + + /** + * getClientRoutingCode + * + * @return string + */ + private function getClientRoutingCode(): string + { + return (new StorecoveRouter())->resolveRouting($this->invoice->client->country->iso_3166_2, $this->invoice->client->classification); + } + + + /** + * Builds the Routing object for StoreCove + * + * @param array $identifiers + * @return array + */ + private function buildRouting(array $identifiers): array + { + return + [ + "routing" => [ + "eIdentifiers" => + $identifiers, + + ] + ]; + } + + + /** + * setEmailRouting + * + * @param string $email + * @return self + */ + private function setEmailRouting(string $email): self + { + + $meta = $this->getStorecoveMeta(); + + if(isset($meta['routing']['emails'])) { + $emails = $meta['routing']['emails']; + array_push($emails, $email); + $meta['routing']['emails'] = $emails; + } else { + $meta['routing']['emails'] = [$email]; + } + + $this->setStorecoveMeta($meta); + + return $this; + } + + + + /** + * setStorecoveMeta + * + * updates the storecove payload for sending documents + * + * @param array $meta + * @return self + */ + private function setStorecoveMeta(array $meta): self + { + + $this->storecove_meta = array_merge($this->storecove_meta, $meta); + + return $this; + } + + /** + * getStorecoveMeta + * + * @return array + */ + public function getStorecoveMeta(): array + { + return $this->storecove_meta; + } + + +} \ No newline at end of file diff --git a/app/Services/EDocument/Gateway/Storecove/Storecove.php b/app/Services/EDocument/Gateway/Storecove/Storecove.php index 59e7e7a533b4..b9d4744055be 100644 --- a/app/Services/EDocument/Gateway/Storecove/Storecove.php +++ b/app/Services/EDocument/Gateway/Storecove/Storecove.php @@ -55,9 +55,12 @@ class Storecove public StorecoveRouter $router; + public Mutator $mutator; + public function __construct() { $this->router = new StorecoveRouter(); + $this->mutator = new Mutator($this); } /** diff --git a/app/Services/EDocument/Standards/Peppol.php b/app/Services/EDocument/Standards/Peppol.php index 3ace8328d314..a547387723ae 100644 --- a/app/Services/EDocument/Standards/Peppol.php +++ b/app/Services/EDocument/Standards/Peppol.php @@ -21,6 +21,9 @@ use InvoiceNinja\EInvoice\EInvoice; use App\Utils\Traits\NumberFormatter; use App\Helpers\Invoice\InvoiceSumInclusive; use App\Exceptions\PeppolValidationException; +use App\Http\Requests\Client\StoreClientRequest; +use App\Services\EDocument\Gateway\Qvalia\Qvalia; +use App\Services\EDocument\Gateway\Storecove\Storecove; use App\Services\EDocument\Standards\Peppol\RO; use InvoiceNinja\EInvoice\Models\Peppol\PaymentMeans; use InvoiceNinja\EInvoice\Models\Peppol\ItemType\Item; @@ -42,6 +45,7 @@ use InvoiceNinja\EInvoice\Models\Peppol\TaxSchemeType\TaxScheme; use InvoiceNinja\EInvoice\Models\Peppol\AmountType\PayableAmount; use InvoiceNinja\EInvoice\Models\Peppol\AmountType\TaxableAmount; use InvoiceNinja\EInvoice\Models\Peppol\TaxTotal as PeppolTaxTotal; +use InvoiceNinja\EInvoice\Models\Peppol\CodeType\IdentificationCode; use InvoiceNinja\EInvoice\Models\Peppol\InvoiceLineType\InvoiceLine; use InvoiceNinja\EInvoice\Models\Peppol\TaxCategoryType\TaxCategory; use InvoiceNinja\EInvoice\Models\Peppol\TaxSubtotalType\TaxSubtotal; @@ -62,6 +66,9 @@ class Peppol extends AbstractService use Taxer; use NumberFormatter; + + //@todo - refactor and move storecove specific logic to the Storecove class + /** * Assumptions: * @@ -294,7 +301,9 @@ class Peppol extends AbstractService private EInvoice $e; - private array $storecove_meta = []; + private string $api_network = Qvalia::class; // Storecove::class; // Qvalia::class; + + public Qvalia | Storecove $gateway; /** * @param Invoice $invoice @@ -305,6 +314,7 @@ class Peppol extends AbstractService $this->calc = $this->invoice->calc(); $this->e = new EInvoice(); $this->setSettings()->setInvoice(); + $this->gateway = new $this->api_network; } /** @@ -326,11 +336,20 @@ class Peppol extends AbstractService $this->p_invoice->AccountingCustomerParty = $this->getAccountingCustomerParty(); $this->p_invoice->InvoiceLine = $this->getInvoiceLines(); - // $this->p_invoice->TaxTotal = $this->getTotalTaxes(); it only wants the aggregate here!! $this->p_invoice->LegalMonetaryTotal = $this->getLegalMonetaryTotal(); + - $this->senderSpecificLevelMutators() - ->receiverSpecificLevelMutators(); + $this->setOrderReference(); + + $this->p_invoice = $this->gateway + ->mutator + ->setInvoice($this->invoice) + ->setPeppol($this->p_invoice) + ->setClientSettings($this->_client_settings) + ->setCompanySettings($this->_company_settings) + ->senderSpecificLevelMutators() + ->receiverSpecificLevelMutators() + ->getPeppol(); if(strlen($this->invoice->backup ?? '') == 0) { @@ -452,6 +471,28 @@ class Peppol extends AbstractService return ['Invoice' => json_decode($this->toJson(), true)]; } + + private function setOrderReference(): self + { + + $this->p_invoice->BuyerReference = $this->invoice->po_number ?? ''; + + if (strlen($this->invoice->po_number ?? '') > 1) { + $order_reference = new OrderReference(); + $id = new ID(); + $id->value = $this->invoice->po_number; + + $order_reference->ID = $id; + + $this->p_invoice->OrderReference = $order_reference; + + + } + + return $this; + + } + /** * getLegalMonetaryTotal * @@ -491,16 +532,16 @@ class Peppol extends AbstractService * * @return float */ - private function getTotalTaxAmount(): float - { - if(!$this->invoice->total_taxes) { - return 0; - } elseif($this->invoice->uses_inclusive_taxes) { - return $this->invoice->total_taxes; - } + // private function getTotalTaxAmount(): float + // { + // if(!$this->invoice->total_taxes) { + // return 0; + // } elseif($this->invoice->uses_inclusive_taxes) { + // return $this->invoice->total_taxes; + // } - return $this->calcAmountLineTax($this->invoice->tax_rate1, $this->invoice->amount) ?? 0; - } + // return $this->calcAmountLineTax($this->invoice->tax_rate1, $this->invoice->amount) ?? 0; + // } private function getTaxType(string $tax_id = ''): string @@ -550,126 +591,7 @@ class Peppol extends AbstractService return $tax_type; } - /** - * getTotalTaxes - * @deprecated - * @return array - */ - // private function getTotalTaxes(): array - // { - // $taxes = []; - - // $type_id = $this->invoice->line_items[0]->type_id; - - // $tax_amount = new TaxAmount(); - // $tax_amount->currencyID = $this->invoice->client->currency()->code; - // $tax_amount->amount = $this->getTotalTaxAmount(); - - // $tax_subtotal = new TaxSubtotal(); - // $tax_subtotal->TaxAmount = $tax_amount; - - // $taxable_amount = new TaxableAmount(); - // $taxable_amount->currencyID = $this->invoice->client->currency()->code; - // $taxable_amount->amount = $this->invoice->uses_inclusive_taxes ? $this->invoice->amount - $this->invoice->total_taxes : $this->invoice->amount; - // $tax_subtotal->TaxableAmount = $taxable_amount; - - // $tc = new TaxCategory(); - // $id = new ID(); - // $id->value = $type_id == '2' ? 'HUR' : 'C62'; - // $tc->ID = $id; - // $tc->Percent = $this->invoice->tax_rate1; - // $ts = new TaxScheme(); - // $id = new ID(); - // $id->value = strlen($this->invoice->tax_name1 ?? '') > 1 ? $this->invoice->tax_name1 : '0'; - // $ts->ID = $id; - // $tc->TaxScheme = $ts; - // $tax_subtotal->TaxCategory = $tc; - - // $tax_total = new TaxTotal(); - // $tax_total->TaxAmount = $tax_amount; - // $tax_total->TaxSubtotal[] = $tax_subtotal; - - // $taxes[] = $tax_total; - - - - // if(strlen($this->invoice->tax_name2 ?? '') > 1) { - - // $tax_amount = new TaxAmount(); - // $tax_amount->currencyID = $this->invoice->client->currency()->code; - - // $tax_amount->amount = $this->invoice->uses_inclusive_taxes ? $this->calcInclusiveLineTax($this->invoice->tax_rate2, $this->invoice->amount) : $this->calcAmountLineTax($this->invoice->tax_rate2, $this->invoice->amount); - - // $tax_subtotal = new TaxSubtotal(); - // $tax_subtotal->TaxAmount = $tax_amount; - - // $taxable_amount = new TaxableAmount(); - // $taxable_amount->currencyID = $this->invoice->client->currency()->code; - // $taxable_amount->amount = $this->invoice->uses_inclusive_taxes ? $this->invoice->amount - $this->invoice->total_taxes : $this->invoice->amount; - // $tax_subtotal->TaxableAmount = $taxable_amount; - - - // $tc = new TaxCategory(); - // $id = new ID(); - // $id->value = $type_id == '2' ? 'HUR' : 'C62'; - // $tc->ID = $id; - // $tc->Percent = $this->invoice->tax_rate2; - // $ts = new TaxScheme(); - // $id = new ID(); - // $id->value = $this->invoice->tax_name2; - // $ts->ID = $id; - // $tc->TaxScheme = $ts; - // $tax_subtotal->TaxCategory = $tc; - - - // $tax_total = new TaxTotal(); - // $tax_total->TaxAmount = $tax_amount; - // $tax_total->TaxSubtotal[] = $tax_subtotal; - - // $taxes[] = $tax_total; - - // } - - // if(strlen($this->invoice->tax_name3 ?? '') > 1) { - - // $tax_amount = new TaxAmount(); - // $tax_amount->currencyID = $this->invoice->client->currency()->code; - // $tax_amount->amount = $this->invoice->uses_inclusive_taxes ? $this->calcInclusiveLineTax($this->invoice->tax_rate3, $this->invoice->amount) : $this->calcAmountLineTax($this->invoice->tax_rate3, $this->invoice->amount); - - // $tax_subtotal = new TaxSubtotal(); - // $tax_subtotal->TaxAmount = $tax_amount; - - // $taxable_amount = new TaxableAmount(); - // $taxable_amount->currencyID = $this->invoice->client->currency()->code; - // $taxable_amount->amount = $this->invoice->uses_inclusive_taxes ? $this->invoice->amount - $this->invoice->total_taxes : $this->invoice->amount; - // $tax_subtotal->TaxableAmount = $taxable_amount; - - // $tc = new TaxCategory(); - - // $id = new ID(); - // $id->value = $type_id == '2' ? 'HUR' : 'C62'; - // $tc->ID = $id; - // $tc->Percent = $this->invoice->tax_rate3; - // $ts = new TaxScheme(); - - // $id = new ID(); - // $id->value = $this->invoice->tax_name3; - - // $ts->ID = $id; - // $tc->TaxScheme = $ts; - // $tax_subtotal->TaxCategory = $tc; - - // $tax_total = new TaxTotal(); - // $tax_total->TaxAmount = $tax_amount; - // $tax_total->TaxSubtotal[] = $tax_subtotal; - - // $taxes[] = $tax_total; - - // } - - // return $taxes; - // } - + private function getInvoiceLines(): array { $lines = []; @@ -769,42 +691,42 @@ class Peppol extends AbstractService * * @return array */ - private function zeroTaxAmount(): array - { - $blank_tax = []; + // private function zeroTaxAmount(): array + // { + // $blank_tax = []; - $tax_amount = new TaxAmount(); - $tax_amount->currencyID = $this->invoice->client->currency()->code; - $tax_amount->amount = '0'; - $tax_subtotal = new TaxSubtotal(); - $tax_subtotal->TaxAmount = $tax_amount; + // $tax_amount = new TaxAmount(); + // $tax_amount->currencyID = $this->invoice->client->currency()->code; + // $tax_amount->amount = '0'; + // $tax_subtotal = new TaxSubtotal(); + // $tax_subtotal->TaxAmount = $tax_amount; - $taxable_amount = new TaxableAmount(); - $taxable_amount->currencyID = $this->invoice->client->currency()->code; - $taxable_amount->amount = '0'; - $tax_subtotal->TaxableAmount = $taxable_amount; + // $taxable_amount = new TaxableAmount(); + // $taxable_amount->currencyID = $this->invoice->client->currency()->code; + // $taxable_amount->amount = '0'; + // $tax_subtotal->TaxableAmount = $taxable_amount; - $tc = new TaxCategory(); - $id = new ID(); - $id->value = 'Z'; - $tc->ID = $id; - $tc->Percent = '0'; - $ts = new TaxScheme(); + // $tc = new TaxCategory(); + // $id = new ID(); + // $id->value = 'Z'; + // $tc->ID = $id; + // $tc->Percent = '0'; + // $ts = new TaxScheme(); - $id = new ID(); - $id->value = '0'; - $ts->ID = $id; - $tc->TaxScheme = $ts; - $tax_subtotal->TaxCategory = $tc; + // $id = new ID(); + // $id->value = '0'; + // $ts->ID = $id; + // $tc->TaxScheme = $ts; + // $tax_subtotal->TaxCategory = $tc; - $tax_total = new TaxTotal(); - $tax_total->TaxAmount = $tax_amount; - $tax_total->TaxSubtotal[] = $tax_subtotal; - $blank_tax[] = $tax_total; + // $tax_total = new TaxTotal(); + // $tax_total->TaxAmount = $tax_amount; + // $tax_total->TaxSubtotal[] = $tax_subtotal; + // $blank_tax[] = $tax_total; - return $blank_tax; - } + // return $blank_tax; + // } /** * getItemTaxes @@ -962,7 +884,11 @@ class Peppol extends AbstractService // $address->CountrySubentityCode = $this->invoice->company->settings->state; $country = new Country(); - $country->IdentificationCode = substr($this->invoice->company->country()->iso_3166_2, 0, 2); + + $ic = new IdentificationCode(); + $ic->value = substr($this->invoice->company->country()->iso_3166_2, 0, 2); + $country->IdentificationCode = $ic; + $address->Country = $country; $party->PostalAddress = $address; @@ -988,30 +914,6 @@ class Peppol extends AbstractService private function resolveTaxScheme(): string { return (new StorecoveRouter())->resolveTaxScheme($this->invoice->client->country->iso_3166_2, $this->invoice->client->classification); - - // $rules = isset($this->routing_rules[$this->invoice->client->country->iso_3166_2]) ? $this->routing_rules[$this->invoice->client->country->iso_3166_2] : [false, false, false, false,]; - - // $code = false; - - // match($this->invoice->client->classification) { - // "business" => $code = "B", - // "government" => $code = "G", - // "individual" => $code = "C", - // default => $code = false, - // }; - - // //single array - // if(is_array($rules) && !is_array($rules[0])) { - // return $rules[2]; - // } - - // foreach($rules as $rule) { - // if(stripos($rule[0], $code) !== false) { - // return $rule[2]; - // } - // } - - // return false; } /** @@ -1056,7 +958,11 @@ class Peppol extends AbstractService // $address->CountrySubentityCode = $this->invoice->client->state; $country = new Country(); - $country->IdentificationCode = substr($this->invoice->client->country->iso_3166_2, 0, 2); + + $ic = new IdentificationCode(); + $ic->value = substr($this->invoice->client->country->iso_3166_2, 0, 2); + + $country->IdentificationCode = $ic; $address->Country = $country; $party->PostalAddress = $address; @@ -1130,33 +1036,6 @@ class Peppol extends AbstractService ///////////////// Helper Methods ///////////////////////// - /** - * getClientRoutingCode - * - * @return string - */ - private function getClientRoutingCode(): string - { - // $receiver_identifiers = $this->routing_rules[$this->invoice->client->country->iso_3166_2]; - // $client_classification = $this->invoice->client->classification == 'government' ? 'G' : 'B'; - - // if(count($receiver_identifiers) > 1) { - - // foreach($receiver_identifiers as $ident) { - // if(str_contains($ident[0], $client_classification)) { - // return $ident[3]; - // } - // } - - // } elseif(count($receiver_identifiers) == 1) { - // return $receiver_identifiers[3]; - // } - - return (new StorecoveRouter())->resolveRouting($this->invoice->client->country->iso_3166_2, $this->invoice->client->classification); - - // throw new \Exception("e-invoice generation halted:: Could not resolve the Tax Code for this client? {$this->invoice->client->hashed_id}"); - - } /** * setInvoiceDefaults @@ -1212,676 +1091,4 @@ class Peppol extends AbstractService return $this; } - /** - * getSetting - * - * Attempts to harvest and return a preconfigured prop from company / client / invoice settings - * - * @param string $property_path - * @return mixed - */ - public function getSetting(string $property_path): mixed - { - - if($prop_value = PropertyResolver::resolve($this->p_invoice, $property_path)) { - return $prop_value; - } elseif($prop_value = PropertyResolver::resolve($this->_client_settings, $property_path)) { - return $prop_value; - } elseif($prop_value = PropertyResolver::resolve($this->_company_settings, $property_path)) { - return $prop_value; - } - return null; - - } - - /** - * getClientSetting - * - * @param string $property_path - * @return mixed - */ - private function getClientSetting(string $property_path): mixed - { - return PropertyResolver::resolve($this->_client_settings, $property_path); - } - - /** - * getCompanySetting - * - * @param string $property_path - * @return mixed - */ - private function getCompanySetting(string $property_path): mixed - { - return PropertyResolver::resolve($this->_company_settings, $property_path); - } - - /** - * senderSpecificLevelMutators - * - * Runs sender level specific requirements for the e-invoice, - * - * ie, mutations that are required by the senders country. - * - * @return self - */ - private function senderSpecificLevelMutators(): self - { - - if(method_exists($this, $this->invoice->company->country()->iso_3166_2)) { - $this->{$this->invoice->company->country()->iso_3166_2}(); - } - - return $this; - } - - /** - * receiverSpecificLevelMutators - * - * Runs receiver level specific requirements for the e-invoice - * - * ie mutations that are required by the receiving country - * @return self - */ - private function receiverSpecificLevelMutators(): self - { - - if(method_exists($this, "client_{$this->invoice->company->country()->iso_3166_2}")) { - $this->{"client_{$this->invoice->company->country()->iso_3166_2}"}(); - } - - return $this; - } - - - /** - * setPaymentMeans - * - * Sets the payment means - if it exists - * @param bool $required - * @return self - */ - private function setPaymentMeans(bool $required = false): self - { - - if(isset($this->p_invoice->PaymentMeans)) { - return $this; - } elseif($paymentMeans = $this->getSetting('Invoice.PaymentMeans')) { - $this->p_invoice->PaymentMeans = is_array($paymentMeans) ? $paymentMeans : [$paymentMeans]; - return $this; - } - - return $this->checkRequired($required, "Payment Means"); - - } - - /** - * setOrderReference - * - * sets the order reference - if it exists (Never rely on settings for this) - * - * @param bool $required - * @return self - */ - private function setOrderReference(bool $required = false): self - { - $this->p_invoice->BuyerReference = $this->invoice->po_number ?? ''; - - if(strlen($this->invoice->po_number ?? '') > 1) { - $order_reference = new OrderReference(); - $id = new ID(); - $id->value = $this->invoice->po_number; - - $order_reference->ID = $id; - - $this->p_invoice->OrderReference = $order_reference; - - // $this->setStorecoveMeta(["document" => [ - // "invoice" => [ - // [ - // "references" => [ - // "documentType" => "purchase_order", - // "documentId" => $this->invoice->po_number, - // ], - // ], - // ], - // ] - // ]); - - return $this; - } - - return $this->checkRequired($required, 'Order Reference'); - - } - - /** - * setCustomerAssignedAccountId - * - * Sets the client id_number CAN rely on settings - * - * @param bool $required - * @return self - */ - private function setCustomerAssignedAccountId(bool $required = false): self - { - //@phpstan-ignore-next-line - if(isset($this->p_invoice->AccountingCustomerParty->CustomerAssignedAccountID)) { - return $this; - } elseif($customer_assigned_account_id = $this->getSetting('Invoice.AccountingCustomerParty.CustomerAssignedAccountID')) { - - $this->p_invoice->AccountingCustomerParty->CustomerAssignedAccountID = $customer_assigned_account_id; - return $this; - } elseif(strlen($this->invoice->client->id_number ?? '') > 1) { - - $customer_assigned_account_id = new CustomerAssignedAccountID(); - $customer_assigned_account_id->value = $this->invoice->client->id_number; - - $this->p_invoice->AccountingCustomerParty->CustomerAssignedAccountID = $customer_assigned_account_id; - return $this; - } - - //@phpstan-ignore-next-line - return $this->checkRequired($required, 'Client ID Number'); - - } - - /** - * Check Required - * - * Throws if a required field is missing. - * - * @param bool $required - * @param string $section - * @return self - */ - private function checkRequired(bool $required, string $section): self - { - return $required ? throw new PeppolValidationException("e-invoice generation halted:: {$section} required", $section, 400) : $this; - } - - - /** - * Builds the Routing object for StoreCove - * - * @param array $identifiers - * @return array - */ - private function buildRouting(array $identifiers): array - { - return - [ - "routing" => [ - "eIdentifiers" => - $identifiers, - - ] - ]; - } - - /** - * setEmailRouting - * - * @param string $email - * @return self - */ - private function setEmailRouting(string $email): self - { - nlog($email); - - $meta = $this->getStorecoveMeta(); - - if(isset($meta['routing']['emails'])) { - $emails = $meta['routing']['emails']; - array_push($emails, $email); - $meta['routing']['emails'] = $emails; - } else { - $meta['routing']['emails'] = [$email]; - } - - $this->setStorecoveMeta($meta); - - return $this; - } - - /** - * setStorecoveMeta - * - * updates the storecove payload for sending documents - * - * @param array $meta - * @return self - */ - private function setStorecoveMeta(array $meta): self - { - - $this->storecove_meta = array_merge($this->storecove_meta, $meta); - - return $this; - } - - /** - * getStorecoveMeta - * - * @return array - */ - public function getStorecoveMeta(): array - { - return $this->storecove_meta; - } - - - - - ////////////////////////// Country level mutators ///////////////////////////////////// - - /** - * DE - * - * @Completed - * @Tested - * - * @return self - */ - private function DE(): self - { - - $this->setPaymentMeans(true); - - return $this; - } - - /** - * CH - * - * @Completed - * - * Completed - QR-Bill to be implemented at a later date. - * @return self - */ - private function CH(): self - { - return $this; - } - - /** - * AT - * - * @Pending - * - * Need to ensure when sending to government entities that we route appropriately - * Also need to ensure customerAssignedAccountIdValue is set so that the sender can be resolved. - * - * Need a way to define if the client is a government entity. - * - * @return self - */ - private function AT(): self - { - //special fields for sending to AT:GOV - - if($this->invoice->client->classification == 'government') { - //routing "b" for production "test" for test environment - $this->setStorecoveMeta($this->buildRouting(["scheme" => 'AT:GOV', "id" => 'b'])); - - //for government clients this must be set. - $this->setCustomerAssignedAccountId(true); - } - - return $this; - } - - private function AU(): self - { - - //if payment means are included, they must be the same `type` - return $this; - } - - /** - * ES - * - * @Pending - * B2G configuration - * B2G Testing - * - * testing. // routing identifier - 293098 - * - * @return self - */ - private function ES(): self - { - - if(!isset($this->invoice->due_date)) { - $this->p_invoice->DueDate = new \DateTime($this->invoice->date); - } - - if($this->invoice->client->classification == 'business' && $this->invoice->company->getSetting('classification') == 'business') { - //must have a paymentmeans as credit_transfer - $this->setPaymentMeans(true); - } - - // For B2G, provide three ES:FACE identifiers in the routing object, - // as well as the ES:VAT tax identifier in the accountingCustomerParty.publicIdentifiers. - // The invoice will then be routed through the FACe network. The three required ES:FACE identifiers are as follows: - // "routing": { - // "eIdentifiers":[ - // { - // "scheme": "ES:FACE", - // "id": "L01234567", - // "role": "ES-01-FISCAL" - // }, - // { - // "scheme": "ES:FACE", - // "id": "L01234567", - // "role": "ES-02-RECEPTOR" - // }, - // { - // "scheme": "ES:FACE", - // "id": "L01234567", - // "role": "ES-03-PAGADOR" - // } - // ] - // } - - return $this; - } - - /** - * FI - * - * @return self - */ - private function FI(): self - { - - // For Finvoice, provide an FI:OPID routing identifier and an FI:OVT legal identifier. - // An FI:VAT is recommended. In many cases (depending on the sender/receiver country and the type of service/goods) - // an FI:VAT is required. So we recommend always including this. - - return $this; - } - - /** - * FR - * @Pending - clarification on codes needed - * - * @return self - */ - private function FR(): self - { - - // When sending invoices to the French government (Chorus Pro): - // All invoices have to be routed to SIRET 0009:11000201100044. There is no test environment for sending to public entities. - // The SIRET / 0009 identifier of the final recipient is to be included in the invoice.accountingCustomerParty.publicIdentifiers array. - - if($this->invoice->client->classification == 'government') { - //route to SIRET 0009:11000201100044 - $this->setStorecoveMeta($this->buildRouting([ - ["scheme" => 'FR:SIRET', "id" => '11000201100044'] - - // ["scheme" => 'FR:SIRET', "id" => '0009:11000201100044'] - ])); - - // The SIRET / 0009 identifier of the final recipient is to be included in the invoice.accountingCustomerParty.publicIdentifiers array. - $this->setCustomerAssignedAccountId(true); - - } - - if(strlen($this->invoice->client->id_number ?? '') == 9) { - //SIREN - $this->setStorecoveMeta($this->buildRouting([ - ["scheme" => 'FR:SIRET', "id" => "{$this->invoice->client->id_number}"] - - // ["scheme" => 'FR:SIRET', "id" => "0002:{$this->invoice->client->id_number}"] - ])); - } else { - //SIRET - $this->setStorecoveMeta($this->buildRouting([ - ["scheme" => 'FR:SIRET', "id" => "{$this->invoice->client->id_number}"] - - // ["scheme" => 'FR:SIRET', "id" => "0009:{$this->invoice->client->id_number}"] - ])); - } - - // Apparently this is not a special field according to support - // sounds like it is optional - // The service code must be sent in invoice.buyerReference (deprecated) or the invoice.references array (documentType buyer_reference) - - if(strlen($this->invoice->po_number ?? '') > 1) { - $this->setOrderReference(false); - } - - return $this; - } - - /** - * IT - * - * @return self - */ - private function IT(): self - { - - // IT Sender, IT Receiver, B2B/B2G - // Provide the receiver IT:VAT and the receiver IT:CUUO (codice destinatario) - if(in_array($this->invoice->client->classification, ['business','government']) && $this->invoice->company->country()->iso_3166_2 == 'IT') { - - $this->setStorecoveMeta($this->buildRouting([ - ["scheme" => 'IT:IVA', "id" => $this->invoice->client->vat_number], - ["scheme" => 'IT:CUUO', "id" => $this->invoice->client->routing_id] - ])); - - return $this; - } - - // IT Sender, IT Receiver, B2C - // Provide the receiver IT:CF and the receiver IT:CUUO (codice destinatario) - if($this->invoice->client->classification == 'individual' && $this->invoice->company->country()->iso_3166_2 == 'IT') { - - $this->setStorecoveMeta($this->buildRouting([ - ["scheme" => 'IT:CF', "id" => $this->invoice->client->vat_number], - // ["scheme" => 'IT:CUUO', "id" => $this->invoice->client->routing_id] - ])); - - $this->setEmailRouting($this->invoice->client->present()->email()); - - return $this; - } - - // IT Sender, non-IT Receiver - // Provide the receiver tax identifier and any routing identifier applicable to the receiving country (see Receiver Identifiers). - if($this->invoice->client->country->iso_3166_2 != 'IT' && $this->invoice->company->country()->iso_3166_2 == 'IT') { - - $code = $this->getClientRoutingCode(); - - nlog("foreign receiver"); - $this->setStorecoveMeta($this->buildRouting([ - ["scheme" => $code, "id" => $this->invoice->client->vat_number] - ])); - - return $this; - } - - return $this; - } - - /** - * client_IT - * - * @return self - */ - private function client_IT(): self - { - - // non-IT Sender, IT Receiver, B2C - // Provide the receiver IT:CF and an optional email. The invoice will be eReported and sent via email. Note that this cannot be a PEC email address. - if(in_array($this->invoice->client->classification, ['individual']) && $this->invoice->company->country()->iso_3166_2 != 'IT') { - - return $this; - } - - // non-IT Sender, IT Receiver, B2B/B2G - // Provide the receiver IT:VAT and the receiver IT:CUUO (codice destinatario) - - return $this; - - } - - /** - * MY - * - * @return self - */ - private function MY(): self - { - //way too much to digest here, delayed. - return $this; - } - - /** - * NL - * - * @return self - */ - private function NL(): self - { - - // When sending to public entities, the invoice.accountingSupplierParty.party.contact.email is mandatory. - - // Dutch senders and receivers require a legal identifier. For companies, this is NL:KVK, for public entities this is NL:OINO. - - return $this; - } - - /** - * NZ - * - * @return self - */ - private function NZ(): self - { - // New Zealand uses a GLN to identify businesses. In addition, when sending invoices to a New Zealand customer, make sure you include the pseudo identifier NZ:GST as their tax identifier. - return $this; - } - - /** - * PL - * - * @return self - */ - private function PL(): self - { - - // Because using this network is not yet mandatory, the default workflow is to not use this network. Therefore, you have to force its use, as follows: - - // "routing": { - // "eIdentifiers": [ - // { - // "scheme": "PL:VAT", - // "id": "PL0101010101" - // } - // ], - // "networks": [ - // { - // "application": "pl-ksef", - // "settings": { - // "enabled": true - // } - // } - // ] - // } - // Note this will only work if your LegalEntity has been setup for this network. - - return $this; - } - - /** - * RO - * - * @return self - */ - private function RO(): self - { - // Because using this network is not yet mandatory, the default workflow is to not use this network. Therefore, you have to force its use, as follows: - $meta = ["networks" => [ - [ - "application" => "ro-anaf", - "settings" => [ - "enabled" => true - ], - ], - ]]; - - $this->setStorecoveMeta($meta); - - $this->setStorecoveMeta($this->buildRouting([ - ["scheme" => 'RO:VAT', "id" => $this->invoice->client->vat_number], - ])); - - $ro = new RO($this->invoice); - - $client_state = $this->getClientSetting('Invoice.AccountingSupplierParty.Party.PostalAddress.Address.CountrySubentity'); - $client_city = $this->getClientSetting('Invoice.AccountingCustomerParty.Party.PostalAddress.Address.CityName'); - - $resolved_state = $ro->getStateCode($client_state); - $resolved_city = $ro->getSectorCode($client_city); - - $this->p_invoice->AccountingCustomerParty->Party->PostalAddress->CountrySubentity = $resolved_state; - $this->p_invoice->AccountingCustomerParty->Party->PostalAddress->CityName = $resolved_city; - $this->p_invoice->AccountingCustomerParty->Party->PhysicalLocation->Address->CountrySubentity = $resolved_state; - $this->p_invoice->AccountingCustomerParty->Party->PhysicalLocation->Address->CityName = $resolved_city; - - return $this; - } - - /** - * SG - * - * @return self - */ - private function SG(): self - { - //delayed - stage 2 - return $this; - } - - //Sweden - private function SE(): self - { - // Deliver invoices to the "Svefaktura" co-operation of local Swedish service providers. - // Routing is through the SE:ORGNR together with a network specification: - - // "routing": { - // "eIdentifiers": [ - // { - // "scheme": "SE:ORGNR", - // "id": "0012345678" - // } - // ], - // "networks": [ - // { - // "application": "svefaktura", - // "settings": { - // "enabled": true - // } - // } - // ] - // } - // Use of the "Svefaktura" co-operation can also be induced by specifying an operator id, as follows: - - // "routing": { - // "eIdentifiers": [ - // { - // "scheme": "SE:ORGNR", - // "id": "0012345678" - // }, - // { - // "scheme": "SE:OPID", - // "id": "1234567890" - // } - // ] - // } - return $this; - } }