mirror of
				https://github.com/invoiceninja/invoiceninja.git
				synced 2025-10-31 12:57:30 -04: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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user