1
0
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:
David Bomba 2024-07-18 14:13:07 +10:00
parent acf73fb6fd
commit a72f26b242
4 changed files with 286 additions and 11 deletions

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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()
{

View File

@ -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&#xFC;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&#xFC;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);