mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
Split einvoice gateways
This commit is contained in:
parent
50faf3261b
commit
737e18aa42
63
app/Services/EDocument/Gateway/MutatorInterface.php
Normal file
63
app/Services/EDocument/Gateway/MutatorInterface.php
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Services\EDocument\Gateway;
|
||||||
|
|
||||||
|
interface MutatorInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
public function receiverSpecificLevelMutators(): self;
|
||||||
|
|
||||||
|
public function senderSpecificLevelMutators(): self;
|
||||||
|
|
||||||
|
public function setInvoice($invoice): self;
|
||||||
|
|
||||||
|
public function setPeppol($p_invoice): self;
|
||||||
|
|
||||||
|
public function getPeppol(): mixed;
|
||||||
|
|
||||||
|
public function setClientSettings($client_settings): self;
|
||||||
|
|
||||||
|
public function setCompanySettings($company_settings): self;
|
||||||
|
|
||||||
|
// Country-specific methods
|
||||||
|
public function DE(): self;
|
||||||
|
|
||||||
|
public function CH(): self;
|
||||||
|
|
||||||
|
public function AT(): self;
|
||||||
|
|
||||||
|
public function AU(): self;
|
||||||
|
|
||||||
|
public function ES(): self;
|
||||||
|
|
||||||
|
public function FI(): self;
|
||||||
|
|
||||||
|
public function FR(): self;
|
||||||
|
|
||||||
|
public function IT(): self;
|
||||||
|
|
||||||
|
public function client_IT(): self;
|
||||||
|
|
||||||
|
public function MY(): self;
|
||||||
|
|
||||||
|
public function NL(): self;
|
||||||
|
|
||||||
|
public function NZ(): self;
|
||||||
|
|
||||||
|
public function PL(): self;
|
||||||
|
|
||||||
|
public function RO(): self;
|
||||||
|
|
||||||
|
public function SG(): self;
|
||||||
|
|
||||||
|
public function SE(): self;
|
||||||
|
}
|
135
app/Services/EDocument/Gateway/MutatorUtil.php
Normal file
135
app/Services/EDocument/Gateway/MutatorUtil.php
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Services\EDocument\Gateway;
|
||||||
|
|
||||||
|
use App\Exceptions\PeppolValidationException;
|
||||||
|
use App\Services\EDocument\Standards\Settings\PropertyResolver;
|
||||||
|
|
||||||
|
class MutatorUtil
|
||||||
|
{
|
||||||
|
|
||||||
|
public function __construct(public MutatorInterface $mutator)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* setPaymentMeans
|
||||||
|
*
|
||||||
|
* Sets the payment means - if it exists
|
||||||
|
* @param bool $required
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function setPaymentMeans(bool $required = false): self
|
||||||
|
{
|
||||||
|
|
||||||
|
if(isset($this->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');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
163
app/Services/EDocument/Gateway/Qvalia/Mutator.php
Normal file
163
app/Services/EDocument/Gateway/Qvalia/Mutator.php
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Services\EDocument\Gateway\Qvalia;
|
||||||
|
|
||||||
|
use App\Services\EDocument\Gateway\MutatorInterface;
|
||||||
|
|
||||||
|
class Mutator implements MutatorInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
private \InvoiceNinja\EInvoice\Models\Peppol\Invoice $p_invoice;
|
||||||
|
|
||||||
|
private ?\InvoiceNinja\EInvoice\Models\Peppol\Invoice $_client_settings;
|
||||||
|
|
||||||
|
private ?\InvoiceNinja\EInvoice\Models\Peppol\Invoice $_company_settings;
|
||||||
|
|
||||||
|
private $invoice;
|
||||||
|
|
||||||
|
public function __construct(public Qvalia $qvalia)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setInvoice($invoice): self
|
||||||
|
{
|
||||||
|
$this->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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -53,6 +53,7 @@ class Qvalia
|
|||||||
|
|
||||||
public Invoice $invoice;
|
public Invoice $invoice;
|
||||||
|
|
||||||
|
public Mutator $mutator;
|
||||||
//integrationid - returned in headers
|
//integrationid - returned in headers
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
@ -60,6 +61,7 @@ class Qvalia
|
|||||||
$this->init();
|
$this->init();
|
||||||
$this->partner = new Partner($this);
|
$this->partner = new Partner($this);
|
||||||
$this->invoice = new Invoice($this);
|
$this->invoice = new Invoice($this);
|
||||||
|
$this->mutator = new Mutator($this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function init(): self
|
private function init(): self
|
||||||
|
622
app/Services/EDocument/Gateway/Storecove/Mutator.php
Normal file
622
app/Services/EDocument/Gateway/Storecove/Mutator.php
Normal file
@ -0,0 +1,622 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Services\EDocument\Gateway\Storecove;
|
||||||
|
|
||||||
|
use App\Services\EDocument\Gateway\MutatorUtil;
|
||||||
|
use App\Services\EDocument\Standards\Peppol\RO;
|
||||||
|
use App\Services\EDocument\Gateway\MutatorInterface;
|
||||||
|
use App\Services\EDocument\Gateway\Storecove\StorecoveRouter;
|
||||||
|
|
||||||
|
class Mutator implements MutatorInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
public \InvoiceNinja\EInvoice\Models\Peppol\Invoice $p_invoice;
|
||||||
|
|
||||||
|
public ?\InvoiceNinja\EInvoice\Models\Peppol\Invoice $_client_settings;
|
||||||
|
|
||||||
|
public ?\InvoiceNinja\EInvoice\Models\Peppol\Invoice $_company_settings;
|
||||||
|
|
||||||
|
public $invoice;
|
||||||
|
|
||||||
|
private array $storecove_meta = [];
|
||||||
|
|
||||||
|
private MutatorUtil $mutator_util;
|
||||||
|
// Constructor
|
||||||
|
public function __construct(public Storecove $storecove)
|
||||||
|
{
|
||||||
|
$this->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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -55,9 +55,12 @@ class Storecove
|
|||||||
|
|
||||||
public StorecoveRouter $router;
|
public StorecoveRouter $router;
|
||||||
|
|
||||||
|
public Mutator $mutator;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->router = new StorecoveRouter();
|
$this->router = new StorecoveRouter();
|
||||||
|
$this->mutator = new Mutator($this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user