Tests
This commit is contained in:
parent
5790e71e73
commit
a74dba5a5d
|
|
@ -362,7 +362,7 @@ class Company extends BaseModel
|
||||||
'smtp_local_domain',
|
'smtp_local_domain',
|
||||||
'smtp_verify_peer',
|
'smtp_verify_peer',
|
||||||
// 'e_invoice',
|
// 'e_invoice',
|
||||||
'e_invoicing_token',
|
// 'e_invoicing_token',
|
||||||
];
|
];
|
||||||
|
|
||||||
protected $hidden = [
|
protected $hidden = [
|
||||||
|
|
|
||||||
|
|
@ -13,17 +13,8 @@ namespace App\Services\EDocument\Gateway\Storecove\Models;
|
||||||
|
|
||||||
class AllowanceCharges
|
class AllowanceCharges
|
||||||
{
|
{
|
||||||
public string $reason;
|
|
||||||
public float $amountExcludingTax;
|
|
||||||
public Tax $tax;
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
string $reason,
|
public string $reason,
|
||||||
float $amountExcludingTax,
|
public float $amountExcludingTax
|
||||||
Tax $tax
|
) {}
|
||||||
) {
|
|
||||||
$this->reason = $reason;
|
|
||||||
$this->amountExcludingTax = $amountExcludingTax;
|
|
||||||
$this->tax = $tax;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ class InvoiceLines
|
||||||
|
|
||||||
public string $description = '';
|
public string $description = '';
|
||||||
|
|
||||||
public Tax $taxesDutiesFees;
|
public Tax $tax;
|
||||||
|
|
||||||
public array $allowanceCharges = []; //line item discounts
|
public array $allowanceCharges = []; //line item discounts
|
||||||
|
|
||||||
|
|
@ -102,14 +102,14 @@ class InvoiceLines
|
||||||
|
|
||||||
public function getTax(): Tax
|
public function getTax(): Tax
|
||||||
{
|
{
|
||||||
return $this->taxesDutiesFees;
|
return $this->tax;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setTax(Tax $tax): void
|
public function setTax(Tax $tax): void
|
||||||
{
|
{
|
||||||
$this->taxesDutiesFees = $tax;
|
$this->tax = $tax;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getAllowanceCharges(): array
|
public function getAllowanceCharges(): array
|
||||||
{
|
{
|
||||||
return $this->allowanceCharges;
|
return $this->allowanceCharges;
|
||||||
|
|
@ -125,6 +125,7 @@ class InvoiceLines
|
||||||
public function addAllowanceCharge($allowanceCharge): self
|
public function addAllowanceCharge($allowanceCharge): self
|
||||||
{
|
{
|
||||||
$this->allowanceCharges[] = $allowanceCharge;
|
$this->allowanceCharges[] = $allowanceCharge;
|
||||||
|
return $this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -163,8 +163,8 @@ class PaymentMeans
|
||||||
|
|
||||||
if($ubl_payment_means->PayeeFinancialAccount ?? false){
|
if($ubl_payment_means->PayeeFinancialAccount ?? false){
|
||||||
|
|
||||||
$this->account = $ubl_payment_means->PayeeFinancialAccount->ID;
|
$this->account = $ubl_payment_means->PayeeFinancialAccount->ID->value;
|
||||||
$this->branche_code = $ubl_payment_means->FinancialInstitutionBranch->ID;
|
$this->branche_code = $ubl_payment_means->FinancialInstitutionBranch->ID->value ?? null;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,17 @@
|
||||||
|
|
||||||
namespace App\Services\EDocument\Gateway\Transformers;
|
namespace App\Services\EDocument\Gateway\Transformers;
|
||||||
|
|
||||||
use App\Services\EDocument\Gateway\Storecove\Models\AccountingCustomerParty;
|
use App\Services\EDocument\Gateway\Storecove\Models\Tax;
|
||||||
use App\Services\EDocument\Gateway\Storecove\Models\AccountingSupplierParty;
|
use App\Services\EDocument\Gateway\Storecove\Models\Party;
|
||||||
use App\Services\EDocument\Gateway\Storecove\Models\Address;
|
use App\Services\EDocument\Gateway\Storecove\Models\Address;
|
||||||
use App\Services\EDocument\Gateway\Storecove\Models\Contact;
|
use App\Services\EDocument\Gateway\Storecove\Models\Contact;
|
||||||
use App\Services\EDocument\Gateway\Storecove\Models\Invoice as StorecoveInvoice;
|
|
||||||
use App\Services\EDocument\Gateway\Storecove\Models\InvoiceLines;
|
|
||||||
use App\Services\EDocument\Gateway\Storecove\Models\Party;
|
|
||||||
use App\Services\EDocument\Gateway\Storecove\Models\PaymentMeans;
|
|
||||||
use App\Services\EDocument\Gateway\Storecove\Models\References;
|
use App\Services\EDocument\Gateway\Storecove\Models\References;
|
||||||
|
use App\Services\EDocument\Gateway\Storecove\Models\InvoiceLines;
|
||||||
|
use App\Services\EDocument\Gateway\Storecove\Models\PaymentMeans;
|
||||||
|
use App\Services\EDocument\Gateway\Storecove\Models\AllowanceCharges;
|
||||||
|
use App\Services\EDocument\Gateway\Storecove\Models\AccountingCustomerParty;
|
||||||
|
use App\Services\EDocument\Gateway\Storecove\Models\AccountingSupplierParty;
|
||||||
|
use App\Services\EDocument\Gateway\Storecove\Models\Invoice as StorecoveInvoice;
|
||||||
|
|
||||||
class StorecoveTransformer implements TransformerInterface
|
class StorecoveTransformer implements TransformerInterface
|
||||||
{
|
{
|
||||||
|
|
@ -32,7 +34,7 @@ class StorecoveTransformer implements TransformerInterface
|
||||||
if (isset($peppolInvoice->InvoicePeriod[0]) &&
|
if (isset($peppolInvoice->InvoicePeriod[0]) &&
|
||||||
isset($peppolInvoice->InvoicePeriod[0]->StartDate) &&
|
isset($peppolInvoice->InvoicePeriod[0]->StartDate) &&
|
||||||
isset($peppolInvoice->InvoicePeriod[0]->EndDate)) {
|
isset($peppolInvoice->InvoicePeriod[0]->EndDate)) {
|
||||||
$this->s_invoice->setInvoicePeriod("{$peppolInvoice->InvoicePeriod[0]->StartDate} - {$peppolInvoice->InvoicePeriod[0]->EndDate}");
|
$this->s_invoice->setInvoicePeriod("{$peppolInvoice->InvoicePeriod[0]->StartDate->format('Y-m-d')} - {$peppolInvoice->InvoicePeriod[0]->EndDate->format('Y-m-d')}");
|
||||||
}
|
}
|
||||||
|
|
||||||
if($peppolInvoice->BuyerReference ?? false){
|
if($peppolInvoice->BuyerReference ?? false){
|
||||||
|
|
@ -41,7 +43,7 @@ class StorecoveTransformer implements TransformerInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($peppolInvoice->OrderReference->ID ?? false) {
|
if ($peppolInvoice->OrderReference->ID ?? false) {
|
||||||
$ref = new References(documentId: $peppolInvoice->OrderReference->ID, documentType: 'sales_order');
|
$ref = new References(documentId: $peppolInvoice->OrderReference->ID->value, documentType: 'sales_order');
|
||||||
$this->s_invoice->addReferences($ref);
|
$this->s_invoice->addReferences($ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -57,7 +59,7 @@ class StorecoveTransformer implements TransformerInterface
|
||||||
city: $peppolInvoice->AccountingCustomerParty->Party->PostalAddress->CityName,
|
city: $peppolInvoice->AccountingCustomerParty->Party->PostalAddress->CityName,
|
||||||
zip: $peppolInvoice->AccountingCustomerParty->Party->PostalAddress->PostalZone,
|
zip: $peppolInvoice->AccountingCustomerParty->Party->PostalAddress->PostalZone,
|
||||||
county: $peppolInvoice->AccountingCustomerParty->Party->PostalAddress->CountrySubentity ?? null,
|
county: $peppolInvoice->AccountingCustomerParty->Party->PostalAddress->CountrySubentity ?? null,
|
||||||
country: $peppolInvoice->AccountingCustomerParty->Party->PostalAddress->Country->IdentificationCode,
|
country: $peppolInvoice->AccountingCustomerParty->Party->PostalAddress->Country->IdentificationCode->value,
|
||||||
);
|
);
|
||||||
|
|
||||||
$contact = new Contact(
|
$contact = new Contact(
|
||||||
|
|
@ -90,11 +92,52 @@ class StorecoveTransformer implements TransformerInterface
|
||||||
$this->s_invoice->addPaymentMeans($payment_means);
|
$this->s_invoice->addPaymentMeans($payment_means);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach($peppolInvoice->InvoiceLine as $invoiceLine)
|
$lines = [];
|
||||||
|
|
||||||
|
foreach($peppolInvoice->InvoiceLine as $peppolLine)
|
||||||
{
|
{
|
||||||
|
|
||||||
$line = new InvoiceLines();
|
$line = new InvoiceLines();
|
||||||
|
|
||||||
|
// Basic line details
|
||||||
|
$line->setLineId($peppolLine->ID->value);
|
||||||
|
$line->setQuantity((int)$peppolLine->InvoicedQuantity);
|
||||||
|
$line->setItemPrice((float)$peppolLine->Price->PriceAmount->amount);
|
||||||
|
$line->setAmountExcludingVat((float)$peppolLine->LineExtensionAmount->amount);
|
||||||
|
|
||||||
|
// Item details
|
||||||
|
$line->setName($peppolLine->Item->Name);
|
||||||
|
$line->setDescription($peppolLine->Item->Description);
|
||||||
|
|
||||||
|
// Tax handling
|
||||||
|
if(isset($peppolLine->Item->ClassifiedTaxCategory) && is_array($peppolLine->Item->ClassifiedTaxCategory)){
|
||||||
|
foreach($peppolLine->Item->ClassifiedTaxCategory as $ctc)
|
||||||
|
{
|
||||||
|
$tax = new Tax((float)$ctc->Percent, $this->resolveJurisdication($ctc, $peppolInvoice));
|
||||||
|
$line->setTax($tax);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//discounts
|
||||||
|
if(isset($peppolLine->Price->AllowanceCharge) && is_array($peppolLine->Price->AllowanceCharge)){
|
||||||
|
|
||||||
|
foreach($peppolLine->Price->AllowanceCharge as $allowance)
|
||||||
|
{
|
||||||
|
$reason = $allowance->ChargeIndicator ? ctrans('texts.fee') : ctrans('texts.discount');
|
||||||
|
$amount = $allowance->Amount->amount;
|
||||||
|
|
||||||
|
$ac = new AllowanceCharges(reason: $reason, amountExcludingTax: $amount);
|
||||||
|
$line->addAllowanceCharge($ac);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$lines[] = $line;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->s_invoice->invoiceLines = $lines;
|
||||||
|
|
||||||
// // Map tax total at invoice level
|
// // Map tax total at invoice level
|
||||||
// $taxTotal = [];
|
// $taxTotal = [];
|
||||||
// if (isset($peppolInvoice->InvoiceLine[0]->TaxTotal[0])) {
|
// if (isset($peppolInvoice->InvoiceLine[0]->TaxTotal[0])) {
|
||||||
|
|
@ -143,6 +186,14 @@ class StorecoveTransformer implements TransformerInterface
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function resolveJurisdication($ctc, $peppolInvoice): string
|
||||||
|
{
|
||||||
|
if(isset($ctc->TaxTotal[0]->JurisdictionRegionAddress->Country->IdentificationCode->value))
|
||||||
|
return $ctc->TaxTotal[0]->JurisdictionRegionAddress->Country->IdentificationCode->value;
|
||||||
|
|
||||||
|
return $peppolInvoice->AccountingSupplierParty->Party->PhysicalLocation->Country->IdentificationCode->value;
|
||||||
|
}
|
||||||
|
|
||||||
public function getInvoice(): StorecoveInvoice
|
public function getInvoice(): StorecoveInvoice
|
||||||
{
|
{
|
||||||
return $this->s_invoice;
|
return $this->s_invoice;
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
namespace App\Services\EDocument\Standards;
|
namespace App\Services\EDocument\Standards;
|
||||||
|
|
||||||
|
use App\DataMapper\Tax\BaseRule;
|
||||||
use App\Models\Company;
|
use App\Models\Company;
|
||||||
use App\Models\Invoice;
|
use App\Models\Invoice;
|
||||||
use App\Models\Product;
|
use App\Models\Product;
|
||||||
|
|
@ -671,13 +672,59 @@ class Peppol extends AbstractService
|
||||||
$line->TaxTotal = $item_taxes;
|
$line->TaxTotal = $item_taxes;
|
||||||
}
|
}
|
||||||
|
|
||||||
$price = new Price();
|
// $price = new Price();
|
||||||
$pa = new PriceAmount();
|
// $pa = new PriceAmount();
|
||||||
$pa->currencyID = $this->invoice->client->currency()->code;
|
// $pa->currencyID = $this->invoice->client->currency()->code;
|
||||||
$pa->amount = (string) ($this->costWithDiscount($item) - ($this->invoice->uses_inclusive_taxes ? ($this->calcInclusiveLineTax($item->tax_rate1, $item->line_total) / $item->quantity) : 0));
|
// $pa->amount = (string) ($this->costWithDiscount($item) - ($this->invoice->uses_inclusive_taxes ? ($this->calcInclusiveLineTax($item->tax_rate1, $item->line_total) / $item->quantity) : 0));
|
||||||
$price->PriceAmount = $pa;
|
// $price->PriceAmount = $pa;
|
||||||
|
|
||||||
|
// $line->Price = $price;
|
||||||
|
|
||||||
|
// Handle Price and Discounts
|
||||||
|
if ($item->discount > 0) {
|
||||||
|
// Base Price (before discount)
|
||||||
|
$basePrice = new Price();
|
||||||
|
$basePriceAmount = new PriceAmount();
|
||||||
|
$basePriceAmount->currencyID = $this->invoice->client->currency()->code;
|
||||||
|
$basePriceAmount->amount = (string)$this->getBasePrice($item);
|
||||||
|
$basePrice->PriceAmount = $basePriceAmount;
|
||||||
|
|
||||||
|
// Add Allowance Charge to Price
|
||||||
|
$allowanceCharge = new \InvoiceNinja\EInvoice\Models\Peppol\AllowanceChargeType\AllowanceCharge();
|
||||||
|
$allowanceCharge->ChargeIndicator = false; // false = discount
|
||||||
|
$allowanceCharge->Amount = new \InvoiceNinja\EInvoice\Models\Peppol\AmountType\Amount();
|
||||||
|
$allowanceCharge->Amount->currencyID = $this->invoice->client->currency()->code;
|
||||||
|
$allowanceCharge->Amount->amount = (string)$this->calculateDiscountAmount($item);
|
||||||
|
$allowanceCharge->BaseAmount = new \InvoiceNinja\EInvoice\Models\Peppol\AmountType\BaseAmount();
|
||||||
|
$allowanceCharge->BaseAmount->currencyID = $this->invoice->client->currency()->code;
|
||||||
|
$allowanceCharge->BaseAmount->amount = (string)$this->getBasePrice($item);
|
||||||
|
|
||||||
|
// Add percentage if available
|
||||||
|
if ($item->discount > 0 && !$item->is_amount_discount) {
|
||||||
|
$mfn = new \InvoiceNinja\EInvoice\Models\Peppol\NumericType\MultiplierFactorNumeric();
|
||||||
|
$mfn->value = (string) ($item->discount / 100);
|
||||||
|
$allowanceCharge->MultiplierFactorNumeric = $mfn; // Convert percentage to decimal
|
||||||
|
}
|
||||||
|
|
||||||
|
$basePrice->AllowanceCharge[] = $allowanceCharge;
|
||||||
|
$line->Price = $basePrice;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// No discount case
|
||||||
|
$price = new Price();
|
||||||
|
$pa = new PriceAmount();
|
||||||
|
$pa->currencyID = $this->invoice->client->currency()->code;
|
||||||
|
$pa->amount = (string) ($this->costWithDiscount($item) - ($this->invoice->uses_inclusive_taxes
|
||||||
|
? ($this->calcInclusiveLineTax($item->tax_rate1, $item->line_total) / $item->quantity)
|
||||||
|
: 0));
|
||||||
|
$price->PriceAmount = $pa;
|
||||||
|
$line->Price = $price;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
$line->Price = $price;
|
|
||||||
|
|
||||||
$lines[] = $line;
|
$lines[] = $line;
|
||||||
}
|
}
|
||||||
|
|
@ -685,6 +732,21 @@ class Peppol extends AbstractService
|
||||||
return $lines;
|
return $lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private function getBasePrice($item): float
|
||||||
|
{
|
||||||
|
return $item->cost;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function calculateDiscountAmount($item): float
|
||||||
|
{
|
||||||
|
if ($item->is_amount_discount) {
|
||||||
|
return $item->discount; // Per unit discount amount
|
||||||
|
}
|
||||||
|
|
||||||
|
return $item->cost * ($item->discount / 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* costWithDiscount
|
* costWithDiscount
|
||||||
|
|
@ -772,7 +834,6 @@ class Peppol extends AbstractService
|
||||||
$taxable_amount->amount = $this->invoice->uses_inclusive_taxes ? $item->line_total - $tax_amount->amount : $item->line_total;
|
$taxable_amount->amount = $this->invoice->uses_inclusive_taxes ? $item->line_total - $tax_amount->amount : $item->line_total;
|
||||||
$tax_subtotal->TaxableAmount = $taxable_amount;
|
$tax_subtotal->TaxableAmount = $taxable_amount;
|
||||||
|
|
||||||
|
|
||||||
$tc = new TaxCategory();
|
$tc = new TaxCategory();
|
||||||
|
|
||||||
$id = new ID();
|
$id = new ID();
|
||||||
|
|
@ -785,6 +846,9 @@ class Peppol extends AbstractService
|
||||||
$id = new ID();
|
$id = new ID();
|
||||||
$id->value = $item->tax_name1;
|
$id->value = $item->tax_name1;
|
||||||
|
|
||||||
|
$jurisdiction = $this->getJurisdiction();
|
||||||
|
$ts->JurisdictionRegionAddress[] = $jurisdiction;
|
||||||
|
|
||||||
$ts->ID = $id;
|
$ts->ID = $id;
|
||||||
$tc->TaxScheme = $ts;
|
$tc->TaxScheme = $ts;
|
||||||
$tax_subtotal->TaxCategory = $tc;
|
$tax_subtotal->TaxCategory = $tc;
|
||||||
|
|
@ -824,6 +888,9 @@ class Peppol extends AbstractService
|
||||||
|
|
||||||
$id = new ID();
|
$id = new ID();
|
||||||
$id->value = $item->tax_name2;
|
$id->value = $item->tax_name2;
|
||||||
|
|
||||||
|
$jurisdiction = $this->getJurisdiction();
|
||||||
|
$ts->JurisdictionRegionAddress[] = $jurisdiction;
|
||||||
|
|
||||||
$ts->ID = $id;
|
$ts->ID = $id;
|
||||||
$tc->TaxScheme = $ts;
|
$tc->TaxScheme = $ts;
|
||||||
|
|
@ -866,6 +933,9 @@ class Peppol extends AbstractService
|
||||||
$id = new ID();
|
$id = new ID();
|
||||||
$id->value = $item->tax_name3;
|
$id->value = $item->tax_name3;
|
||||||
|
|
||||||
|
$jurisdiction = $this->getJurisdiction();
|
||||||
|
$ts->JurisdictionRegionAddress[] = $jurisdiction;
|
||||||
|
|
||||||
$ts->ID = $id;
|
$ts->ID = $id;
|
||||||
$tc->TaxScheme = $ts;
|
$tc->TaxScheme = $ts;
|
||||||
$tax_subtotal->TaxCategory = $tc;
|
$tax_subtotal->TaxCategory = $tc;
|
||||||
|
|
@ -908,7 +978,7 @@ class Peppol extends AbstractService
|
||||||
$vatID->schemeID = $scheme;
|
$vatID->schemeID = $scheme;
|
||||||
}
|
}
|
||||||
|
|
||||||
$vatID->value = $this->company->settings->vat_number; //todo if we are cross border - switch to the supplier local vat number
|
$vatID->value = $this->company->settings->vat_number; //todo if we are cross border - switch to the supplier local vat number ->vat_number; //todo if we are cross border - switch to the supplier local vat number
|
||||||
$pi->ID = $vatID;
|
$pi->ID = $vatID;
|
||||||
|
|
||||||
$party->PartyIdentification[] = $pi;
|
$party->PartyIdentification[] = $pi;
|
||||||
|
|
@ -921,7 +991,6 @@ class Peppol extends AbstractService
|
||||||
// $address->BuildingName = $this->invoice->company->settings->address2;
|
// $address->BuildingName = $this->invoice->company->settings->address2;
|
||||||
$address->PostalZone = $this->invoice->company->settings->postal_code;
|
$address->PostalZone = $this->invoice->company->settings->postal_code;
|
||||||
$address->CountrySubentity = $this->invoice->company->settings->state;
|
$address->CountrySubentity = $this->invoice->company->settings->state;
|
||||||
// $address->CountrySubentityCode = $this->invoice->company->settings->state;
|
|
||||||
|
|
||||||
$country = new Country();
|
$country = new Country();
|
||||||
|
|
||||||
|
|
@ -1018,7 +1087,7 @@ class Peppol extends AbstractService
|
||||||
|
|
||||||
$contact->ElectronicMail = $this->invoice->client->present()->email();
|
$contact->ElectronicMail = $this->invoice->client->present()->email();
|
||||||
|
|
||||||
if(isset($this->invoice->client->phone) && strlen($this->invoice->client->phone >2))
|
if(isset($this->invoice->client->phone) && strlen($this->invoice->client->phone) > 2)
|
||||||
$contact->Telephone = $this->invoice->client->phone;
|
$contact->Telephone = $this->invoice->client->phone;
|
||||||
|
|
||||||
$party->Contact = $contact;
|
$party->Contact = $contact;
|
||||||
|
|
@ -1091,49 +1160,82 @@ class Peppol extends AbstractService
|
||||||
public function setInvoiceDefaults(): self
|
public function setInvoiceDefaults(): self
|
||||||
{
|
{
|
||||||
|
|
||||||
// Stub new invoice with company settings.
|
// Stub new invoice with company settings.
|
||||||
if($this->_company_settings)
|
if($this->_company_settings)
|
||||||
{
|
{
|
||||||
foreach(get_object_vars($this->_company_settings) as $prop => $value){
|
foreach(get_object_vars($this->_company_settings) as $prop => $value){
|
||||||
$this->p_invoice->{$prop} = $value;
|
$this->p_invoice->{$prop} = $value;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overwrite with any client level settings
|
||||||
|
if($this->_client_settings)
|
||||||
|
{
|
||||||
|
foreach (get_object_vars($this->_client_settings) as $prop => $value) {
|
||||||
|
$this->p_invoice->{$prop} = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Plucks special overriding properties scanning the correct settings level
|
||||||
|
$settings = [
|
||||||
|
'AccountingCostCode' => 7,
|
||||||
|
'AccountingCost' => 7,
|
||||||
|
'BuyerReference' => 6,
|
||||||
|
'AccountingSupplierParty' => 1,
|
||||||
|
'AccountingCustomerParty' => 2,
|
||||||
|
'PayeeParty' => 1,
|
||||||
|
'BuyerCustomerParty' => 2,
|
||||||
|
'SellerSupplierParty' => 1,
|
||||||
|
'TaxRepresentativeParty' => 1,
|
||||||
|
'Delivery' => 1,
|
||||||
|
'DeliveryTerms' => 7,
|
||||||
|
'PaymentMeans' => 7,
|
||||||
|
'PaymentTerms' => 7,
|
||||||
|
];
|
||||||
|
|
||||||
|
//only scans for top level props
|
||||||
|
foreach($settings as $prop => $visibility) {
|
||||||
|
|
||||||
|
if($prop_value = $this->gateway->mutator->getSetting($prop)) {
|
||||||
|
$this->p_invoice->{$prop} = $prop_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Overwrite with any client level settings
|
}
|
||||||
if($this->_client_settings)
|
|
||||||
{
|
|
||||||
foreach (get_object_vars($this->_client_settings) as $prop => $value) {
|
|
||||||
$this->p_invoice->{$prop} = $value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Plucks special overriding properties scanning the correct settings level
|
return $this;
|
||||||
$settings = [
|
|
||||||
'AccountingCostCode' => 7,
|
|
||||||
'AccountingCost' => 7,
|
|
||||||
'BuyerReference' => 6,
|
|
||||||
'AccountingSupplierParty' => 1,
|
|
||||||
'AccountingCustomerParty' => 2,
|
|
||||||
'PayeeParty' => 1,
|
|
||||||
'BuyerCustomerParty' => 2,
|
|
||||||
'SellerSupplierParty' => 1,
|
|
||||||
'TaxRepresentativeParty' => 1,
|
|
||||||
'Delivery' => 1,
|
|
||||||
'DeliveryTerms' => 7,
|
|
||||||
'PaymentMeans' => 7,
|
|
||||||
'PaymentTerms' => 7,
|
|
||||||
];
|
|
||||||
|
|
||||||
//only scans for top level props
|
|
||||||
foreach($settings as $prop => $visibility) {
|
|
||||||
|
|
||||||
if($prop_value = $this->gateway->mutator->getSetting($prop)) {
|
|
||||||
$this->p_invoice->{$prop} = $prop_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getJurisdiction()
|
||||||
|
{
|
||||||
|
|
||||||
|
//calculate nexus
|
||||||
|
$country_code = $this->company->country()->iso_3166_2;
|
||||||
|
$br = new BaseRule();
|
||||||
|
$eu_countries = $br->eu_country_codes;
|
||||||
|
|
||||||
|
if($this->invoice->client->country->iso_3166_2 == $this->company->country()->iso_3166_2){
|
||||||
|
//Domestic Sales
|
||||||
|
$country_code = $this->company->country()->iso_3166_2;
|
||||||
|
}
|
||||||
|
elseif(in_array($country_code, $eu_countries) && !in_array($this->invoice->client->country->iso_3166_2, $eu_countries)){
|
||||||
|
//NON-EU sale
|
||||||
|
}
|
||||||
|
elseif(in_array($country_code, $eu_countries) && in_array($this->invoice->client->country->iso_3166_2, $eu_countries)){
|
||||||
|
//EU Sale
|
||||||
|
if($this->company->tax_data->regions->EU->has_sales_above_threshold || !$this->invoice->client->has_valid_vat_number){ //over threshold - tax in buyer country
|
||||||
|
$country_code = $this->invoice->client->country->iso_3166_2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$jurisdiction = new \InvoiceNinja\EInvoice\Models\Peppol\AddressType\JurisdictionRegionAddress();
|
||||||
|
$country = new Country();
|
||||||
|
$ic = new IdentificationCode();
|
||||||
|
$ic->value = $country_code;
|
||||||
|
$country->IdentificationCode = $ic;
|
||||||
|
$jurisdiction->Country = $country;
|
||||||
|
|
||||||
|
return $jurisdiction;
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -178,7 +178,7 @@ class ZugferdEDokument extends AbstractService
|
||||||
if (!$company->tax_data->regions->EU->has_sales_above_threshold){
|
if (!$company->tax_data->regions->EU->has_sales_above_threshold){
|
||||||
// According to european law, each line item can only have one tax rate
|
// According to european law, each line item can only have one tax rate
|
||||||
if (!(empty($item->tax_name1) && empty($item->tax_name2) && empty($item->tax_name3))) {
|
if (!(empty($item->tax_name1) && empty($item->tax_name2) && empty($item->tax_name3))) {
|
||||||
$taxtype = $this->getTaxType($item->tax_id);
|
$taxtype = $this->getTaxType($item->tax_id ?? 2);
|
||||||
if (!empty($item->tax_name1)) {
|
if (!empty($item->tax_name1)) {
|
||||||
if ($taxtype == ZugferdDutyTaxFeeCategories::VAT_EXEMPT_FOR_EEA_INTRACOMMUNITY_SUPPLY_OF_GOODS_AND_SERVICES) {
|
if ($taxtype == ZugferdDutyTaxFeeCategories::VAT_EXEMPT_FOR_EEA_INTRACOMMUNITY_SUPPLY_OF_GOODS_AND_SERVICES) {
|
||||||
$this->xdocument->addDocumentPositionTax($taxtype, 'VAT', $item->tax_rate1, exemptionReason: ctrans('texts.intracommunity_tax_info'));
|
$this->xdocument->addDocumentPositionTax($taxtype, 'VAT', $item->tax_rate1, exemptionReason: ctrans('texts.intracommunity_tax_info'));
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@ return new class extends Migration
|
||||||
{
|
{
|
||||||
public function up(): void
|
public function up(): void
|
||||||
{
|
{
|
||||||
Schema::table('companies', function (Blueprint $table) {
|
// Schema::table('companies', function (Blueprint $table) {
|
||||||
$table->string('e_invoicing_token')->nullable();
|
// $table->string('e_invoicing_token')->nullable();
|
||||||
});
|
// });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -95,6 +95,96 @@ class StorecoveTest extends TestCase
|
||||||
nlog($json);
|
nlog($json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testStorecoveTransformer()
|
||||||
|
{
|
||||||
|
|
||||||
|
$e_invoice = new \InvoiceNinja\EInvoice\Models\Peppol\Invoice();
|
||||||
|
|
||||||
|
$invoice = $this->createATData();
|
||||||
|
|
||||||
|
$item = new InvoiceItem();
|
||||||
|
$item->product_key = "Product Key";
|
||||||
|
$item->notes = "Product Description";
|
||||||
|
$item->cost = 10;
|
||||||
|
$item->quantity = 10;
|
||||||
|
$item->is_amount_discount = true;
|
||||||
|
$item->discount=5;
|
||||||
|
$item->tax_rate1 = 20;
|
||||||
|
$item->tax_name1 = 'VAT';
|
||||||
|
|
||||||
|
$invoice->line_items = [$item];
|
||||||
|
$invoice->calc()->getInvoice();
|
||||||
|
|
||||||
|
$stub = json_decode('{"Invoice":{"Note":"Nooo","PaymentMeans":[{"ID":{"value":"afdasfasdfasdfas"},"PayeeFinancialAccount":{"Name":"PFA-NAME","ID":{"value":"DE89370400440532013000"},"AliasName":"PFA-Alias","AccountTypeCode":{"value":"CHECKING"},"AccountFormatCode":{"value":"IBAN"},"CurrencyCode":{"value":"EUR"},"FinancialInstitutionBranch":{"ID":{"value":"DEUTDEMMXXX"},"Name":"Deutsche Bank"}}}]}}');
|
||||||
|
foreach ($stub as $key => $value) {
|
||||||
|
$e_invoice->{$key} = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
$invoice->e_invoice = $e_invoice;
|
||||||
|
|
||||||
|
$p = new Peppol($invoice);
|
||||||
|
$p->run();
|
||||||
|
$peppolInvoice = $p->getInvoice();
|
||||||
|
|
||||||
|
|
||||||
|
$s_transformer = new StorecoveTransformer();
|
||||||
|
$s_transformer->transform($peppolInvoice);
|
||||||
|
|
||||||
|
$json = $s_transformer->toJson();
|
||||||
|
|
||||||
|
$this->assertJson($json);
|
||||||
|
|
||||||
|
nlog($json);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testStorecoveTransformerWithPercentageDiscount()
|
||||||
|
{
|
||||||
|
|
||||||
|
$e_invoice = new \InvoiceNinja\EInvoice\Models\Peppol\Invoice();
|
||||||
|
|
||||||
|
$invoice = $this->createATData();
|
||||||
|
$invoice->is_amount_discount = false;
|
||||||
|
|
||||||
|
$item = new InvoiceItem();
|
||||||
|
$item->product_key = "Product Key";
|
||||||
|
$item->notes = "Product Description";
|
||||||
|
$item->cost = 10;
|
||||||
|
$item->quantity = 10;
|
||||||
|
$item->is_amount_discount = false;
|
||||||
|
$item->discount=5;
|
||||||
|
$item->tax_rate1 = 20;
|
||||||
|
$item->tax_name1 = 'VAT';
|
||||||
|
|
||||||
|
$invoice->line_items = [$item];
|
||||||
|
$invoice->calc()->getInvoice();
|
||||||
|
|
||||||
|
$stub = json_decode('{"Invoice":{"Note":"Nooo","PaymentMeans":[{"ID":{"value":"afdasfasdfasdfas"},"PayeeFinancialAccount":{"Name":"PFA-NAME","ID":{"value":"DE89370400440532013000"},"AliasName":"PFA-Alias","AccountTypeCode":{"value":"CHECKING"},"AccountFormatCode":{"value":"IBAN"},"CurrencyCode":{"value":"EUR"},"FinancialInstitutionBranch":{"ID":{"value":"DEUTDEMMXXX"},"Name":"Deutsche Bank"}}}]}}');
|
||||||
|
foreach ($stub as $key => $value) {
|
||||||
|
$e_invoice->{$key} = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
$invoice->e_invoice = $e_invoice;
|
||||||
|
|
||||||
|
$p = new Peppol($invoice);
|
||||||
|
$p->run();
|
||||||
|
$peppolInvoice = $p->getInvoice();
|
||||||
|
|
||||||
|
|
||||||
|
$s_transformer = new StorecoveTransformer();
|
||||||
|
$s_transformer->transform($peppolInvoice);
|
||||||
|
|
||||||
|
$json = $s_transformer->toJson();
|
||||||
|
|
||||||
|
$this->assertJson($json);
|
||||||
|
|
||||||
|
nlog("percentage");
|
||||||
|
nlog($json);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public function testUnsetOfVatNumers()
|
public function testUnsetOfVatNumers()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue