Fixes for Total Discount on invoice - zugferd
This commit is contained in:
parent
1ef08dd27e
commit
e000fa1275
|
|
@ -28,6 +28,7 @@ use App\Services\EDocument\Standards\FatturaPA;
|
||||||
use App\Services\EDocument\Standards\RoEInvoice;
|
use App\Services\EDocument\Standards\RoEInvoice;
|
||||||
use App\Services\EDocument\Standards\OrderXDocument;
|
use App\Services\EDocument\Standards\OrderXDocument;
|
||||||
use App\Services\EDocument\Standards\FacturaEInvoice;
|
use App\Services\EDocument\Standards\FacturaEInvoice;
|
||||||
|
use App\Services\EDocument\Standards\ZugferdEDocument;
|
||||||
use App\Services\EDocument\Standards\ZugferdEDokument;
|
use App\Services\EDocument\Standards\ZugferdEDokument;
|
||||||
|
|
||||||
class CreateEDocument implements ShouldQueue
|
class CreateEDocument implements ShouldQueue
|
||||||
|
|
@ -85,7 +86,10 @@ class CreateEDocument implements ShouldQueue
|
||||||
case "XInvoice-Extended":
|
case "XInvoice-Extended":
|
||||||
case "XInvoice-BasicWL":
|
case "XInvoice-BasicWL":
|
||||||
case "XInvoice-Basic":
|
case "XInvoice-Basic":
|
||||||
$zugferd = (new ZugferdEDokument($this->document))->run();
|
|
||||||
|
// $zugferd = (new ZugferdEDokument($this->document))->run();
|
||||||
|
|
||||||
|
$zugferd = (new ZugferdEDocument($this->document))->run();
|
||||||
|
|
||||||
return $this->returnObject ? $zugferd->xdocument : $zugferd->getXml();
|
return $this->returnObject ? $zugferd->xdocument : $zugferd->getXml();
|
||||||
case "Facturae_3.2":
|
case "Facturae_3.2":
|
||||||
|
|
|
||||||
|
|
@ -110,47 +110,40 @@ class ZugferdEDocument extends AbstractService
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get document level discount
|
// Get document level discount
|
||||||
$document_discount = $this->calc->getTotalDiscount();
|
// $document_discount = $this->calc->getTotalDiscount();
|
||||||
$total_taxable = $this->getTaxable();
|
// $total_taxable = $this->getTaxable();
|
||||||
|
$net_subtotal = $this->calc->getNetSubTotal();
|
||||||
|
|
||||||
// Process each tax rate group
|
// Process each tax rate group
|
||||||
foreach ($this->calc->getTaxMap() as $item) {
|
foreach ($this->calc->getTaxMap() as $item) {
|
||||||
$tax_type = $this->getTaxType($item["tax_id"]);
|
$tax_type = $this->getTaxType($item["tax_id"]);
|
||||||
|
|
||||||
// Calculate proportional discount for this tax group
|
|
||||||
$tax_group_total = $item["base_amount"];
|
|
||||||
$proportional_discount = 0;
|
|
||||||
|
|
||||||
if ($document_discount > 0 && $total_taxable > 0) {
|
|
||||||
$proportional_discount = round(($tax_group_total / $total_taxable) * $document_discount, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adjusted taxable amount for this tax group
|
|
||||||
$adjusted_taxable = $tax_group_total - $proportional_discount;
|
|
||||||
|
|
||||||
// Add document level allowance (discount) if present
|
|
||||||
if ($proportional_discount > 0) {
|
|
||||||
$this->xdocument->addDocumentAllowanceCharge(
|
|
||||||
$proportional_discount,
|
|
||||||
false, // isCharge = false means it's a discount
|
|
||||||
$tax_type,
|
|
||||||
"VAT",
|
|
||||||
$item["tax_rate"]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add tax information
|
// Add tax information
|
||||||
$this->xdocument->addDocumentTax(
|
$this->xdocument->addDocumentTax(
|
||||||
$tax_type,
|
$tax_type,
|
||||||
"VAT",
|
"VAT",
|
||||||
$adjusted_taxable, // Taxable amount after discount
|
$item["base_amount"], // Taxable amount after discount
|
||||||
// ($item["tax_rate"] / 100) * $adjusted_taxable, // Tax amount
|
|
||||||
$item["total"],
|
$item["total"],
|
||||||
$item["tax_rate"],
|
$item["tax_rate"],
|
||||||
$tax_type == ZugferdDutyTaxFeeCategories::VAT_EXEMPT_FOR_EEA_INTRACOMMUNITY_SUPPLY_OF_GOODS_AND_SERVICES
|
$tax_type == ZugferdDutyTaxFeeCategories::VAT_EXEMPT_FOR_EEA_INTRACOMMUNITY_SUPPLY_OF_GOODS_AND_SERVICES
|
||||||
? ctrans('texts.intracommunity_tax_info')
|
? ctrans('texts.intracommunity_tax_info')
|
||||||
: ''
|
: ''
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if ($this->calc->getTotalDiscount() > 0) {
|
||||||
|
|
||||||
|
$ratio = $item["base_amount"] / $net_subtotal;
|
||||||
|
|
||||||
|
$this->xdocument->addDocumentAllowanceCharge(
|
||||||
|
round($this->calc->getTotalDiscount() * $ratio, 2),
|
||||||
|
false,
|
||||||
|
$tax_type,
|
||||||
|
"VAT",
|
||||||
|
$item["tax_rate"]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
|
|
@ -197,10 +190,22 @@ class ZugferdEDocument extends AbstractService
|
||||||
// Calculate amounts after discount
|
// Calculate amounts after discount
|
||||||
$taxable_amount = $this->getTaxable();
|
$taxable_amount = $this->getTaxable();
|
||||||
|
|
||||||
|
nlog([
|
||||||
|
$this->document->amount, // Total amount with VAT
|
||||||
|
$this->document->balance, // Amount due
|
||||||
|
$this->calc->getSubTotal(), // Sum before tax
|
||||||
|
$this->calc->getTotalSurcharges(), // Total charges
|
||||||
|
$document_discount, // Total allowances
|
||||||
|
$taxable_amount, // Tax basis total (net)
|
||||||
|
$total_tax, // Total tax amount
|
||||||
|
0.0, // Total prepaid amount
|
||||||
|
$this->document->amount - $this->document->balance,
|
||||||
|
]);
|
||||||
|
|
||||||
$this->xdocument->setDocumentSummation(
|
$this->xdocument->setDocumentSummation(
|
||||||
$this->document->amount, // Total amount with VAT
|
$this->document->amount, // Total amount with VAT
|
||||||
$this->document->balance, // Amount due
|
$this->document->balance, // Amount due
|
||||||
$subtotal, // Sum before tax
|
$this->calc->getSubTotal(), // Sum before tax
|
||||||
$this->calc->getTotalSurcharges(), // Total charges
|
$this->calc->getTotalSurcharges(), // Total charges
|
||||||
$document_discount, // Total allowances
|
$document_discount, // Total allowances
|
||||||
$taxable_amount, // Tax basis total (net)
|
$taxable_amount, // Tax basis total (net)
|
||||||
|
|
@ -211,66 +216,67 @@ class ZugferdEDocument extends AbstractService
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
private function setLineItems(): self
|
|
||||||
{
|
|
||||||
foreach ($this->document->line_items as $index => $item) {
|
|
||||||
/** @var InvoiceItem $item **/
|
|
||||||
|
|
||||||
// 1. Start new position and set basic details
|
private function setLineItems(): self
|
||||||
$this->xdocument->addNewPosition($index)
|
{
|
||||||
->setDocumentPositionProductDetails(
|
foreach ($this->document->line_items as $index => $item) {
|
||||||
strlen($item->product_key ?? '') >= 1 ? $item->product_key : "no product name defined",
|
/** @var InvoiceItem $item **/
|
||||||
$item->notes
|
|
||||||
)
|
|
||||||
->setDocumentPositionQuantity(
|
|
||||||
$item->quantity,
|
|
||||||
$item->type_id == 2 ? "HUR" : "H87"
|
|
||||||
)
|
|
||||||
->setDocumentPositionNetPrice(
|
|
||||||
$this->document->uses_inclusive_tax ? $item->net_cost : $item->cost
|
|
||||||
);
|
|
||||||
|
|
||||||
// 2. ALWAYS add tax information (even if zero)
|
// 1. Start new position and set basic details
|
||||||
if(strlen($item->tax_name1) > 1) {
|
$this->xdocument->addNewPosition($index)
|
||||||
$this->xdocument->addDocumentPositionTax(
|
->setDocumentPositionProductDetails(
|
||||||
$this->getTaxType($item->tax_id ?? '2'),
|
strlen($item->product_key ?? '') >= 1 ? $item->product_key : "no product name defined",
|
||||||
'VAT',
|
$item->notes
|
||||||
$item->tax_rate1
|
)
|
||||||
);
|
->setDocumentPositionQuantity(
|
||||||
} else {
|
$item->quantity,
|
||||||
// Add zero tax if no tax is specified
|
$item->type_id == 2 ? "HUR" : "H87"
|
||||||
$this->xdocument->addDocumentPositionTax(
|
)
|
||||||
ZugferdDutyTaxFeeCategories::EXEMPT_FROM_TAX,
|
->setDocumentPositionNetPrice(
|
||||||
'VAT',
|
$this->document->uses_inclusive_taxes ? $item->net_cost : $item->cost
|
||||||
0
|
);
|
||||||
);
|
|
||||||
|
// 2. ALWAYS add tax information (even if zero)
|
||||||
|
if(strlen($item->tax_name1) > 1) {
|
||||||
|
$this->xdocument->addDocumentPositionTax(
|
||||||
|
$this->getTaxType($item->tax_id ?? '2'),
|
||||||
|
'VAT',
|
||||||
|
$item->tax_rate1
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Add zero tax if no tax is specified
|
||||||
|
$this->xdocument->addDocumentPositionTax(
|
||||||
|
ZugferdDutyTaxFeeCategories::EXEMPT_FROM_TAX,
|
||||||
|
'VAT',
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Add allowances/charges (discounts) if any
|
||||||
|
if($item->discount > 0) {
|
||||||
|
$line_discount = $this->calculateTotalItemDiscountAmount($item);
|
||||||
|
$this->xdocument->addDocumentPositionGrossPriceAllowanceCharge(
|
||||||
|
abs($line_discount),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Finally add monetary summation
|
||||||
|
$this->xdocument->setDocumentPositionLineSummation($item->line_total);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Add allowances/charges (discounts) if any
|
return $this;
|
||||||
if($item->discount > 0) {
|
}
|
||||||
$line_discount = $this->calculateTotalItemDiscountAmount($item);
|
|
||||||
$this->xdocument->addDocumentPositionGrossPriceAllowanceCharge(
|
private function calculateTotalItemDiscountAmount($item): float
|
||||||
abs($line_discount),
|
{
|
||||||
false
|
if ($item->is_amount_discount) {
|
||||||
);
|
return $item->discount;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Finally add monetary summation
|
return ($item->cost * $item->quantity) * ($item->discount / 100);
|
||||||
$this->xdocument->setDocumentPositionLineSummation($item->line_total);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function calculateTotalItemDiscountAmount($item): float
|
|
||||||
{
|
|
||||||
if ($item->is_amount_discount) {
|
|
||||||
return $item->discount;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ($item->cost * $item->quantity) * ($item->discount / 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private function setClientTaxRegistration(): self
|
private function setClientTaxRegistration(): self
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue