diff --git a/app/Services/EDocument/Standards/FatturaPANew.php b/app/Services/EDocument/Standards/FatturaPANew.php new file mode 100644 index 0000000000..19ceb0c26a --- /dev/null +++ b/app/Services/EDocument/Standards/FatturaPANew.php @@ -0,0 +1,281 @@ +init() + ->setIdTrasmittente() //order of execution matters. + ->setDatiTrasmissione() + ->setIdFiscaleIVA() + ->setAnagrafica() + ->setDatiAnagrafici() + ->setCedentePrestatore() + ->setClientDetails() + ->setDatiGeneraliDocumento() + ->setDatiGenerali() + ->setLineItems() + ->setDettaglioPagamento() + ->setFatturaElettronica(); + } + + public function getFatturaElettronica(): FatturaElettronica + { + return $this->FatturaElettronica; + } + + private function setDatiTrasmissione(): self + { + + $this->DatiTrasmissione->FormatoTrasmissione = "FPR12"; + $this->DatiTrasmissione->CodiceDestinatario = $this->invoice->client->routing_id; + $this->DatiTrasmissione->ProgressivoInvio = $this->invoice->number; + + $this->DatiTrasmissione->IdTrasmittente = $this->IdTrasmittente; + + $this->FatturaElettronicaHeader->DatiTrasmissione = $this->DatiTrasmissione; + + return $this; + } + + + private function setIdTrasmittente():self + { + $this->IdTrasmittente->IdPaese = $this->invoice->company->country()->iso_3166_2; + $this->IdTrasmittente->IdCodice = $this->invoice->company->settings->vat_number; + + return $this; + } + + private function setCedentePrestatore():self + { + $this->CedentePrestatore->DatiAnagrafici = $this->DatiAnagrafici; + + $sede = new Sede; + $sede->Indirizzo = $this->invoice->company->settings->address1; + $sede->CAP = (int)$this->invoice->company->settings->postal_code; + $sede->Comune = $this->invoice->company->settings->city; + $sede->Provincia = $this->invoice->company->settings->state; + $sede->Nazione = $this->invoice->company->country()->iso_3166_2; + $this->CedentePrestatore->Sede = $sede; + + $this->FatturaElettronicaHeader->CedentePrestatore = $this->CedentePrestatore; + + return $this; + } + + private function setDatiAnagrafici():self + { + $this->DatiAnagrafici->RegimeFiscale = "RF01"; + $this->DatiAnagrafici->Anagrafica = $this->Anagrafica; + $this->DatiAnagrafici->IdFiscaleIVA = $this->IdFiscaleIVA; + + return $this; + } + + private function setClientDetails():self + { + + $datiAnagrafici = new DatiAnagrafici(); + $anagrafica = new Anagrafica(); + $anagrafica->Denominazione = $this->invoice->client->present()->name(); + $datiAnagrafici->Anagrafica = $anagrafica; + + $sede = new Sede; + $sede->Indirizzo = $this->invoice->client->address1; + $sede->CAP = (int)$this->invoice->client->postal_code; + $sede->Comune = $this->invoice->client->city; + $sede->Provincia = $this->invoice->client->state; + $sede->Nazione = $this->invoice->client->country->iso_3166_2; + + $cessionarioCommittente = new CessionarioCommittente; + $cessionarioCommittente->DatiAnagrafici = $datiAnagrafici; + $cessionarioCommittente->Sede = $sede; + + $this->FatturaElettronicaHeader->CessionarioCommittente = $cessionarioCommittente; + + return $this; + } + + private function setIdFiscaleIVA():self + { + + $this->IdFiscaleIVA->IdPaese = $this->invoice->company->country()->iso_3166_2; + $this->IdFiscaleIVA->IdCodice = $this->invoice->company->settings->vat_number; + + return $this; + } + + //this is a choice, need to switch based on values here. + private function setAnagrafica():self + { + $this->Anagrafica->Denominazione = $this->invoice->company->present()->name(); + + return $this; + } + + private function setDatiGeneraliDocumento():self + { + + $this->DatiGeneraliDocumento->TipoDocumento = "TD01"; + $this->DatiGeneraliDocumento->Divisa = $this->invoice->client->currency()->code; + $this->DatiGeneraliDocumento->Data = new \DateTime($this->invoice->date); + $this->DatiGeneraliDocumento->Numero = $this->invoice->number; + $this->DatiGeneraliDocumento->Causale[] = substr($this->invoice->public_notes ?? '',0, 200); //unsure.. + + return $this; + } + + private function setDatiGenerali():self + { + $this->DatiGenerali->DatiGeneraliDocumento = $this->DatiGeneraliDocumento; + + $this->FatturaElettronicaBody->DatiGenerali = $this->DatiGenerali; + + return $this; + } + + private function setDettaglioPagamento():self + { + + $this->DettaglioPagamento->ModalitaPagamento = "MP01"; //String + $this->DettaglioPagamento->DataScadenzaPagamento = new \DateTime($this->invoice->due_date ?? $this->invoice->date); + $this->DettaglioPagamento->ImportoPagamento = (string) sprintf('%0.2f', $this->invoice->balance); + + $DatiPagamento = new DatiPagamento; + $DatiPagamento->CondizioniPagamento = "TP02"; + $DatiPagamento->DettaglioPagamento[] = $this->DettaglioPagamento; + + $this->FatturaElettronicaBody->DatiPagamento[] = $DatiPagamento; + + return $this; + } + + private function setLineItems(): self + { + + $datiBeniServizi = new DatiBeniServizi(); + $tax_rate_level = 0; + //line items + foreach ($this->invoice->line_items as $key => $item) { + + $numero = $key + 1; + $dettaglioLinee = new DettaglioLinee; + $dettaglioLinee->NumeroLinea = "{$numero}"; + $dettaglioLinee->Descrizione = $item->notes ?? 'Descrizione'; + $dettaglioLinee->Quantita = sprintf('%0.2f', $item->quantity); + $dettaglioLinee->PrezzoUnitario = sprintf('%0.2f', $item->cost); + $dettaglioLinee->PrezzoTotale = sprintf('%0.2f', $item->line_total); + $dettaglioLinee->AliquotaIVA = sprintf('%0.2f', $item->tax_rate1); + + + $datiBeniServizi->DettaglioLinee[] = $dettaglioLinee; + + if ($item->tax_rate1 > $tax_rate_level) { + $tax_rate_level = sprintf('%0.2f', $item->tax_rate1); + } + + } + + //totals + if($this->invoice->tax_rate1 > $tax_rate_level) { + $tax_rate_level = sprintf('%0.2f', $this->invoice->tax_rate1); + } + + $calc = $this->invoice->calc(); + $subtotal = sprintf('%0.2f', $calc->getSubTotal()); + $taxes = sprintf('%0.2f', $calc->getTotalTaxes()); + + $datiRiepilogo = new DatiRiepilogo; + $datiRiepilogo->AliquotaIVA = "{$tax_rate_level}"; + $datiRiepilogo->ImponibileImporto = "{$subtotal}"; + $datiRiepilogo->Imposta = "{$taxes}"; + $datiRiepilogo->EsigibilitaIVA = "I"; + + $datiBeniServizi->DatiRiepilogo[] = $datiRiepilogo; + + $this->FatturaElettronicaBody->DatiBeniServizi = $datiBeniServizi; + + return $this; + } + + private function setFatturaElettronica(): self + { + + $this->FatturaElettronica->FatturaElettronicaBody[] = $this->FatturaElettronicaBody; + $this->FatturaElettronica->FatturaElettronicaHeader = $this->FatturaElettronicaHeader; + + return $this; + } + + private function init(): self + { + + $this->FatturaElettronica = new FatturaElettronica; + $this->FatturaElettronicaBody = new FatturaElettronicaBody; + $this->FatturaElettronicaHeader = new FatturaElettronicaHeader; + $this->DatiTrasmissione = new DatiTrasmissione; + $this->IdTrasmittente = new IdTrasmittente; + $this->CedentePrestatore = new CedentePrestatore; + $this->DatiAnagrafici = new DatiAnagrafici; + $this->IdFiscaleIVA = new IdFiscaleIVA; + $this->Anagrafica = new Anagrafica; + $this->DatiGeneraliDocumento = new DatiGeneraliDocumento; + $this->DatiGenerali = new DatiGenerali; + $this->DettaglioPagamento = new DettaglioPagamento; + + return $this; + + } +} \ No newline at end of file diff --git a/composer.lock b/composer.lock index e791c2541a..576f99130b 100644 --- a/composer.lock +++ b/composer.lock @@ -5076,12 +5076,12 @@ "source": { "type": "git", "url": "https://github.com/invoiceninja/einvoice.git", - "reference": "d1d416382b1949cc2cbb6501487f1592acf4d235" + "reference": "6b908a33abab764dbb1ff113b8fe0992b18fe024" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/invoiceninja/einvoice/zipball/d1d416382b1949cc2cbb6501487f1592acf4d235", - "reference": "d1d416382b1949cc2cbb6501487f1592acf4d235", + "url": "https://api.github.com/repos/invoiceninja/einvoice/zipball/6b908a33abab764dbb1ff113b8fe0992b18fe024", + "reference": "6b908a33abab764dbb1ff113b8fe0992b18fe024", "shasum": "" }, "require": { @@ -5121,7 +5121,7 @@ "source": "https://github.com/invoiceninja/einvoice/tree/main", "issues": "https://github.com/invoiceninja/einvoice/issues" }, - "time": "2024-05-27T23:02:56+00:00" + "time": "2024-05-28T01:34:02+00:00" }, { "name": "invoiceninja/inspector", diff --git a/database/factories/ClientFactory.php b/database/factories/ClientFactory.php index 3b0463502f..f979c8d7d3 100644 --- a/database/factories/ClientFactory.php +++ b/database/factories/ClientFactory.php @@ -13,6 +13,7 @@ namespace Database\Factories; use App\DataMapper\ClientSettings; use Illuminate\Database\Eloquent\Factories\Factory; +use Str; class ClientFactory extends Factory { @@ -49,6 +50,7 @@ class ClientFactory extends Factory 'shipping_country_id' => 4, 'settings' => ClientSettings::defaults(), 'client_hash' => \Illuminate\Support\Str::random(40), + 'routing_id' => rand(100000,200000), ]; } } diff --git a/tests/Feature/EInvoice/FatturaPATest.php b/tests/Feature/EInvoice/FatturaPATest.php index bb683e10ac..7c9c57dbc0 100644 --- a/tests/Feature/EInvoice/FatturaPATest.php +++ b/tests/Feature/EInvoice/FatturaPATest.php @@ -11,11 +11,21 @@ namespace Tests\Feature\EInvoice; -use App\Services\EDocument\Standards\FatturaPA; -use Illuminate\Foundation\Testing\DatabaseTransactions; -use Illuminate\Routing\Middleware\ThrottleRequests; -use Tests\MockAccountData; use Tests\TestCase; +use App\Models\Client; +use App\Models\Company; +use Tests\MockAccountData; +use App\DataMapper\ClientSettings; +use App\DataMapper\CompanySettings; +use App\DataMapper\InvoiceItem; +use App\Models\Invoice; +use Invoiceninja\Einvoice\Symfony\Encode; +use App\Services\EDocument\Standards\FatturaPANew; +use Illuminate\Routing\Middleware\ThrottleRequests; +use Illuminate\Foundation\Testing\DatabaseTransactions; +use Invoiceninja\Einvoice\Models\FatturaPA\FatturaElettronica; +use Invoiceninja\Einvoice\Models\FatturaPA\FatturaElettronicaBodyType\FatturaElettronicaBody; +use Invoiceninja\Einvoice\Models\FatturaPA\FatturaElettronicaHeaderType\FatturaElettronicaHeader; /** * @test @@ -38,9 +48,82 @@ class FatturaPATest extends TestCase public function testInvoiceBoot() { - $fat = new FatturaPA($this->invoice); - $xml = $fat->run(); - $this->assertnotNull($xml); + $settings = CompanySettings::defaults(); + $settings->address1 = 'Via Silvio Spaventa 108'; + $settings->city = 'Calcinelli'; + +$settings->state = 'PA'; + +// $settings->state = 'Perugia'; + $settings->postal_code = '61030'; + $settings->country_id = '380'; + $settings->currency_id = '3'; + $settings->vat_number = '01234567890'; + $settings->id_number = ''; + + $company = Company::factory()->create([ + 'account_id' => $this->account->id, + 'settings' => $settings, + ]); + + $client_settings = ClientSettings::defaults(); + $client_settings->currency_id = '3'; + + $client = Client::factory()->create([ + 'company_id' => $company->id, + 'user_id' => $this->user->id, + 'name' => 'Italian Client Name', + 'address1' => 'Via Antonio da Legnago 68', + 'city' => 'Monasterace', + + 'state' => 'CR', + // 'state' => 'Reggio Calabria', + 'postal_code' => '89040', + 'country_id' => 380, + 'routing_id' => 'ABC1234', + 'settings' => $client_settings, + ]); + + $item = new InvoiceItem; + $item->product_key = "Product Key"; + $item->notes = "Product Description"; + $item->cost = 10; + $item->quantity = 10; + + $invoice = Invoice::factory()->create([ + 'company_id' => $company->id, + 'user_id' => $this->user->id, + 'client_id' => $client->id, + 'discount' => 0, + 'uses_inclusive_taxes' => false, + 'status_id' => 1, + 'tax_rate1' => 19, + 'tax_name1' => 'IVA', + 'line_items' => [$item], + 'number' => 'ITA-'.rand(1000,100000) + ]); + + $invoice->service()->markSent()->save(); + + $fat = new FatturaPANew($invoice); + $fat->run(); + + $fe = $fat->getFatturaElettronica(); + + $this->assertNotNull($fe); + + $this->assertInstanceOf(FatturaElettronica::class, $fe); + $this->assertInstanceOf(FatturaElettronicaBody::class, $fe->FatturaElettronicaBody[0]); + $this->assertInstanceOf(FatturaElettronicaHeader::class, $fe->FatturaElettronicaHeader); + + + $encoder = new Encode($fe); + $xml = $encoder->toXml(); + + echo $xml; + $this->assertNotNull($xml); + + } }