Improvements for rounding calculations for discounted item amounts

This commit is contained in:
David Bomba 2024-11-05 19:25:15 +11:00
parent ffc1f4789a
commit d93ac9ae59
7 changed files with 231 additions and 363 deletions

View File

@ -207,46 +207,6 @@ class InvoiceItemSum
return $this; return $this;
} }
private function calculateNexus()
{
$company_country_code = $this->invoice->company->country()->iso_3166_2;
$client_country_code = $this->client->country->iso_3166_2;
$base_rule = new \App\DataMapper\Tax\BaseRule();
$eu_countries = $base_rule->eu_country_codes;
$nexus_rule = $company_country_code;
if ($client_country_code == $company_country_code) {
//Domestic Sales
$nexus_rule = $company_country_code;
} elseif (in_array($company_country_code, $eu_countries) && !in_array($client_country_code, $eu_countries)) {
//NON-EU Sale
$nexus_rule = $company_country_code;
} elseif (in_array($company_country_code, $eu_countries) && in_array($client_country_code, $eu_countries)) {
//EU Sale
// Invalid VAT number = seller country nexus
if(isset($this->client->has_valid_vat_number) && !$this->client->has_valid_vat_number){
$nexus_rule = $company_country_code;
}
elseif (isset($this->invoice->company->tax_data->regions->EU->has_sales_above_threshold) && $this->invoice->company->tax_data->regions->EU->has_sales_above_threshold) { //over threshold - tax in buyer country
$nexus_rule = $client_country_code;
} elseif (isset($this->invoice->company->tax_data->regions->EU->has_sales_above_threshold) && !$this->invoice->company->tax_data->regions->EU->has_sales_above_threshold) {
$nexus_rule = $company_country_code;
} elseif ($this->client->classification != 'individual' && (isset($this->client->has_valid_vat_number) && !$this->client->has_valid_vat_number)){
$nexus_rule = $company_country_code;
} else {
$nexus_rule = $company_country_code;
}
}
nlog($nexus_rule);
$class = "App\DataMapper\Tax\\".str_replace("-", "_", $nexus_rule)."\\Rule";
return $class;
}
private function push(): self private function push(): self
{ {
$this->sub_total += round($this->getLineTotal(), $this->currency->precision); $this->sub_total += round($this->getLineTotal(), $this->currency->precision);
@ -335,7 +295,7 @@ class InvoiceItemSum
$item_tax += $item_tax_rate1_total; $item_tax += $item_tax_rate1_total;
if (strlen($this->item->tax_name1) > 1) { if (strlen($this->item->tax_name1) > 1) {
$this->groupTax($this->item->tax_name1, $this->item->tax_rate1, $item_tax_rate1_total); $this->groupTax($this->item->tax_name1, $this->item->tax_rate1, $item_tax_rate1_total, $amount, $this->item->tax_id);
} }
$item_tax_rate2_total = $this->calcAmountLineTax($this->item->tax_rate2, $amount); $item_tax_rate2_total = $this->calcAmountLineTax($this->item->tax_rate2, $amount);
@ -343,7 +303,7 @@ class InvoiceItemSum
$item_tax += $item_tax_rate2_total; $item_tax += $item_tax_rate2_total;
if (strlen($this->item->tax_name2) > 1) { if (strlen($this->item->tax_name2) > 1) {
$this->groupTax($this->item->tax_name2, $this->item->tax_rate2, $item_tax_rate2_total); $this->groupTax($this->item->tax_name2, $this->item->tax_rate2, $item_tax_rate2_total, $amount, $this->item->tax_id);
} }
$item_tax_rate3_total = $this->calcAmountLineTax($this->item->tax_rate3, $amount); $item_tax_rate3_total = $this->calcAmountLineTax($this->item->tax_rate3, $amount);
@ -351,7 +311,7 @@ class InvoiceItemSum
$item_tax += $item_tax_rate3_total; $item_tax += $item_tax_rate3_total;
if (strlen($this->item->tax_name3) > 1) { if (strlen($this->item->tax_name3) > 1) {
$this->groupTax($this->item->tax_name3, $this->item->tax_rate3, $item_tax_rate3_total); $this->groupTax($this->item->tax_name3, $this->item->tax_rate3, $item_tax_rate3_total, $amount, $this->item->tax_id);
} }
$this->setTotalTaxes($this->formatValue($item_tax, $this->currency->precision)); $this->setTotalTaxes($this->formatValue($item_tax, $this->currency->precision));
@ -375,6 +335,7 @@ class InvoiceItemSum
->map(fn ($i) => [ ->map(fn ($i) => [
'name' => $item->{"tax_name{$i}"} ?? '', 'name' => $item->{"tax_name{$i}"} ?? '',
'percentage' => $item->{"tax_rate{$i}"} ?? 0, 'percentage' => $item->{"tax_rate{$i}"} ?? 0,
'tax_id' => $item->tax_id ?? '1',
]) ])
->filter(fn ($tax) => strlen($tax['name']) > 1); ->filter(fn ($tax) => strlen($tax['name']) > 1);
}) })
@ -400,21 +361,23 @@ class InvoiceItemSum
$tax_component += round($this->invoice->custom_surcharge4 * ($tax['percentage'] / 100), 2); $tax_component += round($this->invoice->custom_surcharge4 * ($tax['percentage'] / 100), 2);
} }
$amount = $this->invoice->custom_surcharge4 + $this->invoice->custom_surcharge3 + $this->invoice->custom_surcharge2 + $this->invoice->custom_surcharge1;
if($tax_component > 0) if($tax_component > 0)
$this->groupTax($tax['name'], $tax['percentage'], $tax_component); $this->groupTax($tax['name'], $tax['percentage'], $tax_component, $amount, $tax['tax_id']);
}); });
return $this; return $this;
} }
private function groupTax($tax_name, $tax_rate, $tax_total) private function groupTax($tax_name, $tax_rate, $tax_total, $amount, $tax_id = '')
{ {
$group_tax = []; $group_tax = [];
$key = str_replace(' ', '', $tax_name.$tax_rate); $key = str_replace(' ', '', $tax_name.$tax_rate);
$group_tax = ['key' => $key, 'total' => $tax_total, 'tax_name' => $tax_name.' '.Number::formatValueNoTrailingZeroes(floatval($tax_rate), $this->client).'%']; $group_tax = ['key' => $key, 'total' => $tax_total, 'tax_name' => $tax_name.' '.Number::formatValueNoTrailingZeroes(floatval($tax_rate), $this->client).'%', 'tax_id' => $tax_id, 'tax_rate' => $tax_rate, 'base_amount' => $amount];
$this->tax_collection->push(collect($group_tax)); $this->tax_collection->push(collect($group_tax));
} }
@ -516,7 +479,7 @@ class InvoiceItemSum
$item_tax += $item_tax_rate1_total; $item_tax += $item_tax_rate1_total;
if ($item_tax_rate1_total != 0) { if ($item_tax_rate1_total != 0) {
$this->groupTax($this->item->tax_name1, $this->item->tax_rate1, $item_tax_rate1_total); $this->groupTax($this->item->tax_name1, $this->item->tax_rate1, $item_tax_rate1_total, $amount, $this->item->tax_id);
} }
$item_tax_rate2_total = $this->calcAmountLineTax($this->item->tax_rate2, $amount); $item_tax_rate2_total = $this->calcAmountLineTax($this->item->tax_rate2, $amount);
@ -524,7 +487,7 @@ class InvoiceItemSum
$item_tax += $item_tax_rate2_total; $item_tax += $item_tax_rate2_total;
if ($item_tax_rate2_total != 0) { if ($item_tax_rate2_total != 0) {
$this->groupTax($this->item->tax_name2, $this->item->tax_rate2, $item_tax_rate2_total); $this->groupTax($this->item->tax_name2, $this->item->tax_rate2, $item_tax_rate2_total, $amount, $this->item->tax_id);
} }
$item_tax_rate3_total = $this->calcAmountLineTax($this->item->tax_rate3, $amount); $item_tax_rate3_total = $this->calcAmountLineTax($this->item->tax_rate3, $amount);
@ -532,7 +495,7 @@ class InvoiceItemSum
$item_tax += $item_tax_rate3_total; $item_tax += $item_tax_rate3_total;
if ($item_tax_rate3_total != 0) { if ($item_tax_rate3_total != 0) {
$this->groupTax($this->item->tax_name3, $this->item->tax_rate3, $item_tax_rate3_total); $this->groupTax($this->item->tax_name3, $this->item->tax_rate3, $item_tax_rate3_total, $amount, $this->item->tax_id);
} }
$this->item->gross_line_total = $this->getLineTotal() + $item_tax; $this->item->gross_line_total = $this->getLineTotal() + $item_tax;

