diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
new file mode 100644
index 0000000..065f4e1
--- /dev/null
+++ b/.github/workflows/tests.yml
@@ -0,0 +1,32 @@
+name: Tests
+
+on: [push, pull_request]
+
+jobs:
+ test:
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: true
+ matrix:
+ os: [ubuntu-latest]
+ php: [8.2, 8.1]
+ stability: [prefer-stable]
+
+ name: P${{ matrix.php }} - ${{ matrix.stability }} - ${{ matrix.os }}
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v3
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.php }}
+ extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo
+ coverage: none
+
+ - name: Install dependencies
+ run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction
+
+ - name: Execute tests
+ run: vendor/bin/phpunit
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..708b377
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+vendor
+composer.lock
+.phpunit.result.cache
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..0167e43
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,7 @@
+# Changelog
+
+All notable changes to `fattura-elettronica` will be documented in this file
+
+## beta - Versione Beta (2023-11-14)
+
+- Versione iniziale "beta" completa ma con funzionalità "base"
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..b146003
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,55 @@
+# Contributing
+
+Contributions are welcome.
+
+Please read and understand the contribution guide before creating an issue or pull request.
+
+## Etiquette
+
+This project is open source, and as such, the maintainers give their free time to build and maintain the source code
+held within. They make the code freely available in the hope that it will be of use to other developers. It would be
+extremely unfair for them to suffer abuse or anger for their hard work.
+
+Please be considerate towards maintainers when raising issues or presenting pull requests. Let's show the
+world that developers are civilized and selfless people.
+
+It's the duty of the maintainer to ensure that all submissions to the project are of sufficient
+quality to benefit the project. Many developers have different skillsets, strengths, and weaknesses. Respect the maintainer's decision, and do not be upset or abusive if your submission is not used.
+
+## Viability
+
+When requesting or submitting new features, first consider whether it might be useful to others. Open
+source projects are used by many developers, who may have entirely different needs to your own. Think about
+whether or not your feature is likely to be used by other users of the project.
+
+## Procedure
+
+Before filing an issue:
+
+- Attempt to replicate the problem, to ensure that it wasn't a coincidental incident.
+- Check to make sure your feature suggestion isn't already present within the project.
+- Check the pull requests tab to ensure that the bug doesn't have a fix in progress.
+- Check the pull requests tab to ensure that the feature isn't already in progress.
+
+Before submitting a pull request:
+
+- Check the codebase to ensure that your feature doesn't already exist.
+- Check the pull requests to ensure that another person hasn't already submitted the feature or fix.
+
+## Requirements
+
+If the project maintainer has any additional requirements, you will find them listed here.
+
+- **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](https://pear.php.net/package/PHP_CodeSniffer).
+
+- **Add tests!** - Your patch won't be accepted if it doesn't have tests.
+
+- **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date.
+
+- **Consider our release cycle** - We try to follow [SemVer v2.0.0](https://semver.org/). Randomly breaking public APIs is not an option.
+
+- **One pull request per feature** - If you want to do more than one thing, send multiple pull requests.
+
+- **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](https://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting.
+
+**Happy coding**!
diff --git a/LICENSE b/LICENSE.md
similarity index 96%
rename from LICENSE
rename to LICENSE.md
index 73ce75f..f206bf9 100644
--- a/LICENSE
+++ b/LICENSE.md
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2023 Axio Studio
+Copyright (c) Axio Studio s.r.l.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index 063c1bf..73308c1 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,114 @@
-# fattura-elettronica
-Una libreria Laravel per la generazione dell'XML di una fattura elettronica
+# Fattura Elettronica PHP
+
+[![Latest Version on Packagist](https://img.shields.io/packagist/v/axiostudio/fattura-elettronica.svg?style=flat-square)](https://packagist.org/packages/axiostudio/fattura-elettronica)
+[![Tests](https://github.com/axiostudio/fattura-elettronica/actions/workflows/tests.yml/badge.svg)](https://github.com/axiostudio/fattura-elettronica/actions/workflows/tests.yml)
+[![Total Downloads](https://img.shields.io/packagist/dt/axiostudio/fattura-elettronica.svg?style=flat-square)](https://packagist.org/packages/axiostudio/fattura-elettronica)
+
+## Introduzione
+
+Fattura Elettronica è un pacchetto PHP che consente di generare all'interno del proprio applicativo, fatture elettroniche in XML compatibili con le seguenti specifiche: https://www.agenziaentrate.gov.it/portale/web/guest/fatturazione-elettronica-e-dati-fatture-transfrontaliere-new
+
+## Requisiti
+
+- Composer 2
+- PHP 8.1 o 8.2
+
+## Installazione
+
+Per installare il pacchetto eseguire:
+
+```bash
+composer require axiostudio/fattura-elettronica
+```
+
+## Utilizzo
+
+Finita l'installazione, per utilizzare il pacchetto è necessario inizializzare la classe:
+
+```php
+$fattura = new \Axiostudio\FatturaElettronica\FatturaElettronica();
+```
+
+Successivamente bisogna passare i dati necessari al metodo "compose" che ci restituirà un array contente un parametro `fileName` che dichiara il nome del file XML generato e un parametro `fileContent` che contiene in formato string l'XML generato.
+
+Per generare la fattura è necessario richiamare il metodo `compose()` passando al suo interno i dati necessari alla fattura che saranno parsati, computati e convertiti in formato XML secondo le specifiche.
+
+Ricordiamo che il pacchetto è in fase BETA, le specifiche dei vari Model utilizzati saranno documentati nella versione 1.0. Di seguito un esempio di utilizzo:
+
+```php
+$fattura = new FatturaElettronica();
+
+$datiTrasmissione = [
+ '12345678910',
+ '123'
+];
+
+$anagraficaPrestatore = [
+ '12345678910',
+ 'Fornitore srl'
+];
+
+$sedePrestatore = [
+ 'Via Verdi',
+ '00100',
+ 'Roma',
+ 'RM'
+];
+
+$anagraficaCommittente = [
+ '12345678911',
+ 'Cliente srl'
+];
+
+$sedeCommittente = [
+ 'Via Puccini',
+ '20100',
+ 'Milano',
+ 'MI'
+];
+
+$datiGeneraliDocumento = [
+ '123',
+ '2021-01-01',
+ '345.22'
+];
+
+$datiGenerali = [$datiGeneraliDocumento];
+
+$dettaglioPagamento = ['345.22'];
+
+$datiDatiPagamento = [$dettaglioPagamento];
+
+$dettaglioLinee = [
+ ['Articolo di riga 1', '100.50'],
+ ['Articolo di riga 2', '100.50'],
+ ['Articolo di riga 3', '100.00', '1', 'pz', '0.00', 'N2.1'],
+];
+
+$datiRiepilogo = [
+ ['201.00', '22.00'],
+ ['100.00', '0.00', 'N2.1'],
+];
+
+$datiXml = $fattura->compose(
+ $datiTrasmissione,
+ $anagraficaPrestatore,
+ $sedePrestatore,
+ $anagraficaCommittente,
+ $sedeCommittente,
+ $datiGenerali,
+ $datiDatiPagamento,
+ $dettaglioLinee,
+ $datiRiepilogo
+);
+
+var_dump($datiXml);
+```
+
+## Note
+
+Questo pacchetto è in versione BETA, per supporto o bug utilizzare le Issue di Github, per collaborare invece è sufficente aprire un PR con le specifiche dell'integrazione eseguita.
+
+## Credits
+
+Questo pacchetto è stato creato ed è mantenuto da Axio Studio, per maggiori informazioni: https://axio.studio.
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..763dfbd
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,50 @@
+{
+ "name": "axiostudio/fattura-elettronica",
+ "description": "Pacchetto per la gestione della fatturazione elettronica in PHP.",
+ "keywords": [
+ "axiostudio",
+ "fattura-elettronica",
+ "xml",
+ "php",
+ "italia",
+ "package",
+ "fattura",
+ "PA",
+ "Laravel",
+ "Backend"
+ ],
+ "homepage": "https://github.com/axiostudio/fattura-elettronica",
+ "license": "MIT",
+ "type": "library",
+ "authors": [
+ {
+ "name": "Axio Studio",
+ "email": "info@axio.studio"
+ }
+ ],
+ "require": {
+ "php": "^8.1|^8.2",
+ "spatie/array-to-xml": "^3.2",
+ "symfony/validator": "^6.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.0"
+ },
+ "autoload": {
+ "psr-4": {
+ "Axiostudio\\FatturaElettronica\\": "src"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Axiostudio\\FatturaElettronica\\Tests\\": "tests"
+ }
+ },
+ "scripts": {
+ "test": "vendor/bin/phpunit"
+
+ },
+ "config": {
+ "sort-packages": true
+ }
+}
diff --git a/phpunit.xml b/phpunit.xml
new file mode 100644
index 0000000..58f4c6c
--- /dev/null
+++ b/phpunit.xml
@@ -0,0 +1,13 @@
+
+
+
+
+ ./src/
+
+
+
+
+ ./tests/
+
+
+
diff --git a/src/Abstracts/Model.php b/src/Abstracts/Model.php
new file mode 100644
index 0000000..96c338d
--- /dev/null
+++ b/src/Abstracts/Model.php
@@ -0,0 +1,11 @@
+createModel(new Fattura($header, $body), true);
+
+ return $this->createXml($fattura);
+ }
+
+ protected function create(
+ array $FatturaElettronicaHeader,
+ array $FatturaElettronicaBody,
+ array $DettaglioLinee,
+ array $DatiRiepilogo
+ ): array {
+
+ $xmlContent = $this->fileContent($FatturaElettronicaHeader, $FatturaElettronicaBody);
+ $xmlContent = $this->addDatiBeniServiziToXml($xmlContent, $DettaglioLinee, $DatiRiepilogo);
+
+ return [
+ 'fileContent' => $xmlContent,
+ 'fileName' => $this->fileName($FatturaElettronicaHeader)
+ ];
+ }
+
+ public function compose(
+ array $datiTrasmissione,
+ array $anagraficaPrestatore,
+ array $sedePrestatore,
+ array $anagraficaCommittente,
+ array $sedeCommittente,
+ array $datiGenerali,
+ array $datiDatiPagamento,
+ array $DettaglioLinee,
+ array $DatiRiepilogo
+ ): array {
+
+ $cedentePrestatore = [
+ $anagraficaPrestatore,
+ $sedePrestatore
+ ];
+
+ $cessionarioCommittente = [
+ $anagraficaCommittente,
+ $sedeCommittente
+ ];
+
+ $FatturaElettronicaHeader = [
+ $datiTrasmissione,
+ $cedentePrestatore,
+ $cessionarioCommittente
+ ];
+
+ $FatturaElettronicaBody = [
+ $datiGenerali,
+ $datiDatiPagamento
+ ];
+
+ return $this->create(
+ $FatturaElettronicaHeader,
+ $FatturaElettronicaBody,
+ $DettaglioLinee,
+ $DatiRiepilogo
+ );
+ }
+}
diff --git a/src/Handlers/Model.php b/src/Handlers/Model.php
new file mode 100644
index 0000000..756925a
--- /dev/null
+++ b/src/Handlers/Model.php
@@ -0,0 +1,34 @@
+addMethodMapping('loadValidatorMetadata')
+ ->getValidator();
+
+ $errors = $validator->validate($model);
+
+ if (count($errors) > 0) {
+ $errorsString = (string) $errors;
+ throw new \Exception($errorsString);
+ }
+
+ if (!$toArray) {
+ return $model;
+ }
+
+ return $this->modelToArray($model);
+ }
+
+ protected function modelToArray(ModelInterface $model): array
+ {
+ return json_decode(json_encode($model), true);
+ }
+};
diff --git a/src/Handlers/Xml.php b/src/Handlers/Xml.php
new file mode 100644
index 0000000..b634d7a
--- /dev/null
+++ b/src/Handlers/Xml.php
@@ -0,0 +1,41 @@
+updateXml($xml);
+ }
+
+ protected function updateXml(string $xml): string
+ {
+ $xml = str_replace('', Settings::XmlStart(), $xml);
+ $xml = str_replace('', '', $xml);
+ $xml = str_replace('', Settings::XmlEnd(), $xml);
+
+ return $xml;
+ }
+
+ public function createXmlBlock(array $array): string
+ {
+ $xml = ArrayToXml::convert($array);
+
+ return $this->updateXmlBlock($xml);
+ }
+
+ protected function updateXmlBlock(string $xml): string
+ {
+ $xml = str_replace('', '', $xml);
+ $xml = str_replace('', '', $xml);
+ $xml = str_replace('', '', $xml);
+
+ return $xml;
+ }
+}
diff --git a/src/Handlers/XmlDatiBeniServizi.php b/src/Handlers/XmlDatiBeniServizi.php
new file mode 100644
index 0000000..f9e385b
--- /dev/null
+++ b/src/Handlers/XmlDatiBeniServizi.php
@@ -0,0 +1,36 @@
+ $linea) {
+ $data = $this->createModel(new DettaglioLinee(...$linea), true);
+ $data['NumeroLinea'] = $numero + 1;
+ $DettaglioLineeBlock .= '' . $this->createXmlBlock($data) . '';
+ }
+
+ $DatiRiepilogoBlock = '';
+
+ foreach ($DatiRiepilogo as $dato) {
+ $data = $this->createModel(new DatiRiepilogo(...$dato), true);
+ $DatiRiepilogoBlock .= '' . $this->createXmlBlock($data) . '';
+ }
+
+ $xml = str_replace('', $DettaglioLineeBlock . $DatiRiepilogoBlock, $Xml);
+
+ return $xml;
+ }
+}
diff --git a/src/Models/Anagrafica.php b/src/Models/Anagrafica.php
new file mode 100644
index 0000000..59e5853
--- /dev/null
+++ b/src/Models/Anagrafica.php
@@ -0,0 +1,33 @@
+Denominazione = $args[0];
+ }
+
+ if (isset($args[1]) && $args[1]) {
+ $this->Nome = $args[1];
+ }
+
+ if (isset($args[2]) && $args[2]) {
+ $this->Cognome = $args[2];
+ }
+ }
+
+ public static function loadValidatorMetadata(ClassMetadata $metadata): void
+ {
+ //
+ }
+}
diff --git a/src/Models/CedentePrestatore.php b/src/Models/CedentePrestatore.php
new file mode 100644
index 0000000..307d23a
--- /dev/null
+++ b/src/Models/CedentePrestatore.php
@@ -0,0 +1,24 @@
+DatiAnagrafici = $this->createModel(new DatiAnagrafici(...$args[0]));
+ $this->Sede = $this->createModel(new Sede(...$args[1]));
+ }
+
+ public static function loadValidatorMetadata(ClassMetadata $metadata): void
+ {
+ //
+ }
+}
diff --git a/src/Models/CessionarioCommittente.php b/src/Models/CessionarioCommittente.php
new file mode 100644
index 0000000..c2d5d30
--- /dev/null
+++ b/src/Models/CessionarioCommittente.php
@@ -0,0 +1,24 @@
+DatiAnagrafici = $this->createModel(new DatiAnagrafici(...$args[0]));
+ $this->Sede = $this->createModel(new Sede(...$args[1]));
+ }
+
+ public static function loadValidatorMetadata(ClassMetadata $metadata): void
+ {
+ //
+ }
+}
diff --git a/src/Models/DatiAnagrafici.php b/src/Models/DatiAnagrafici.php
new file mode 100644
index 0000000..1854738
--- /dev/null
+++ b/src/Models/DatiAnagrafici.php
@@ -0,0 +1,40 @@
+IdFiscaleIVA = is_array($args[0]) ?
+ $this->createModel(new Id(...$args[0])) :
+ $this->createModel(new Id($args[0]));
+
+ $this->Anagrafica = is_array($args[1]) ?
+ $this->createModel(new Anagrafica(...$args[1])) :
+ $this->createModel(new Anagrafica($args[1]));
+
+ if (isset($args[2]) && $args[2]) {
+ $this->CodiceFiscale = $args[2];
+ }
+
+ $this->RegimeFiscale = (isset($args[3]) && $args[3]) ? $args[3] : Settings::RegimeFiscaleDefault();
+ }
+
+ public static function loadValidatorMetadata(ClassMetadata $metadata): void
+ {
+ $metadata->addPropertyConstraint('CodiceFiscale', new Length(null, 8, 16));
+ $metadata->addPropertyConstraint('RegimeFiscale', new Choice(Settings::RegimeFiscale()));
+ }
+}
diff --git a/src/Models/DatiBeniServizi.php b/src/Models/DatiBeniServizi.php
new file mode 100644
index 0000000..d4cf125
--- /dev/null
+++ b/src/Models/DatiBeniServizi.php
@@ -0,0 +1,22 @@
+XmlDatiBeniServizi = null;
+ }
+
+ public static function loadValidatorMetadata(ClassMetadata $metadata): void
+ {
+ //
+ }
+}
diff --git a/src/Models/DatiGenerali.php b/src/Models/DatiGenerali.php
new file mode 100644
index 0000000..0a5a16e
--- /dev/null
+++ b/src/Models/DatiGenerali.php
@@ -0,0 +1,22 @@
+DatiGeneraliDocumento = $this->createModel(new DatiGeneraliDocumento(...$args[0]));
+ }
+
+ public static function loadValidatorMetadata(ClassMetadata $metadata): void
+ {
+ //
+ }
+}
diff --git a/src/Models/DatiGeneraliDocumento.php b/src/Models/DatiGeneraliDocumento.php
new file mode 100644
index 0000000..ec93d0c
--- /dev/null
+++ b/src/Models/DatiGeneraliDocumento.php
@@ -0,0 +1,37 @@
+Numero = $args[0];
+ $this->Data = $args[1];
+ $this->ImportoTotaleDocumento = $args[2];
+ $this->Causale = (isset($args[4]) && $args[4]) ? $args[4] : Settings::CausaleDefault();
+ $this->TipoDocumento = (isset($args[4]) && $args[4]) ? $args[4] : Settings::TipoDocumentoDefault();
+ $this->Divisa = (isset($args[5]) && $args[5]) ? $args[5] : Settings::ValutaDefault();
+ }
+
+ public static function loadValidatorMetadata(ClassMetadata $metadata): void
+ {
+ $metadata->addPropertyConstraint('TipoDocumento', new Choice(Settings::TipoDocumento()));
+ $metadata->addPropertyConstraint('Divisa', new Length(3));
+ $metadata->addPropertyConstraint('Data', new Date());
+ }
+}
diff --git a/src/Models/DatiPagamento.php b/src/Models/DatiPagamento.php
new file mode 100644
index 0000000..aaff22c
--- /dev/null
+++ b/src/Models/DatiPagamento.php
@@ -0,0 +1,25 @@
+DettaglioPagamento = $this->createModel(new DettaglioPagamento(...$args[0]));
+ $this->CondizioniPagamento = (isset($args[1]) && $args[1]) ? $args[1] : Settings::CondizioniPagamentoDefault();
+ }
+
+ public static function loadValidatorMetadata(ClassMetadata $metadata): void
+ {
+ $metadata->addPropertyConstraint('CondizioniPagamento', new Choice(Settings::CondizioniPagamento()));
+ }
+}
diff --git a/src/Models/DatiRiepilogo.php b/src/Models/DatiRiepilogo.php
new file mode 100644
index 0000000..010b0d1
--- /dev/null
+++ b/src/Models/DatiRiepilogo.php
@@ -0,0 +1,37 @@
+ImponibileImporto = $args[0];
+ $this->AliquotaIVA = $args[1];
+
+ if (isset($args[2]) && $args[2]) {
+ $this->Natura = $args[2];
+ } else {
+ $this->EsigibilitaIVA = (isset($args[3]) && $args[3]) ? $args[3] : Settings::EsigibilitaIVADefault();
+ }
+
+ $this->Imposta = $this->ImponibileImporto * $this->AliquotaIVA / 100;
+ }
+
+ public static function loadValidatorMetadata(ClassMetadata $metadata): void
+ {
+ $metadata->addPropertyConstraint('Natura', new Choice(Settings::Natura()));
+ $metadata->addPropertyConstraint('EsigibilitaIVA', new Choice(Settings::EsigibilitaIVA()));
+ }
+}
diff --git a/src/Models/DatiTrasmissione.php b/src/Models/DatiTrasmissione.php
new file mode 100644
index 0000000..f087c7e
--- /dev/null
+++ b/src/Models/DatiTrasmissione.php
@@ -0,0 +1,42 @@
+IdTrasmittente = is_array($args[0]) ?
+ $this->createModel(new Id($args[0][0], $args[0][1])) :
+ $this->createModel(new Id($args[0]));
+
+ $this->ProgressivoInvio = $args[1];
+ $this->CodiceDestinatario = (isset($args[2]) && $args[2]) ? $args[2] : Settings::CodiceDestinatarioDefault();
+
+ if (isset($args[3]) && $args[3]) {
+ $this->PECDestinatario = $args[3];
+ }
+
+ $this->FormatoTrasmissione = (isset($args[4]) && $args[4]) ? $args[4] : Settings::FormatoTrasmissioneDefault();
+ }
+
+ public static function loadValidatorMetadata(ClassMetadata $metadata): void
+ {
+ $metadata->addPropertyConstraint('FormatoTrasmissione', new Choice(Settings::FormatoTrasmissione()));
+ $metadata->addPropertyConstraint('CodiceDestinatario', new Length(null, 6, 7));
+ $metadata->addPropertyConstraint('PECDestinatario', new Email());
+ }
+}
diff --git a/src/Models/DettaglioLinee.php b/src/Models/DettaglioLinee.php
new file mode 100644
index 0000000..4f35617
--- /dev/null
+++ b/src/Models/DettaglioLinee.php
@@ -0,0 +1,39 @@
+Descrizione = $args[0];
+ $this->PrezzoUnitario = $args[1];
+ $this->Quantita = (isset($args[2]) && $args[2]) ? $args[2] : Settings::QuantitaDefault();
+ $this->UnitaMisura = (isset($args[3]) && $args[3]) ? $args[3] : Settings::UnitaMisuraDefault();
+ $this->AliquotaIVA = (isset($args[4]) && $args[4]) ? $args[4] : Settings::AliquotaIVADefault();
+ $this->PrezzoTotale = $this->PrezzoUnitario * $this->Quantita;
+
+ if (isset($args[5]) && $args[5]) {
+ $this->Natura = $args[5];
+ }
+ }
+
+ public static function loadValidatorMetadata(ClassMetadata $metadata): void
+ {
+ $metadata->addPropertyConstraint('UnitaMisura', new Choice(Settings::UnitaMisura()));
+ $metadata->addPropertyConstraint('Natura', new Choice(Settings::Natura()));
+ }
+}
diff --git a/src/Models/DettaglioPagamento.php b/src/Models/DettaglioPagamento.php
new file mode 100644
index 0000000..d15d788
--- /dev/null
+++ b/src/Models/DettaglioPagamento.php
@@ -0,0 +1,80 @@
+ImportoPagamento = $args[0];
+
+ if (isset($args[1]) && $args[1]) {
+ $this->DataScadenzaPagamento = $args[1];
+ }
+
+ if (isset($args[2]) && $args[2]) {
+ $this->DataRiferimentoTerminiPagamento = $args[2];
+ }
+
+ if (isset($args[3]) && $args[3]) {
+ $this->GiorniTerminiPagamento = $args[3];
+ }
+
+ if (isset($args[4]) && $args[4]) {
+ $this->Beneficiario = $args[4];
+ }
+
+ if (isset($args[5]) && $args[5]) {
+ $this->IstituroFinanziario = $args[5];
+ }
+
+ if (isset($args[6]) && $args[6]) {
+ $this->IBAN = $args[6];
+ }
+
+ if (isset($args[7]) && $args[7]) {
+ $this->ABI = $args[7];
+ }
+
+ if (isset($args[7]) && $args[8]) {
+ $this->CAB = $args[8];
+ }
+
+ if (isset($args[8]) && $args[9]) {
+ $this->BIC = $args[9];
+ }
+
+ $this->ModalitaPagamento = (isset($args[10]) && $args[10]) ? $args[10] : Settings::ModalitaPagamentoDefault();
+ }
+
+ public static function loadValidatorMetadata(ClassMetadata $metadata): void
+ {
+ $metadata->addPropertyConstraint('DataScadenzaPagamento', new Date());
+ $metadata->addPropertyConstraint('DataRiferimentoTerminiPagamento', new Date());
+ $metadata->addPropertyConstraint('IBAN', new Iban());
+ $metadata->addPropertyConstraint('ABI', new Length(5));
+ $metadata->addPropertyConstraint('CAB', new Length(5));
+ $metadata->addPropertyConstraint('BIC', new Length(null, 8, 11));
+ $metadata->addPropertyConstraint('ModalitaPagamento', new Choice(Settings::ModalitaPagamento()));
+ }
+}
diff --git a/src/Models/FatturaElettronica.php b/src/Models/FatturaElettronica.php
new file mode 100644
index 0000000..49344fb
--- /dev/null
+++ b/src/Models/FatturaElettronica.php
@@ -0,0 +1,24 @@
+FatturaElettronicaHeader = $this->createModel(new FatturaElettronicaHeader(...$args[0]));
+ $this->FatturaElettronicaBody = $this->createModel(new FatturaElettronicaBody(...$args[1]));
+ }
+
+ public static function loadValidatorMetadata(ClassMetadata $metadata): void
+ {
+ //
+ }
+}
diff --git a/src/Models/FatturaElettronicaBody.php b/src/Models/FatturaElettronicaBody.php
new file mode 100644
index 0000000..f92f939
--- /dev/null
+++ b/src/Models/FatturaElettronicaBody.php
@@ -0,0 +1,27 @@
+DatiGenerali = $this->createModel(new DatiGenerali(...$args[0]));
+ $this->DatiPagamento = $this->createModel(new DatiPagamento(...$args[1]));
+ $this->DatiBeniServizi = $this->createModel(new DatiBeniServizi([]));
+ }
+
+ public static function loadValidatorMetadata(ClassMetadata $metadata): void
+ {
+ //
+ }
+}
diff --git a/src/Models/FatturaElettronicaHeader.php b/src/Models/FatturaElettronicaHeader.php
new file mode 100644
index 0000000..3473846
--- /dev/null
+++ b/src/Models/FatturaElettronicaHeader.php
@@ -0,0 +1,25 @@
+DatiTrasmissione = $this->createModel(new DatiTrasmissione(...$args[0]));
+ $this->CedentePrestatore = $this->createModel(new CedentePrestatore(...$args[1]));
+ $this->CessionarioCommittente = $this->createModel(new CessionarioCommittente(...$args[2]));
+ }
+
+ public static function loadValidatorMetadata(ClassMetadata $metadata): void
+ {
+ //
+ }
+}
diff --git a/src/Models/Id.php b/src/Models/Id.php
new file mode 100644
index 0000000..4178b67
--- /dev/null
+++ b/src/Models/Id.php
@@ -0,0 +1,26 @@
+IdCodice = $args[0];
+ $this->IdPaese = (isset($args[1]) && $args[1]) ? $args[1] : Settings::IdPaeseDefault();
+ }
+
+ public static function loadValidatorMetadata(ClassMetadata $metadata): void
+ {
+ $metadata->addPropertyConstraint('IdCodice', new Length(null, 9, 11));
+ $metadata->addPropertyConstraint('IdPaese', new Length(2));
+ }
+}
diff --git a/src/Models/Sede.php b/src/Models/Sede.php
new file mode 100644
index 0000000..8864d31
--- /dev/null
+++ b/src/Models/Sede.php
@@ -0,0 +1,32 @@
+Indirizzo = $args[0];
+ $this->CAP = $args[1];
+ $this->Comune = $args[2];
+ $this->Provincia = (isset($args[3]) && $args[3]) ? $args[3] : null;
+ $this->Nazione = (isset($args[4]) && $args[4]) ? $args[4] : Settings::IdPaeseDefault();
+ }
+
+ public static function loadValidatorMetadata(ClassMetadata $metadata): void
+ {
+ $metadata->addPropertyConstraint('Provincia', new Length(2));
+ $metadata->addPropertyConstraint('Nazione', new Length(2));
+ }
+}
diff --git a/src/Settings.php b/src/Settings.php
new file mode 100644
index 0000000..5565d9c
--- /dev/null
+++ b/src/Settings.php
@@ -0,0 +1,121 @@
+';
+ }
+
+ public static function XmlEnd(): string
+ {
+ return '';
+ }
+}
diff --git a/tests/AnagraficaTest.php b/tests/AnagraficaTest.php
new file mode 100644
index 0000000..0360a54
--- /dev/null
+++ b/tests/AnagraficaTest.php
@@ -0,0 +1,23 @@
+assertInstanceOf(Anagrafica::class, $anagrafica);
+ $this->assertEquals($denominazione, $anagrafica->Denominazione);
+ $this->assertEquals($nome, $anagrafica->Nome);
+ $this->assertEquals($cognome, $anagrafica->Cognome);
+ }
+}
diff --git a/tests/CedentePrestatoreTest.php b/tests/CedentePrestatoreTest.php
new file mode 100644
index 0000000..2f15c47
--- /dev/null
+++ b/tests/CedentePrestatoreTest.php
@@ -0,0 +1,30 @@
+assertInstanceOf(CedentePrestatore::class, $cedentePrestatore);
+ $this->assertInstanceOf(DatiAnagrafici::class, $cedentePrestatore->DatiAnagrafici);
+ $this->assertInstanceOf(Sede::class, $cedentePrestatore->Sede);
+ }
+}
diff --git a/tests/CessionarioCommittente.php b/tests/CessionarioCommittente.php
new file mode 100644
index 0000000..0463dfc
--- /dev/null
+++ b/tests/CessionarioCommittente.php
@@ -0,0 +1,30 @@
+assertInstanceOf(CessionarioCommittente::class, $cedentePrestatore);
+ $this->assertInstanceOf(DatiAnagrafici::class, $cedentePrestatore->DatiAnagrafici);
+ $this->assertInstanceOf(Sede::class, $cedentePrestatore->Sede);
+ }
+}
diff --git a/tests/DatiAnagraficiTest.php b/tests/DatiAnagraficiTest.php
new file mode 100644
index 0000000..0878046
--- /dev/null
+++ b/tests/DatiAnagraficiTest.php
@@ -0,0 +1,28 @@
+assertInstanceOf(DatiAnagrafici::class, $datiAnagrafici);
+ $this->assertInstanceOf(Id::class, $datiAnagrafici->IdFiscaleIVA);
+ $this->assertInstanceOf(Anagrafica::class, $datiAnagrafici->Anagrafica);
+ $this->assertEquals($codiceFiscale, $datiAnagrafici->CodiceFiscale);
+ $this->assertEquals($regimeFiscale, $datiAnagrafici->RegimeFiscale);
+ }
+}
diff --git a/tests/DatiBeniservizi.php b/tests/DatiBeniservizi.php
new file mode 100644
index 0000000..53aa212
--- /dev/null
+++ b/tests/DatiBeniservizi.php
@@ -0,0 +1,17 @@
+assertInstanceOf(DatiBeniServizi::class, $datiBeniServizi);
+ $this->assertNull($datiBeniServizi->XmlDatiBeniServizi);
+ }
+}
diff --git a/tests/DatiGeneraliDocumentoTest.php b/tests/DatiGeneraliDocumentoTest.php
new file mode 100644
index 0000000..0ce884e
--- /dev/null
+++ b/tests/DatiGeneraliDocumentoTest.php
@@ -0,0 +1,39 @@
+assertInstanceOf(DatiGeneraliDocumento::class, $datiGeneraliDocumento);
+ $this->assertEquals($numero, $datiGeneraliDocumento->Numero);
+ $this->assertEquals($data, $datiGeneraliDocumento->Data);
+ $this->assertEquals($importoTotaleDocumento, $datiGeneraliDocumento->ImportoTotaleDocumento);
+ $this->assertEquals(Settings::CausaleDefault(), $datiGeneraliDocumento->Causale);
+ $this->assertEquals(Settings::TipoDocumentoDefault(), $datiGeneraliDocumento->TipoDocumento);
+ $this->assertEquals(Settings::ValutaDefault(), $datiGeneraliDocumento->Divisa);
+ }
+
+ public function testValidation(): void
+ {
+ $datiGeneraliDocumento = new DatiGeneraliDocumento('123', '2023-11-13', 123.45);
+
+ $validator = Validation::createValidatorBuilder()->enableAnnotationMapping()->getValidator();
+
+ $violations = $validator->validate($datiGeneraliDocumento);
+
+ $this->assertCount(0, $violations);
+ }
+}
diff --git a/tests/DatiGeneraliTest.php b/tests/DatiGeneraliTest.php
new file mode 100644
index 0000000..a681ca0
--- /dev/null
+++ b/tests/DatiGeneraliTest.php
@@ -0,0 +1,24 @@
+assertInstanceOf(DatiGenerali::class, $datiGenerali);
+ $this->assertInstanceOf(DatiGeneraliDocumento::class, $datiGenerali->DatiGeneraliDocumento);
+ }
+}
diff --git a/tests/DatiPagamentoTest.php b/tests/DatiPagamentoTest.php
new file mode 100644
index 0000000..30e884d
--- /dev/null
+++ b/tests/DatiPagamentoTest.php
@@ -0,0 +1,22 @@
+assertInstanceOf(DatiPagamento::class, $datiPagamento);
+ $this->assertInstanceOf(DettaglioPagamento::class, $datiPagamento->DettaglioPagamento);
+ $this->assertEquals(Settings::CondizioniPagamentoDefault(), $datiPagamento->CondizioniPagamento);
+ }
+}
diff --git a/tests/DatiRiepilogoTest.php b/tests/DatiRiepilogoTest.php
new file mode 100644
index 0000000..0908e07
--- /dev/null
+++ b/tests/DatiRiepilogoTest.php
@@ -0,0 +1,24 @@
+assertInstanceOf(DatiRiepilogo::class, $datiRiepilogo);
+ $this->assertEquals($imponibileImporto, $datiRiepilogo->ImponibileImporto);
+ $this->assertEquals($aliquotaIVA, $datiRiepilogo->AliquotaIVA);
+ $this->assertEquals($natura, $datiRiepilogo->Natura);
+ $this->assertEquals($imponibileImporto * $aliquotaIVA / 100, $datiRiepilogo->Imposta);
+ }
+}
diff --git a/tests/DatiTrasmissioneTest.php b/tests/DatiTrasmissioneTest.php
new file mode 100644
index 0000000..f87b9fe
--- /dev/null
+++ b/tests/DatiTrasmissioneTest.php
@@ -0,0 +1,60 @@
+assertInstanceOf(DatiTrasmissione::class, $datiTrasmissione);
+ $this->assertInstanceOf(Id::class, $datiTrasmissione->IdTrasmittente);
+ $this->assertEquals($progressivoInvio, $datiTrasmissione->ProgressivoInvio);
+ $this->assertEquals($codiceDestinatario, $datiTrasmissione->CodiceDestinatario);
+ $this->assertEquals($pecDestinatario, $datiTrasmissione->PECDestinatario);
+ $this->assertEquals($formatoTrasmissione, $datiTrasmissione->FormatoTrasmissione);
+ }
+
+ public function testConstructorWithoutEmailPECDestinatario(): void
+ {
+ $id = '0123456789';
+
+ $progressivoInvio = '001';
+ $codiceDestinatario = '1234567';
+ $formatoTrasmissione = 'FPA12';
+
+ $datiTrasmissione = new DatiTrasmissione($id, $progressivoInvio, $codiceDestinatario, null, $formatoTrasmissione);
+
+ $this->assertInstanceOf(DatiTrasmissione::class, $datiTrasmissione);
+ $this->assertInstanceOf(Id::class, $datiTrasmissione->IdTrasmittente);
+ $this->assertEquals($progressivoInvio, $datiTrasmissione->ProgressivoInvio);
+ $this->assertEquals($codiceDestinatario, $datiTrasmissione->CodiceDestinatario);
+ $this->assertEquals($formatoTrasmissione, $datiTrasmissione->FormatoTrasmissione);
+ }
+
+ public function testValidation(): void
+ {
+ $id = '0123456789';
+
+ $datiTrasmissione = new DatiTrasmissione($id, '001', '1234567', 'pec@example.com', 'FPA12');
+
+ $validator = Validation::createValidatorBuilder()->enableAnnotationMapping()->getValidator();
+
+ $violations = $validator->validate($datiTrasmissione);
+
+ $this->assertCount(0, $violations);
+ }
+}
diff --git a/tests/DettaglioLineeTest.php b/tests/DettaglioLineeTest.php
new file mode 100644
index 0000000..2038770
--- /dev/null
+++ b/tests/DettaglioLineeTest.php
@@ -0,0 +1,61 @@
+assertInstanceOf(DettaglioLinee::class, $dettaglioLinee);
+ $this->assertEquals($descrizione, $dettaglioLinee->Descrizione);
+ $this->assertEquals($prezzoUnitario, $dettaglioLinee->PrezzoUnitario);
+ $this->assertEquals($quantita, $dettaglioLinee->Quantita);
+ $this->assertEquals($unitaMisura, $dettaglioLinee->UnitaMisura);
+ $this->assertEquals($aliquotaIVA, $dettaglioLinee->AliquotaIVA);
+ $this->assertEquals($prezzoUnitario * $quantita, $dettaglioLinee->PrezzoTotale);
+ $this->assertEquals($natura, $dettaglioLinee->Natura);
+ }
+
+ public function testConstructorWithoutNatura(): void
+ {
+ $descrizione = 'Product Description';
+ $prezzoUnitario = 10.00;
+ $quantita = 2.5;
+ $unitaMisura = 'PCE';
+ $aliquotaIVA = 22.00;
+
+ $dettaglioLinee = new DettaglioLinee($descrizione, $prezzoUnitario, $quantita, $unitaMisura, $aliquotaIVA);
+
+ $this->assertInstanceOf(DettaglioLinee::class, $dettaglioLinee);
+ $this->assertEquals($descrizione, $dettaglioLinee->Descrizione);
+ $this->assertEquals($prezzoUnitario, $dettaglioLinee->PrezzoUnitario);
+ $this->assertEquals($quantita, $dettaglioLinee->Quantita);
+ $this->assertEquals($unitaMisura, $dettaglioLinee->UnitaMisura);
+ $this->assertEquals($aliquotaIVA, $dettaglioLinee->AliquotaIVA);
+ $this->assertEquals($prezzoUnitario * $quantita, $dettaglioLinee->PrezzoTotale);
+ }
+
+ public function testValidation(): void
+ {
+ $dettaglioLinee = new DettaglioLinee('Product Description', 10.00, 2.5, 'PCE', 22.00);
+
+ $validator = Validation::createValidatorBuilder()->enableAnnotationMapping()->getValidator();
+
+ $violations = $validator->validate($dettaglioLinee);
+
+ $this->assertCount(0, $violations);
+ }
+}
diff --git a/tests/DettaglioPagamentoTest.php b/tests/DettaglioPagamentoTest.php
new file mode 100644
index 0000000..d57c139
--- /dev/null
+++ b/tests/DettaglioPagamentoTest.php
@@ -0,0 +1,97 @@
+assertInstanceOf(DettaglioPagamento::class, $dettaglioPagamento);
+ $this->assertEquals($importoPagamento, $dettaglioPagamento->ImportoPagamento);
+ $this->assertEquals($dataScadenzaPagamento, $dettaglioPagamento->DataScadenzaPagamento);
+ $this->assertEquals($dataRiferimentoTerminiPagamento, $dettaglioPagamento->DataRiferimentoTerminiPagamento);
+ $this->assertEquals($giorniTerminiPagamento, $dettaglioPagamento->GiorniTerminiPagamento);
+ $this->assertEquals($beneficiario, $dettaglioPagamento->Beneficiario);
+ $this->assertEquals($istitutoFinanziario, $dettaglioPagamento->IstituroFinanziario);
+ $this->assertEquals($iban, $dettaglioPagamento->IBAN);
+ $this->assertEquals($abi, $dettaglioPagamento->ABI);
+ $this->assertEquals($cab, $dettaglioPagamento->CAB);
+ $this->assertEquals($bic, $dettaglioPagamento->BIC);
+ $this->assertEquals($modalitaPagamento, $dettaglioPagamento->ModalitaPagamento);
+ }
+
+ public function testConstructorWithoutIBAN(): void
+ {
+ $importoPagamento = 100.00;
+ $dataScadenzaPagamento = '2023-01-01';
+ $dataRiferimentoTerminiPagamento = '2023-01-05';
+ $giorniTerminiPagamento = '5';
+ $beneficiario = 'John Doe';
+ $istitutoFinanziario = 'Bank ABC';
+ $modalitaPagamento = 'MP02';
+
+ $dettaglioPagamento = new DettaglioPagamento(
+ $importoPagamento,
+ $dataScadenzaPagamento,
+ $dataRiferimentoTerminiPagamento,
+ $giorniTerminiPagamento,
+ $beneficiario,
+ $istitutoFinanziario,
+ null,
+ null,
+ null,
+ null,
+ $modalitaPagamento
+ );
+
+ $this->assertInstanceOf(DettaglioPagamento::class, $dettaglioPagamento);
+ $this->assertEquals($importoPagamento, $dettaglioPagamento->ImportoPagamento);
+ $this->assertEquals($dataScadenzaPagamento, $dettaglioPagamento->DataScadenzaPagamento);
+ $this->assertEquals($dataRiferimentoTerminiPagamento, $dettaglioPagamento->DataRiferimentoTerminiPagamento);
+ $this->assertEquals($giorniTerminiPagamento, $dettaglioPagamento->GiorniTerminiPagamento);
+ $this->assertEquals($beneficiario, $dettaglioPagamento->Beneficiario);
+ $this->assertEquals($istitutoFinanziario, $dettaglioPagamento->IstituroFinanziario);
+ $this->assertEquals($modalitaPagamento, $dettaglioPagamento->ModalitaPagamento);
+ }
+
+ public function testValidation(): void
+ {
+ $dettaglioPagamento = new DettaglioPagamento(100.00, '2023-01-01', '2023-01-05', '5', 'John Doe', 'Bank ABC', 'IT60X0542811101000000123456', '12345', '67890', 'ABCITR12345', 'MP02');
+
+ $validator = Validation::createValidatorBuilder()->enableAnnotationMapping()->getValidator();
+
+ $violations = $validator->validate($dettaglioPagamento);
+
+ $this->assertCount(0, $violations);
+ }
+}
diff --git a/tests/IdTest.php b/tests/IdTest.php
new file mode 100644
index 0000000..65c880c
--- /dev/null
+++ b/tests/IdTest.php
@@ -0,0 +1,45 @@
+assertInstanceOf(Id::class, $id);
+ $this->assertEquals($idCodice, $id->IdCodice);
+ $this->assertEquals($idPaese, $id->IdPaese);
+ }
+
+ public function testConstructorWithoutIdPaese(): void
+ {
+ $idCodice = 'ABC123456789';
+
+ $id = new Id($idCodice);
+
+ $this->assertInstanceOf(Id::class, $id);
+ $this->assertEquals($idCodice, $id->IdCodice);
+ $this->assertEquals(Settings::IdPaeseDefault(), $id->IdPaese);
+ }
+
+ public function testValidation(): void
+ {
+ $id = new Id('ABC123456789', 'IT');
+
+ $validator = Validation::createValidatorBuilder()->enableAnnotationMapping()->getValidator();
+
+ $violations = $validator->validate($id);
+
+ $this->assertCount(0, $violations);
+ }
+}
diff --git a/tests/SedeTest.php b/tests/SedeTest.php
new file mode 100644
index 0000000..b981387
--- /dev/null
+++ b/tests/SedeTest.php
@@ -0,0 +1,56 @@
+assertInstanceOf(Sede::class, $sede);
+ $this->assertEquals($indirizzo, $sede->Indirizzo);
+ $this->assertEquals($cap, $sede->CAP);
+ $this->assertEquals($comune, $sede->Comune);
+ $this->assertEquals($provincia, $sede->Provincia);
+ $this->assertEquals($nazione, $sede->Nazione);
+ }
+
+ public function testConstructorWithoutProvinciaAndNazione(): void
+ {
+ $indirizzo = 'Via Example, 123';
+ $cap = '12345';
+ $comune = 'Example City';
+
+ $sede = new Sede($indirizzo, $cap, $comune);
+
+ $this->assertInstanceOf(Sede::class, $sede);
+ $this->assertEquals($indirizzo, $sede->Indirizzo);
+ $this->assertEquals($cap, $sede->CAP);
+ $this->assertEquals($comune, $sede->Comune);
+ $this->assertNull($sede->Provincia);
+ $this->assertEquals(Settings::IdPaeseDefault(), $sede->Nazione);
+ }
+
+ public function testValidation(): void
+ {
+ $sede = new Sede('Via Example, 123', '12345', 'Example City', 'EX', 'IT');
+
+ $validator = Validation::createValidatorBuilder()->enableAnnotationMapping()->getValidator();
+
+ $violations = $validator->validate($sede);
+
+ $this->assertCount(0, $violations);
+ }
+}
diff --git a/tests/TestCase.php b/tests/TestCase.php
new file mode 100644
index 0000000..9669a5c
--- /dev/null
+++ b/tests/TestCase.php
@@ -0,0 +1,8 @@
+