mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-10 05:02:36 +01:00
Add prop resolver
This commit is contained in:
parent
acf73fb6fd
commit
a72f26b242
@ -17,6 +17,7 @@ use App\Services\AbstractService;
|
||||
use App\Helpers\Invoice\InvoiceSum;
|
||||
use InvoiceNinja\EInvoice\EInvoice;
|
||||
use App\Helpers\Invoice\InvoiceSumInclusive;
|
||||
use App\Helpers\Invoice\Taxer;
|
||||
use InvoiceNinja\EInvoice\Models\Peppol\PaymentMeans;
|
||||
use InvoiceNinja\EInvoice\Models\Peppol\ItemType\Item;
|
||||
use InvoiceNinja\EInvoice\Models\Peppol\PartyType\Party;
|
||||
@ -27,6 +28,7 @@ use InvoiceNinja\EInvoice\Models\Peppol\CountryType\Country;
|
||||
use InvoiceNinja\EInvoice\Models\Peppol\AmountType\TaxAmount;
|
||||
use InvoiceNinja\EInvoice\Models\Peppol\TaxTotalType\TaxTotal;
|
||||
use App\Services\EDocument\Standards\Settings\PropertyResolver;
|
||||
use App\Utils\Traits\NumberFormatter;
|
||||
use InvoiceNinja\EInvoice\Models\Peppol\AmountType\PriceAmount;
|
||||
use InvoiceNinja\EInvoice\Models\Peppol\PartyNameType\PartyName;
|
||||
use InvoiceNinja\EInvoice\Models\Peppol\TaxSchemeType\TaxScheme;
|
||||
@ -48,6 +50,9 @@ use InvoiceNinja\EInvoice\Models\Peppol\FinancialAccountType\PayeeFinancialAccou
|
||||
|
||||
class Peppol extends AbstractService
|
||||
{
|
||||
use Taxer;
|
||||
use NumberFormatter;
|
||||
|
||||
private array $InvoiceTypeCodes = [
|
||||
"380" => "Commercial invoice",
|
||||
"381" => "Credit note",
|
||||
@ -137,7 +142,7 @@ class Peppol extends AbstractService
|
||||
|
||||
$tea = new TaxExclusiveAmount();
|
||||
$tea->currencyID = $this->invoice->client->currency()->code;
|
||||
$tea->amount = $taxable;
|
||||
$tea->amount = $this->invoice->uses_inclusive_taxes ? round($this->invoice->amount - $this->invoice->total_taxes,2) : $taxable;
|
||||
$lmt->TaxExclusiveAmount = $tea;
|
||||
|
||||
$tia = new TaxInclusiveAmount();
|
||||
@ -163,19 +168,16 @@ class Peppol extends AbstractService
|
||||
|
||||
$tax_amount = new TaxAmount();
|
||||
$tax_amount->currencyID = $this->invoice->client->currency()->code;
|
||||
$tax_amount->amount = round($this->invoice->amount * (1 / $this->invoice->tax_rate1), 2);
|
||||
$tax_amount->amount = $this->invoice->uses_inclusive_taxes ? $this->calcInclusiveLineTax($this->invoice->tax_rate1, $this->invoice->amount) : $this->calcAmountLineTax($this->invoice->tax_rate1, $this->invoice->amount);
|
||||
|
||||
$tax_subtotal = new TaxSubtotal();
|
||||
$tax_subtotal->TaxAmount = $tax_amount;
|
||||
|
||||
|
||||
$taxable_amount = new TaxableAmount();
|
||||
$taxable_amount->currencyID = $this->invoice->client->currency()->code;
|
||||
$taxable_amount->amount = $this->invoice->amount;
|
||||
$tax_subtotal->TaxableAmount = $taxable_amount;
|
||||
|
||||
|
||||
|
||||
$tc = new TaxCategory();
|
||||
$tc->ID = $type_id == '2' ? 'HUR' : 'C62';
|
||||
$tc->Percent = $this->invoice->tax_rate1;
|
||||
@ -196,7 +198,8 @@ class Peppol extends AbstractService
|
||||
|
||||
$tax_amount = new TaxAmount();
|
||||
$tax_amount->currencyID = $this->invoice->client->currency()->code;
|
||||
$tax_amount->amount = round($this->invoice->amount * (1 / $this->invoice->tax_rate2), 2);
|
||||
|
||||
$tax_amount->amount = $this->invoice->uses_inclusive_taxes ? $this->calcInclusiveLineTax($this->invoice->tax_rate2, $this->invoice->amount) : $this->calcAmountLineTax($this->invoice->tax_rate2, $this->invoice->amount);
|
||||
|
||||
$tax_subtotal = new TaxSubtotal();
|
||||
$tax_subtotal->TaxAmount = $tax_amount;
|
||||
@ -228,7 +231,7 @@ class Peppol extends AbstractService
|
||||
|
||||
$tax_amount = new TaxAmount();
|
||||
$tax_amount->currencyID = $this->invoice->client->currency()->code;
|
||||
$tax_amount->amount = round($this->invoice->amount * (1 / $this->invoice->tax_rate1), 2);
|
||||
$tax_amount->amount = $this->invoice->uses_inclusive_taxes ? $this->calcInclusiveLineTax($this->invoice->tax_rate3, $this->invoice->amount) : $this->calcAmountLineTax($this->invoice->tax_rate3, $this->invoice->amount);
|
||||
|
||||
$tax_subtotal = new TaxSubtotal();
|
||||
$tax_subtotal->TaxAmount = $tax_amount;
|
||||
@ -327,7 +330,7 @@ class Peppol extends AbstractService
|
||||
|
||||
$tax_amount = new TaxAmount();
|
||||
$tax_amount->currencyID = $this->invoice->client->currency()->code;
|
||||
$tax_amount->amount = round(($item->line_total * ($item->tax_rate1/100)), 2);
|
||||
$tax_amount->amount = $this->invoice->uses_inclusive_taxes ? $this->calcInclusiveLineTax($item->tax_rate1, $item->line_total) : $this->calcAmountLineTax($item->tax_rate1, $item->line_total);
|
||||
$tax_subtotal = new TaxSubtotal();
|
||||
$tax_subtotal->TaxAmount = $tax_amount;
|
||||
|
||||
@ -356,7 +359,7 @@ class Peppol extends AbstractService
|
||||
|
||||
$tax_amount = new TaxAmount();
|
||||
$tax_amount->currencyID = $this->invoice->client->currency()->code;
|
||||
$tax_amount->amount = round(($item->line_total * ($item->tax_rate2/100)), 2);
|
||||
$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->TaxAmount = $tax_amount;
|
||||
@ -389,7 +392,7 @@ class Peppol extends AbstractService
|
||||
|
||||
$tax_amount = new TaxAmount();
|
||||
$tax_amount->currencyID = $this->invoice->client->currency()->code;
|
||||
$tax_amount->amount = round(($item->line_total * ($item->tax_rate3/100)), 2);
|
||||
$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->TaxAmount = $tax_amount;
|
||||
@ -563,7 +566,6 @@ class Peppol extends AbstractService
|
||||
$this->p_invoice->{$prop} = $prop_value;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
return $this;
|
||||
|
@ -0,0 +1,44 @@
|
||||
<?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\Standards\Settings;
|
||||
|
||||
class PropertyResolver
|
||||
{
|
||||
public static function resolve($object, string $propertyPath)
|
||||
{
|
||||
$pathSegments = explode('.', $propertyPath);
|
||||
|
||||
return self::traverse($object, $pathSegments);
|
||||
}
|
||||
|
||||
private static function traverse($object, array $pathSegments) {
|
||||
if (empty($pathSegments)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$currentProperty = array_shift($pathSegments);
|
||||
|
||||
if (is_object($object) && isset($object->{$currentProperty})) {
|
||||
$nextObject = $object->{$currentProperty};
|
||||
} elseif (is_array($object) && array_key_exists($currentProperty, $object)) {
|
||||
$nextObject = $object[$currentProperty];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (empty($pathSegments)) {
|
||||
return $nextObject;
|
||||
}
|
||||
|
||||
return self::traverse($nextObject, $pathSegments);
|
||||
}
|
||||
}
|
@ -168,6 +168,119 @@ class PeppolTest extends TestCase
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function testDeInvoiceInclusiveTaxes()
|
||||
{
|
||||
|
||||
$settings = CompanySettings::defaults();
|
||||
$settings->address1 = 'Dudweilerstr. 34b';
|
||||
$settings->city = 'Ost Alessa';
|
||||
$settings->state = 'Bayern';
|
||||
$settings->postal_code = '98060';
|
||||
$settings->vat_number = 'DE923356489';
|
||||
$settings->country_id = '276';
|
||||
$settings->currency_id = '3';
|
||||
|
||||
$einvoice = new \InvoiceNinja\EInvoice\Models\Peppol\Invoice();
|
||||
|
||||
$fib = new FinancialInstitutionBranch();
|
||||
$fib->ID = "DEUTDEMMXXX"; //BIC
|
||||
$fib->Name = 'Deutsche Bank';
|
||||
|
||||
$pfa = new PayeeFinancialAccount();
|
||||
$pfa->ID = 'DE89370400440532013000';
|
||||
$pfa->Name = 'PFA-NAME';
|
||||
$pfa->AliasName = 'PFA-Alias';
|
||||
$pfa->AccountTypeCode = 'CHECKING';
|
||||
$pfa->AccountFormatCode = 'IBAN';
|
||||
$pfa->CurrencyCode = 'EUR';
|
||||
$pfa->FinancialInstitutionBranch = $fib;
|
||||
|
||||
$pm = new PaymentMeans();
|
||||
$pm->PayeeFinancialAccount = $pfa;
|
||||
$einvoice->PaymentMeans[] = $pm;
|
||||
|
||||
$company = Company::factory()->create([
|
||||
'account_id' => $this->account->id,
|
||||
'settings' => $settings,
|
||||
'e_invoice' => $einvoice,
|
||||
]);
|
||||
|
||||
$client_settings = ClientSettings::defaults();
|
||||
$client_settings->currency_id = '3';
|
||||
|
||||
$client = Client::factory()->create([
|
||||
'company_id' => $company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'name' => 'German Client Name',
|
||||
'address1' => 'Kinderhausen 96b',
|
||||
'address2' => 'Apt. 842',
|
||||
'city' => 'Süd Jessestadt',
|
||||
'state' => 'Bayern',
|
||||
'postal_code' => '33323',
|
||||
'country_id' => 276,
|
||||
'routing_id' => 'ABC1234',
|
||||
'settings' => $client_settings,
|
||||
]);
|
||||
|
||||
|
||||
$item = new InvoiceItem();
|
||||
$item->product_key = "Product Key";
|
||||
$item->notes = "Product Description";
|
||||
$item->cost = 10;
|
||||
$item->quantity = 10;
|
||||
$item->tax_rate1 = 19;
|
||||
$item->tax_name1 = 'mwst';
|
||||
|
||||
$invoice = Invoice::factory()->create([
|
||||
'company_id' => $company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'client_id' => $client->id,
|
||||
'discount' => 0,
|
||||
'uses_inclusive_taxes' => true,
|
||||
'status_id' => 1,
|
||||
'tax_rate1' => 0,
|
||||
'tax_name1' => '',
|
||||
'tax_rate2' => 0,
|
||||
'tax_rate3' => 0,
|
||||
'tax_name2' => '',
|
||||
'tax_name3' => '',
|
||||
'line_items' => [$item],
|
||||
'number' => 'DE-'.rand(1000, 100000),
|
||||
'date' => now()->format('Y-m-d')
|
||||
]);
|
||||
|
||||
$invoice = $invoice->calc()->getInvoice();
|
||||
$invoice->service()->markSent()->save();
|
||||
|
||||
$this->assertEquals(100, $invoice->amount);
|
||||
|
||||
$peppol = new Peppol($invoice);
|
||||
$peppol->setInvoiceDefaults();
|
||||
$peppol->run();
|
||||
|
||||
$de_invoice = $peppol->getInvoice();
|
||||
|
||||
$this->assertNotNull($de_invoice);
|
||||
|
||||
$e = new EInvoice();
|
||||
$xml = $e->encode($de_invoice, 'xml');
|
||||
$this->assertNotNull($xml);
|
||||
|
||||
nlog("inclusive");
|
||||
nlog($xml);
|
||||
|
||||
$errors = $e->validate($de_invoice);
|
||||
|
||||
if(count($errors) > 0) {
|
||||
nlog($errors);
|
||||
}
|
||||
|
||||
$this->assertCount(0, $errors);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function testInvoiceBoot()
|
||||
{
|
||||
|
||||
|
@ -217,6 +217,122 @@ class StorecoveTest extends TestCase
|
||||
</cac:InvoiceLine>
|
||||
';
|
||||
|
||||
//inclusive
|
||||
$x = '
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Invoice
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
|
||||
<cbc:ID>DE-53423</cbc:ID>
|
||||
<cbc:IssueDate>2024-07-18</cbc:IssueDate>
|
||||
<cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>
|
||||
<cac:AccountingSupplierParty>
|
||||
<cac:Party>
|
||||
<cac:PartyName>
|
||||
<cbc:Name>Untitled Company</cbc:Name>
|
||||
</cac:PartyName>
|
||||
<cac:PostalAddress>
|
||||
<cbc:StreetName>Dudweilerstr. 34b</cbc:StreetName>
|
||||
<cbc:CityName>Ost Alessa</cbc:CityName>
|
||||
<cbc:PostalZone>98060</cbc:PostalZone>
|
||||
<cbc:CountrySubentity>Bayern</cbc:CountrySubentity>
|
||||
<cac:Country>
|
||||
<cbc:IdentificationCode>DE</cbc:IdentificationCode>
|
||||
</cac:Country>
|
||||
</cac:PostalAddress>
|
||||
<cac:PhysicalLocation>
|
||||
<cbc:StreetName>Dudweilerstr. 34b</cbc:StreetName>
|
||||
<cbc:CityName>Ost Alessa</cbc:CityName>
|
||||
<cbc:PostalZone>98060</cbc:PostalZone>
|
||||
<cbc:CountrySubentity>Bayern</cbc:CountrySubentity>
|
||||
<cac:Country>
|
||||
<cbc:IdentificationCode>DE</cbc:IdentificationCode>
|
||||
</cac:Country>
|
||||
</cac:PhysicalLocation>
|
||||
<cac:Contact>
|
||||
<cbc:ElectronicMail>owner@gmail.com</cbc:ElectronicMail>
|
||||
</cac:Contact>
|
||||
</cac:Party>
|
||||
</cac:AccountingSupplierParty>
|
||||
<cac:AccountingCustomerParty>
|
||||
<cac:Party>
|
||||
<cac:PartyName>
|
||||
<cbc:Name>German Client Name</cbc:Name>
|
||||
</cac:PartyName>
|
||||
<cac:PostalAddress>
|
||||
<cbc:StreetName>Kinderhausen 96b</cbc:StreetName>
|
||||
<cbc:CityName>Süd Jessestadt</cbc:CityName>
|
||||
<cbc:PostalZone>33323</cbc:PostalZone>
|
||||
<cbc:CountrySubentity>Bayern</cbc:CountrySubentity>
|
||||
<cac:Country>
|
||||
<cbc:IdentificationCode>DE</cbc:IdentificationCode>
|
||||
</cac:Country>
|
||||
</cac:PostalAddress>
|
||||
<cac:PhysicalLocation>
|
||||
<cbc:StreetName>Kinderhausen 96b</cbc:StreetName>
|
||||
<cbc:CityName>Süd Jessestadt</cbc:CityName>
|
||||
<cbc:PostalZone>33323</cbc:PostalZone>
|
||||
<cbc:CountrySubentity>Bayern</cbc:CountrySubentity>
|
||||
<cac:Country>
|
||||
<cbc:IdentificationCode>DE</cbc:IdentificationCode>
|
||||
</cac:Country>
|
||||
</cac:PhysicalLocation>
|
||||
<cac:Contact>
|
||||
<cbc:ElectronicMail>No Email Set</cbc:ElectronicMail>
|
||||
</cac:Contact>
|
||||
</cac:Party>
|
||||
</cac:AccountingCustomerParty>
|
||||
<cac:PaymentMeans>
|
||||
<PayeeFinancialAccount>
|
||||
<ID>DE89370400440532013000</ID>
|
||||
<Name>PFA-NAME</Name>
|
||||
<AliasName>PFA-Alias</AliasName>
|
||||
<AccountTypeCode>CHECKING</AccountTypeCode>
|
||||
<AccountFormatCode>IBAN</AccountFormatCode>
|
||||
<CurrencyCode>EUR</CurrencyCode>
|
||||
<FinancialInstitutionBranch>
|
||||
<ID>DEUTDEMMXXX</ID>
|
||||
<Name>Deutsche Bank</Name>
|
||||
</FinancialInstitutionBranch>
|
||||
</PayeeFinancialAccount>
|
||||
</cac:PaymentMeans>
|
||||
<cac:TaxTotal/>
|
||||
<cac:LegalMonetaryTotal>
|
||||
<cbc:LineExtensionAmount currencyID="EUR">100</cbc:LineExtensionAmount>
|
||||
<cbc:TaxExclusiveAmount currencyID="EUR">84.03</cbc:TaxExclusiveAmount>
|
||||
<cbc:TaxInclusiveAmount currencyID="EUR">100.00</cbc:TaxInclusiveAmount>
|
||||
<cbc:PayableAmount currencyID="EUR">100.00</cbc:PayableAmount>
|
||||
</cac:LegalMonetaryTotal>
|
||||
<cac:InvoiceLine>
|
||||
<cbc:ID>1</cbc:ID>
|
||||
<cbc:InvoicedQuantity>10</cbc:InvoicedQuantity>
|
||||
<cbc:LineExtensionAmount currencyID="EUR">100</cbc:LineExtensionAmount>
|
||||
<cac:TaxTotal>
|
||||
<cbc:TaxAmount currencyID="EUR">15.97</cbc:TaxAmount>
|
||||
<cac:TaxSubtotal>
|
||||
<cbc:TaxableAmount currencyID="EUR">100</cbc:TaxableAmount>
|
||||
<cbc:TaxAmount currencyID="EUR">15.97</cbc:TaxAmount>
|
||||
<cac:TaxCategory>
|
||||
<cbc:ID>C62</cbc:ID>
|
||||
<cbc:Percent>19</cbc:Percent>
|
||||
<cac:TaxScheme>
|
||||
<cbc:ID>mwst</cbc:ID>
|
||||
</cac:TaxScheme>
|
||||
</cac:TaxCategory>
|
||||
</cac:TaxSubtotal>
|
||||
</cac:TaxTotal>
|
||||
<cac:Item>
|
||||
<cbc:Description>Product Description</cbc:Description>
|
||||
<cbc:Name>Product Key</cbc:Name>
|
||||
</cac:Item>
|
||||
<cac:Price>
|
||||
<cbc:PriceAmount currencyID="EUR">10</cbc:PriceAmount>
|
||||
</cac:Price>
|
||||
</cac:InvoiceLine>
|
||||
';
|
||||
|
||||
|
||||
$sc = new \App\Services\EDocument\Gateway\Storecove\Storecove();
|
||||
$sc->sendDocument($x);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user