View File

@ -244,7 +244,7 @@ class InvoiceItemSumInclusive
$item_tax += $this->formatValue($item_tax_rate1_total, $this->currency->precision); $item_tax += $this->formatValue($item_tax_rate1_total, $this->currency->precision);
if (strlen($this->item->tax_name1) > 1) { if (strlen($this->item->tax_name1) > 1) {
$this->groupTax($this->item->tax_name1, $this->item->tax_rate1, $item_tax_rate1_total); $this->groupTax($this->item->tax_name1, $this->item->tax_rate1, $item_tax_rate1_total, $amount, $this->item->tax_id);
} }
$item_tax_rate2_total = $this->calcInclusiveLineTax($this->item->tax_rate2, $amount); $item_tax_rate2_total = $this->calcInclusiveLineTax($this->item->tax_rate2, $amount);
@ -252,7 +252,7 @@ class InvoiceItemSumInclusive
$item_tax += $this->formatValue($item_tax_rate2_total, $this->currency->precision); $item_tax += $this->formatValue($item_tax_rate2_total, $this->currency->precision);
if (strlen($this->item->tax_name2) > 1) { if (strlen($this->item->tax_name2) > 1) {
$this->groupTax($this->item->tax_name2, $this->item->tax_rate2, $item_tax_rate2_total); $this->groupTax($this->item->tax_name2, $this->item->tax_rate2, $item_tax_rate2_total, $amount, $this->item->tax_id);
} }
$item_tax_rate3_total = $this->calcInclusiveLineTax($this->item->tax_rate3, $amount); $item_tax_rate3_total = $this->calcInclusiveLineTax($this->item->tax_rate3, $amount);
@ -260,7 +260,7 @@ class InvoiceItemSumInclusive
$item_tax += $this->formatValue($item_tax_rate3_total, $this->currency->precision); $item_tax += $this->formatValue($item_tax_rate3_total, $this->currency->precision);
if (strlen($this->item->tax_name3) > 1) { if (strlen($this->item->tax_name3) > 1) {
$this->groupTax($this->item->tax_name3, $this->item->tax_rate3, $item_tax_rate3_total); $this->groupTax($this->item->tax_name3, $this->item->tax_rate3, $item_tax_rate3_total, $amount, $this->item->tax_id);
} }
$this->item->tax_amount = $this->formatValue($item_tax, $this->currency->precision); $this->item->tax_amount = $this->formatValue($item_tax, $this->currency->precision);
@ -270,13 +270,13 @@ class InvoiceItemSumInclusive
return $this; return $this;
} }
private function groupTax($tax_name, $tax_rate, $tax_total) private function groupTax($tax_name, $tax_rate, $tax_total, $amount, $tax_id = '')
{ {
$group_tax = []; $group_tax = [];
$key = str_replace(' ', '', $tax_name.$tax_rate); $key = str_replace(' ', '', $tax_name.$tax_rate);
$group_tax = ['key' => $key, 'total' => $tax_total, 'tax_name' => $tax_name.' '.Number::formatValueNoTrailingZeroes(floatval($tax_rate), $this->client).'%']; $group_tax = ['key' => $key, 'total' => $tax_total, 'tax_name' => $tax_name.' '.Number::formatValueNoTrailingZeroes(floatval($tax_rate), $this->client).'%', 'tax_id' => $tax_id, 'base_amoount' => $amount];
$this->tax_collection->push(collect($group_tax)); $this->tax_collection->push(collect($group_tax));
} }
@ -376,7 +376,7 @@ class InvoiceItemSumInclusive
$item_tax += $item_tax_rate1_total; $item_tax += $item_tax_rate1_total;
if ($item_tax_rate1_total != 0) { if ($item_tax_rate1_total != 0) {
$this->groupTax($this->item->tax_name1, $this->item->tax_rate1, $item_tax_rate1_total); $this->groupTax($this->item->tax_name1, $this->item->tax_rate1, $item_tax_rate1_total, $amount, $this->item->tax_id);
} }
$item_tax_rate2_total = $this->calcInclusiveLineTax($this->item->tax_rate2, $amount); $item_tax_rate2_total = $this->calcInclusiveLineTax($this->item->tax_rate2, $amount);
@ -384,7 +384,7 @@ class InvoiceItemSumInclusive
$item_tax += $item_tax_rate2_total; $item_tax += $item_tax_rate2_total;
if ($item_tax_rate2_total != 0) { if ($item_tax_rate2_total != 0) {
$this->groupTax($this->item->tax_name2, $this->item->tax_rate2, $item_tax_rate2_total); $this->groupTax($this->item->tax_name2, $this->item->tax_rate2, $item_tax_rate2_total, $amount, $this->item->tax_id);
} }
$item_tax_rate3_total = $this->calcInclusiveLineTax($this->item->tax_rate3, $amount); $item_tax_rate3_total = $this->calcInclusiveLineTax($this->item->tax_rate3, $amount);
@ -392,7 +392,7 @@ class InvoiceItemSumInclusive
$item_tax += $item_tax_rate3_total; $item_tax += $item_tax_rate3_total;
if ($item_tax_rate3_total != 0) { if ($item_tax_rate3_total != 0) {
$this->groupTax($this->item->tax_name3, $this->item->tax_rate3, $item_tax_rate3_total); $this->groupTax($this->item->tax_name3, $this->item->tax_rate3, $item_tax_rate3_total, $amount, $this->item->tax_id);
} }
$this->setTotalTaxes($this->getTotalTaxes() + $item_tax); $this->setTotalTaxes($this->getTotalTaxes() + $item_tax);

