From a72f26b242e1aecfe63a7d56fdea8b8d023be30c Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 18 Jul 2024 14:13:07 +1000 Subject: [PATCH] Add prop resolver --- app/Services/EDocument/Standards/Peppol.php | 24 ++-- .../Standards/Settings/PropertyResolver.php | 44 +++++++ tests/Feature/EInvoice/PeppolTest.php | 113 +++++++++++++++++ .../Einvoice/Storecove/StorecoveTest.php | 116 ++++++++++++++++++ 4 files changed, 286 insertions(+), 11 deletions(-) create mode 100644 app/Services/EDocument/Standards/Settings/PropertyResolver.php diff --git a/app/Services/EDocument/Standards/Peppol.php b/app/Services/EDocument/Standards/Peppol.php index def2cee37bcd..afc2825813a2 100644 --- a/app/Services/EDocument/Standards/Peppol.php +++ b/app/Services/EDocument/Standards/Peppol.php @@ -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; diff --git a/app/Services/EDocument/Standards/Settings/PropertyResolver.php b/app/Services/EDocument/Standards/Settings/PropertyResolver.php new file mode 100644 index 000000000000..2476a70b39a0 --- /dev/null +++ b/app/Services/EDocument/Standards/Settings/PropertyResolver.php @@ -0,0 +1,44 @@ +{$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); + } +} \ No newline at end of file diff --git a/tests/Feature/EInvoice/PeppolTest.php b/tests/Feature/EInvoice/PeppolTest.php index 787ad6913e72..e7d85b759340 100644 --- a/tests/Feature/EInvoice/PeppolTest.php +++ b/tests/Feature/EInvoice/PeppolTest.php @@ -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() { diff --git a/tests/Integration/Einvoice/Storecove/StorecoveTest.php b/tests/Integration/Einvoice/Storecove/StorecoveTest.php index ab732d73af59..414feab2047a 100644 --- a/tests/Integration/Einvoice/Storecove/StorecoveTest.php +++ b/tests/Integration/Einvoice/Storecove/StorecoveTest.php @@ -217,6 +217,122 @@ class StorecoveTest extends TestCase '; +//inclusive +$x = ' + + +DE-53423 + 2024-07-18 + 380 + + + + Untitled Company + + + Dudweilerstr. 34b + Ost Alessa + 98060 + Bayern + + DE + + + + Dudweilerstr. 34b + Ost Alessa + 98060 + Bayern + + DE + + + + owner@gmail.com + + + + + + + German Client Name + + + Kinderhausen 96b + Süd Jessestadt + 33323 + Bayern + + DE + + + + Kinderhausen 96b + Süd Jessestadt + 33323 + Bayern + + DE + + + + No Email Set + + + + + + DE89370400440532013000 + PFA-NAME + PFA-Alias + CHECKING + IBAN + EUR + + DEUTDEMMXXX + Deutsche Bank + + + + + + 100 + 84.03 + 100.00 + 100.00 + + + 1 + 10 + 100 + + 15.97 + + 100 + 15.97 + + C62 + 19 + + mwst + + + + + + Product Description + Product Key + + + 10 + + +'; + + $sc = new \App\Services\EDocument\Gateway\Storecove\Storecove(); $sc->sendDocument($x);