View File

@ -349,11 +349,25 @@ class InvoiceSum
return $value['key'] == $key; return $value['key'] == $key;
})->pluck('tax_name')->first(); })->pluck('tax_name')->first();
$tax_rate = $values->filter(function ($value, $k) use ($key) {
return $value['key'] == $key;
})->pluck('tax_rate')->first();
$tax_id = $values->filter(function ($value, $k) use ($key) {
return $value['key'] == $key;
})->pluck('tax_id')->first();
$total_line_tax = $values->filter(function ($value, $k) use ($key) { $total_line_tax = $values->filter(function ($value, $k) use ($key) {
return $value['key'] == $key; return $value['key'] == $key;
})->sum('total'); })->sum('total');
$this->tax_map[] = ['name' => $tax_name, 'total' => $total_line_tax]; $base_amount = $values->filter(function ($value, $k) use ($key) {
return $value['key'] == $key;
})->sum('base_amount');
$tax_id = $values->first()['tax_id'] ?? '';
$this->tax_map[] = ['name' => $tax_name, 'total' => $total_line_tax, 'tax_id' => $tax_id, 'tax_rate' => $tax_rate, 'base_amount' => $base_amount];
$this->total_taxes += $total_line_tax; $this->total_taxes += $total_line_tax;
} }

View File

@ -368,19 +368,33 @@ class InvoiceSumInclusive
$values = $this->invoice_items->getGroupedTaxes(); $values = $this->invoice_items->getGroupedTaxes();
foreach ($keys as $key) { foreach ($keys as $key) {
$tax_name = $values->filter(function ($value, $k) use ($key) { $tax_name = $values->filter(function ($value, $k) use ($key) {
return $value['key'] == $key; return $value['key'] == $key;
})->pluck('tax_name')->first(); })->pluck('tax_name')->first();
$tax_rate = $values->filter(function ($value, $k) use ($key) {
return $value['key'] == $key;
})->pluck('tax_rate')->first();
$tax_id = $values->filter(function ($value, $k) use ($key) {
return $value['key'] == $key;
})->pluck('tax_id')->first();
$total_line_tax = $values->filter(function ($value, $k) use ($key) { $total_line_tax = $values->filter(function ($value, $k) use ($key) {
return $value['key'] == $key; return $value['key'] == $key;
})->sum('total'); })->sum('total');
//$total_line_tax -= $this->discount($total_line_tax); $base_amount = $values->filter(function ($value, $k) use ($key) {
return $value['key'] == $key;
})->sum('base_amount');
$this->tax_map[] = ['name' => $tax_name, 'total' => $total_line_tax]; $tax_id = $values->first()['tax_id'] ?? '';
$this->tax_map[] = ['name' => $tax_name, 'total' => $total_line_tax, 'tax_id' => $tax_id, 'tax_rate' => $tax_rate, 'base_amount' => $base_amount];
$this->total_taxes += $total_line_tax; $this->total_taxes += $total_line_tax;
} }
return $this; return $this;

View File

@ -174,10 +174,6 @@ class Peppol extends AbstractService
$this->getJurisdiction(); //Sets the nexus object into the Peppol document. $this->getJurisdiction(); //Sets the nexus object into the Peppol document.
$this->getAllUsedTaxes(); //Maps all used line item taxes $this->getAllUsedTaxes(); //Maps all used line item taxes
nlog("amount: {$this->invoice->amount}");
nlog("taxes: {$this->invoice->total_taxes}");
nlog("exclusive = {$this->invoice->amount} -{$this->invoice->total_taxes}");
/** Invoice Level Props */ /** Invoice Level Props */
$id = new \InvoiceNinja\EInvoice\Models\Peppol\IdentifierType\CustomizationID(); $id = new \InvoiceNinja\EInvoice\Models\Peppol\IdentifierType\CustomizationID();
$id->value = $this->customizationID; $id->value = $this->customizationID;
@ -723,159 +719,6 @@ class Peppol extends AbstractService
} }
/**
* getInvoiceLines
*
* Compiles the invoice line items of the document
*
* @return array
*/
private function getInvoiceLinesx(): array
{
$lines = [];
foreach($this->invoice->line_items as $key => $item) {
$base_price_amount = (string)$this->calculateAdjustedBaseAmount($item);
$_item = new Item();
$_item->Name = $item->product_key;
$_item->Description = $item->notes;
$ctc = new ClassifiedTaxCategory();
$ctc->ID = new ID();
$ctc->ID->value = $this->getTaxType($item->tax_id);
if($item->tax_rate1 > 0)
$ctc->Percent = (string)$item->tax_rate1;
$ts = new TaxScheme();
$id = new ID();
$id->value = $this->standardizeTaxSchemeId($item->tax_name1);
$ts->ID = $id;
$ctc->TaxScheme = $ts;
if(floatval($item->tax_rate1) === 0.0)
{
$ctc = $this->resolveTaxExemptReason($item, $ctc);
if($this->tax_category_id == 'O')
unset($ctc->Percent);
}
$_item->ClassifiedTaxCategory[] = $ctc;
if ($item->tax_rate2 > 0) {
$ctc = new ClassifiedTaxCategory();
$ctc->ID = new ID();
$ctc->ID->value = $this->getTaxType($item->tax_id);
$ctc->Percent = (string)$item->tax_rate2;
$ts = new TaxScheme();
$id = new ID();
$id->value = $this->standardizeTaxSchemeId($item->tax_name2);
$ts->ID = $id;
$ctc->TaxScheme = $ts;
$_item->ClassifiedTaxCategory[] = $ctc;
}
if ($item->tax_rate3 > 0) {
$ctc = new ClassifiedTaxCategory();
$ctc->ID = new ID();
$ctc->ID->value = $this->getTaxType($item->tax_id);
$ctc->Percent = (string)$item->tax_rate3;
$ts = new TaxScheme();
$id = new ID();
$id->value = $this->standardizeTaxSchemeId($item->tax_name3);
$ts->ID = $id;
$ctc->TaxScheme = $ts;
$_item->ClassifiedTaxCategory[] = $ctc;
}
$line = new InvoiceLine();
$id = new ID();
$id->value = (string) ($key+1);
$line->ID = $id;
$iq = new \InvoiceNinja\EInvoice\Models\Peppol\QuantityType\InvoicedQuantity();
$iq->amount = $item->quantity;
$iq->unitCode = $item->unit_code ?? 'C62';
$line->InvoicedQuantity = $iq;
$lea = new LineExtensionAmount();
$lea->currencyID = $this->invoice->client->currency()->code;
$lea->amount = $this->invoice->uses_inclusive_taxes ? $item->line_total - $this->calcInclusiveLineTax($item->tax_rate1, $item->line_total) : $item->line_total;
$line->LineExtensionAmount = $lea;
$line->Item = $_item;
/** Builds the tax map for the document */
$this->getItemTaxes($item);
// 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)$item->cost;
$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)number_format($this->calculateTotalItemDiscountAmount($item),2, '.', '');
$this->allowance_total += $this->calculateTotalItemDiscountAmount($item);
// Add percentage if available
if ($item->discount > 0 && !$item->is_amount_discount) {
$allowanceCharge->BaseAmount = new \InvoiceNinja\EInvoice\Models\Peppol\AmountType\BaseAmount();
$allowanceCharge->BaseAmount->currencyID = $this->invoice->client->currency()->code;
$allowanceCharge->BaseAmount->amount = (string)round(($item->cost * $item->quantity),2);
$mfn = new \InvoiceNinja\EInvoice\Models\Peppol\NumericType\MultiplierFactorNumeric();
$mfn->value = (string) round($item->discount,2);
$allowanceCharge->MultiplierFactorNumeric = $mfn; // Convert percentage to decimal
}
// }
// Required reason
$allowanceCharge->AllowanceChargeReason = ctrans('texts.discount');
$line->Price = $basePrice;
$line->AllowanceCharge[] = $allowanceCharge;
} else {
// No discount case
$price = new Price();
$pa = new PriceAmount();
$pa->currencyID = $this->invoice->client->currency()->code;
$pa->amount = (string)$item->cost;
$price->PriceAmount = $pa;
$line->Price = $price;
}
$lines[] = $line;
}
return $lines;
}
/** /**
* getInvoiceLines * getInvoiceLines
* *
@ -889,8 +732,6 @@ class Peppol extends AbstractService
foreach($this->invoice->line_items as $key => $item) { foreach($this->invoice->line_items as $key => $item) {
$base_price_amount = (string)$this->calculateAdjustedBaseAmount($item);
$_item = new Item(); $_item = new Item();
$_item->Name = $item->product_key; $_item->Name = $item->product_key;
$_item->Description = $item->notes; $_item->Description = $item->notes;
@ -969,7 +810,7 @@ class Peppol extends AbstractService
$line->Item = $_item; $line->Item = $_item;
/** Builds the tax map for the document */ /** Builds the tax map for the document */
$this->getItemTaxes($item); // $this->getItemTaxes($item);
// Handle Price and Discounts // Handle Price and Discounts
if ($item->discount > 0) { if ($item->discount > 0) {
@ -1154,161 +995,161 @@ class Peppol extends AbstractService
* *
* @param object $item * @param object $item
* @return array * @return array
*/ // */
private function getItemTaxes(object $item): array // private function getItemTaxes(object $item): array
{ // {
$item_taxes = []; // $item_taxes = [];
$adjusted_base_amount = $this->calculateAdjustedBaseAmount($item); // $adjusted_base_amount = $this->calculateAdjustedBaseAmount($item);
// if(strlen($item->tax_name1 ?? '') > 1) { // // if(strlen($item->tax_name1 ?? '') > 1) {
$tax_amount = new TaxAmount(); // $tax_amount = new TaxAmount();
$tax_amount->currencyID = $this->invoice->client->currency()->code; // $tax_amount->currencyID = $this->invoice->client->currency()->code;
$tax_amount->amount = $this->invoice->uses_inclusive_taxes ? $this->calcInclusiveLineTax($item->tax_rate1, $adjusted_base_amount) : $this->calcAmountLineTax($item->tax_rate1, $adjusted_base_amount); // $tax_amount->amount = $this->invoice->uses_inclusive_taxes ? $this->calcInclusiveLineTax($item->tax_rate1, $adjusted_base_amount) : $this->calcAmountLineTax($item->tax_rate1, $adjusted_base_amount);
$tax_subtotal = new TaxSubtotal(); // $tax_subtotal = new TaxSubtotal();
$tax_subtotal->TaxAmount = $tax_amount; // $tax_subtotal->TaxAmount = $tax_amount;
$taxable_amount = new TaxableAmount(); // $taxable_amount = new TaxableAmount();
$taxable_amount->currencyID = $this->invoice->client->currency()->code; // $taxable_amount->currencyID = $this->invoice->client->currency()->code;
$taxable_amount->amount = $this->invoice->uses_inclusive_taxes ? $adjusted_base_amount - $tax_amount->amount : $adjusted_base_amount; // $taxable_amount->amount = $this->invoice->uses_inclusive_taxes ? $adjusted_base_amount - $tax_amount->amount : $adjusted_base_amount;
$tax_subtotal->TaxableAmount = $taxable_amount; // $tax_subtotal->TaxableAmount = $taxable_amount;
$tc = new TaxCategory(); // $tc = new TaxCategory();
$id = new ID(); // $id = new ID();
$id->value = $this->getTaxType($item->tax_id); // $id->value = $this->getTaxType($item->tax_id);
if(floatval($item->tax_rate1) === 0.0) // if(floatval($item->tax_rate1) === 0.0)
$id->value = $this->resolveTaxExemptReason($item); // $id->value = $this->resolveTaxExemptReason($item);
$tc->ID = $id; // $tc->ID = $id;
$tc->Percent = (string)$item->tax_rate1; // $tc->Percent = (string)$item->tax_rate1;
$ts = new TaxScheme(); // $ts = new TaxScheme();
$id = new ID(); // $id = new ID();
$id->value = $this->standardizeTaxSchemeId($item->tax_name1); // $id->value = $this->standardizeTaxSchemeId($item->tax_name1);
$jurisdiction = $this->getJurisdiction(); // $jurisdiction = $this->getJurisdiction();
$ts->JurisdictionRegionAddress[] = $jurisdiction; // $ts->JurisdictionRegionAddress[] = $jurisdiction;
$ts->ID = $id; // $ts->ID = $id;
$tc->TaxScheme = $ts; // $tc->TaxScheme = $ts;
$tax_subtotal->TaxCategory = $tc; // $tax_subtotal->TaxCategory = $tc;
$tax_total = new TaxTotal(); // $tax_total = new TaxTotal();
$tax_total->TaxAmount = $tax_amount; // $tax_total->TaxAmount = $tax_amount;
$tax_total->TaxSubtotal[] = $tax_subtotal; // $tax_total->TaxSubtotal[] = $tax_subtotal;
$this->tax_map[] = [ // $this->tax_map[] = [
'taxableAmount' => $taxable_amount->amount, // 'taxableAmount' => $taxable_amount->amount,
'taxAmount' => $tax_amount->amount, // 'taxAmount' => $tax_amount->amount,
'percentage' => $item->tax_rate1, // 'percentage' => $item->tax_rate1,
]; // ];
$item_taxes[] = $tax_total; // $item_taxes[] = $tax_total;
// } // // }
if(strlen($item->tax_name2 ?? '') > 1) { // if(strlen($item->tax_name2 ?? '') > 1) {
$tax_amount = new TaxAmount(); // $tax_amount = new TaxAmount();
$tax_amount->currencyID = $this->invoice->client->currency()->code; // $tax_amount->currencyID = $this->invoice->client->currency()->code;
$tax_amount->amount = $this->invoice->uses_inclusive_taxes ? $this->calcInclusiveLineTax($item->tax_rate2, $item->line_total) : $this->calcAmountLineTax($item->tax_rate2, $item->line_total); // $tax_amount->amount = $this->invoice->uses_inclusive_taxes ? $this->calcInclusiveLineTax($item->tax_rate2, $item->line_total) : $this->calcAmountLineTax($item->tax_rate2, $item->line_total);
$tax_subtotal = new TaxSubtotal(); // $tax_subtotal = new TaxSubtotal();
$tax_subtotal->TaxAmount = $tax_amount; // $tax_subtotal->TaxAmount = $tax_amount;
$taxable_amount = new TaxableAmount(); // $taxable_amount = new TaxableAmount();
$taxable_amount->currencyID = $this->invoice->client->currency()->code; // $taxable_amount->currencyID = $this->invoice->client->currency()->code;
$taxable_amount->amount = $item->line_total; // $taxable_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();
$id->value = $this->getTaxType($item->tax_id); // $id->value = $this->getTaxType($item->tax_id);
$tc->ID = $id; // $tc->ID = $id;
$tc->Percent = (string)$item->tax_rate2; // $tc->Percent = (string)$item->tax_rate2;
$ts = new TaxScheme(); // $ts = new TaxScheme();
$id = new ID(); // $id = new ID();
$id->value = $this->standardizeTaxSchemeId($item->tax_name2); // $id->value = $this->standardizeTaxSchemeId($item->tax_name2);
$jurisdiction = $this->getJurisdiction(); // $jurisdiction = $this->getJurisdiction();
$ts->JurisdictionRegionAddress[] = $jurisdiction; // $ts->JurisdictionRegionAddress[] = $jurisdiction;
$ts->ID = $id; // $ts->ID = $id;
$tc->TaxScheme = $ts; // $tc->TaxScheme = $ts;
$tax_subtotal->TaxCategory = $tc; // $tax_subtotal->TaxCategory = $tc;
$tax_total = new TaxTotal(); // $tax_total = new TaxTotal();
$tax_total->TaxAmount = $tax_amount; // $tax_total->TaxAmount = $tax_amount;
$tax_total->TaxSubtotal[] = $tax_subtotal; // $tax_total->TaxSubtotal[] = $tax_subtotal;
$this->tax_map[] = [ // $this->tax_map[] = [
'taxableAmount' => $taxable_amount->amount, // 'taxableAmount' => $taxable_amount->amount,
'taxAmount' => $tax_amount->amount, // 'taxAmount' => $tax_amount->amount,
'percentage' => $item->tax_rate2, // 'percentage' => $item->tax_rate2,
]; // ];
$item_taxes[] = $tax_total; // $item_taxes[] = $tax_total;
} // }
if(strlen($item->tax_name3 ?? '') > 1) { // if(strlen($item->tax_name3 ?? '') > 1) {
$tax_amount = new TaxAmount(); // $tax_amount = new TaxAmount();
$tax_amount->currencyID = $this->invoice->client->currency()->code; // $tax_amount->currencyID = $this->invoice->client->currency()->code;
$tax_amount->amount = $this->invoice->uses_inclusive_taxes ? $this->calcInclusiveLineTax($item->tax_rate3, $item->line_total) : $this->calcAmountLineTax($item->tax_rate3, $item->line_total); // $tax_amount->amount = $this->invoice->uses_inclusive_taxes ? $this->calcInclusiveLineTax($item->tax_rate3, $item->line_total) : $this->calcAmountLineTax($item->tax_rate3, $item->line_total);
$tax_subtotal = new TaxSubtotal(); // $tax_subtotal = new TaxSubtotal();
$tax_subtotal->TaxAmount = $tax_amount; // $tax_subtotal->TaxAmount = $tax_amount;
$taxable_amount = new TaxableAmount(); // $taxable_amount = new TaxableAmount();
$taxable_amount->currencyID = $this->invoice->client->currency()->code; // $taxable_amount->currencyID = $this->invoice->client->currency()->code;
$taxable_amount->amount = $item->line_total; // $taxable_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();
$id->value = $this->getTaxType($item->tax_id); // $id->value = $this->getTaxType($item->tax_id);
$tc->ID = $id; // $tc->ID = $id;
$tc->Percent = (string)$item->tax_rate3; // $tc->Percent = (string)$item->tax_rate3;
$ts = new TaxScheme(); // $ts = new TaxScheme();
$id = new ID(); // $id = new ID();
$id->value = $this->standardizeTaxSchemeId($item->tax_name3); // $id->value = $this->standardizeTaxSchemeId($item->tax_name3);
$jurisdiction = $this->getJurisdiction(); // $jurisdiction = $this->getJurisdiction();
$ts->JurisdictionRegionAddress[] = $jurisdiction; // $ts->JurisdictionRegionAddress[] = $jurisdiction;
$ts->ID = $id; // $ts->ID = $id;
$tc->TaxScheme = $ts; // $tc->TaxScheme = $ts;
$tax_subtotal->TaxCategory = $tc; // $tax_subtotal->TaxCategory = $tc;
$tax_total = new TaxTotal(); // $tax_total = new TaxTotal();
$tax_total->TaxAmount = $tax_amount; // $tax_total->TaxAmount = $tax_amount;
$tax_total->TaxSubtotal[] = $tax_subtotal; // $tax_total->TaxSubtotal[] = $tax_subtotal;
$this->tax_map[] = [ // $this->tax_map[] = [
'taxableAmount' => $taxable_amount->amount, // 'taxableAmount' => $taxable_amount->amount,
'taxAmount' => $tax_amount->amount, // 'taxAmount' => $tax_amount->amount,
'percentage' => $item->tax_rate3, // 'percentage' => $item->tax_rate3,
]; // ];
$item_taxes[] = $tax_total; // $item_taxes[] = $tax_total;
} // }
return $item_taxes; // return $item_taxes;
} // }
/** /**
* getAccountingSupplierParty * getAccountingSupplierParty
@ -1627,49 +1468,29 @@ class Peppol extends AbstractService
{ {
$tax_total = new TaxTotal(); $tax_total = new TaxTotal();
$taxes = $this->calc->getTaxMap();
$taxes = collect($this->tax_map) if(count($taxes) < 1)
->groupBy('percentage')
->map(function ($group) {
return [
'taxableAmount' => $group->sum('taxableAmount'),
'taxAmount' => $group->sum('taxAmount'),
'percentage' => $group->first()['percentage'],
];
});
foreach($taxes as $grouped_tax)
{ {
// Required: TaxAmount (BT-110)
$tax_amount = new TaxAmount(); $tax_amount = new TaxAmount();
$tax_amount->currencyID = $this->invoice->client->currency()->code; $tax_amount->currencyID = $this->invoice->client->currency()->code;
$tax_amount->amount = (string)$grouped_tax['taxAmount']; $tax_amount->amount = (string)0;
$tax_total->TaxAmount = $tax_amount; $tax_total->TaxAmount = $tax_amount;
// Required: TaxSubtotal (BG-23)
$tax_subtotal = new TaxSubtotal(); $tax_subtotal = new TaxSubtotal();
// Required: TaxableAmount (BT-116) // Required: TaxableAmount (BT-116)
$taxable_amount = new TaxableAmount(); $taxable_amount = new TaxableAmount();
$taxable_amount->currencyID = $this->invoice->client->currency()->code; $taxable_amount->currencyID = $this->invoice->client->currency()->code;
$taxable_amount->amount = (string)round($this->invoice->amount - $this->invoice->total_taxes, 2);
if (floatval($grouped_tax['taxAmount']) === 0.0) {
$taxable_amount->amount = (string)round($this->invoice->amount - $this->invoice->total_taxes, 2);
}
else
$taxable_amount->amount = (string)$grouped_tax['taxableAmount'];
$tax_subtotal->TaxableAmount = $taxable_amount; $tax_subtotal->TaxableAmount = $taxable_amount;
// Required: TaxAmount (BT-117)
$subtotal_tax_amount = new TaxAmount(); $subtotal_tax_amount = new TaxAmount();
$subtotal_tax_amount->currencyID = $this->invoice->client->currency()->code; $subtotal_tax_amount->currencyID = $this->invoice->client->currency()->code;
$subtotal_tax_amount->amount = (string)$grouped_tax['taxAmount']; $subtotal_tax_amount->amount = (string)0;
$tax_subtotal->TaxAmount = $subtotal_tax_amount; $tax_subtotal->TaxAmount = $subtotal_tax_amount;
@ -1678,17 +1499,14 @@ class Peppol extends AbstractService
// Required: TaxCategory ID (BT-118) // Required: TaxCategory ID (BT-118)
$category_id = new ID(); $category_id = new ID();
$category_id->value = 'S'; // Standard rate $category_id->value = $this->tax_category_id; // Exempt
if(floatval($grouped_tax['taxAmount']) === 0.0){
$category_id->value = $this->tax_category_id; // Exempt
}
$tax_category->ID = $category_id; $tax_category->ID = $category_id;
// Required: TaxCategory Rate (BT-119) // Required: TaxCategory Rate (BT-119)
if($grouped_tax['percentage'] > 0) // if ($grouped_tax['tax_rate'] > 0) {
$tax_category->Percent = (string)$grouped_tax['percentage']; // $tax_category->Percent = (string)$grouped_tax['tax_rate'];
// }
// Required: TaxScheme (BG-23) // Required: TaxScheme (BG-23)
$tax_scheme = new TaxScheme(); $tax_scheme = new TaxScheme();
@ -1703,6 +1521,63 @@ class Peppol extends AbstractService
$tax_total->TaxSubtotal[] = $tax_subtotal; $tax_total->TaxSubtotal[] = $tax_subtotal;
$this->p_invoice->TaxTotal[] = $tax_total; $this->p_invoice->TaxTotal[] = $tax_total;
return $this;
}
foreach($taxes as $grouped_tax)
{
// Required: TaxAmount (BT-110)
$tax_amount = new TaxAmount();
$tax_amount->currencyID = $this->invoice->client->currency()->code;
$tax_amount->amount = (string)$grouped_tax['total'];
$tax_total->TaxAmount = $tax_amount;
// Required: TaxSubtotal (BG-23)
$tax_subtotal = new TaxSubtotal();
// Required: TaxableAmount (BT-116)
$taxable_amount = new TaxableAmount();
$taxable_amount->currencyID = $this->invoice->client->currency()->code;
$taxable_amount->amount = (string)round($grouped_tax['base_amount'],2);
$tax_subtotal->TaxableAmount = $taxable_amount;
// Required: TaxAmount (BT-117)
$subtotal_tax_amount = new TaxAmount();
$subtotal_tax_amount->currencyID = $this->invoice->client->currency()->code;
$subtotal_tax_amount->amount = (string)round($grouped_tax['total'],2);
$tax_subtotal->TaxAmount = $subtotal_tax_amount;
// Required: TaxCategory (BG-23)
$tax_category = new TaxCategory();
// Required: TaxCategory ID (BT-118)
$category_id = new ID();
$category_id->value = $this->getTaxType($grouped_tax['tax_id']); // Standard rate
$tax_category->ID = $category_id;
// Required: TaxCategory Rate (BT-119)
if($grouped_tax['tax_rate'] > 0)
$tax_category->Percent = (string)$grouped_tax['tax_rate'];
// Required: TaxScheme (BG-23)
$tax_scheme = new TaxScheme();
$scheme_id = new ID();
$scheme_id->value = $this->standardizeTaxSchemeId("taxname");
$tax_scheme->ID = $scheme_id;
$tax_category->TaxScheme = $tax_scheme;
$tax_subtotal->TaxCategory = $this->globalTaxCategories[0];
$tax_total->TaxSubtotal[] = $tax_subtotal;
$this->p_invoice->TaxTotal[] = $tax_total;
} }
return $this; return $this;

View File

@ -55,7 +55,7 @@ return [
| |
*/ */
'after_commit' => false, 'after_commit' => true,
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------

View File

@ -269,7 +269,7 @@ class PeppolTest extends TestCase
} }
for($x=0; $x<100; $x++){ for($x=0; $x<1000; $x++){
$scenario = $scenarios[0]; $scenario = $scenarios[0];
@ -292,6 +292,8 @@ class PeppolTest extends TestCase
$validator->validate(); $validator->validate();
if (count($validator->getErrors()) > 0) { if (count($validator->getErrors()) > 0) {
nlog("index {$x}");
nlog($invoice->withoutRelations()->toArray());
nlog($p->toXml()); nlog($p->toXml());
nlog($validator->getErrors()); nlog($validator->getErrors());
} }