Code Monkey home page Code Monkey logo

php-e-invoice-it's Introduction

PHP E-invoice It

A PHP package for managing italian e-invoice and notice XML formats:

  • XML management through DOMDocument and DOMXPath
  • full FatturaElettronica (FatturaPA) XML skeleton
  • smart simplified xpath strings for getting/setting values (and for adding/getting/removing elements if needed)
  • multiple bodies (invoice lot)
  • multiple line items (DettaglioLinee)
  • multiple generic elements with setElementCount()
  • XML normalization: remove empty elements and automatically split Causale in chunks if > 200 characters
  • optional FatturaElettronica validation thanks to Slamdunk/php-validatore-fattura-elettronica

(Pacchetto PHP per gestire il formato XML di fatture e notifiche come richiesto dal SdI).

(Qui la documentazione aggiornata in italiano).

Please refer to

See Forum Italia - Fatturazione Elettronica for server configuration, interoperability tests, etc. In particular:

Apache configuration
Accreditamento SDICoop: configurazione SSL su Apache - Fatturazione Elettroni…
Interoperability tests
Test Interoperabilità Soluzioni - Fatturazione Elettronica - Forum Italia

Quickstart

Dependencies

  • php-xml
  • tested for PHP 7+ should work on PHP 5.5 too

Installation

Composer

composer require taocomp/php-e-invoice-it

Manually

  • Clone/download the repository
  • require_once('/path/to/php-e-invoice-it/vendor/autoload.php');

Invoice

Create a new invoice

Create a new FPA12 invoice:

$invoice = new FatturaElettronica('FPA12');

Create a new FPR12 invoice:

$invoice = new FatturaElettronica('FPR12');

Create a new invoice from file

$invoice = new FatturaElettronica('/path/to/invoice.xml');

Create a custom template invoice to be used later

$prefixPath = __DIR__ . '/tmpfiles';
$filename = 'my-custom-template.xml';
$invoice = new FatturaElettronica('FPR12');
$invoice->setValue('IdTrasmittente/IdCodice', '00011122233');
$invoice->setValue('IdTrasmittente/IdPaese', 'IT');
$invoice->setFilename($filename);
$invoice->setPrefixPath($prefixPath)->save();

Invoice lot and line items

Set 3 bodies:

$invoice->addBody(2);
// or
$invoice->setBodyCount(3);

Set 4 line items for second body:

$invoice->addLineItem(3, 2);
// or
$invoice->setLineItemCount(4, 2);

Get/Set values

In general, you can get/set values (and add/get/remove elements) by using a tag/path and an optional context.

Please note that:

  • an absolute path is relative to the root element: /FatturaElettronicaHeader means /p:FatturaElettronica/FatturaElettronicaHeader
  • tags and relative paths are prefixed with (.)//
  • xpath predicates are allowed: $invoice->getValue('DettaglioLinee[2]/NumeroLinea');

Get a value:

$invoice->getValue('ModalitaPagamento');

Get a value with a context:

$invoice->getValue('NumItem', 'DatiContratto');

Invalid queries (they return more than one element):

$invoice->getValue('IdPaese');
$invoice->getValue('Sede/Indirizzo', 'FatturaElettronicaHeader');

Set a value for a specific element:

$invoice->setValue('ProgressivoInvio', 10001);

Set many single values at once:

$invoice->setValues('IdTrasmittente', array(
    'IdCodice' => '09876543210',
    'IdPaese' => 'IT'
));
$invoice->setValues('CedentePrestatore/Sede', array(
    'Indirizzo' => 'VIA UNIVERSO 1'
));
$invoice->setValues('CessionarioCommittente', array(
    // CessionarioCommittente/DatiAnagrafici/CodiceFiscale
    'DatiAnagrafici/CodiceFiscale' => '01234567890',
    // Denominazione, somewhere inside CessionarioCommittente
    'Denominazione' => 'BETA SRL'
));
// Set values for second body
$body2 = $invoice->getBody(2);
$invoice->setValue('Numero', 44, $body2);
$invoice->setValue('Data', '2018-12-12', $body2);

Set values to multiple elements at once:

$invoice->setValuesToAll('DatiGenerali', array(
    // All "RiferimentoNumeroLinea" somewhere inside DatiGenerali
    'RiferimentoNumeroLinea' => 1,
    // All "IdDocumento" somewhere inside DatiGenerali
    'IdDocumento' => 4455,
    // All "NumItem" somewhere inside DatiGenerali
    'NumItem' => 1
));

Set values from an assoc array:

$array =  array(
    'DatiAnagraficiVettore' => array(
        'IdFiscaleIVA' => array(
            'IdPaese' => 'IT',
            'IdCodice' => '09876543210'
        ),
        'Anagrafica' => array(
            'Denominazione' => 'TRASPORTO SRLS'
        ),
        'NumeroLicenzaGuida' => 'AA090909'
    ),
    'MezzoTrasporto' => 'Mezzo',
    'CausaleTrasporto' => 'La causale del traporto',
    'NumeroColli' => '1',
    'Descrizione' => 'La descrizione'
);

$invoice->setValuesFromArray('DatiTrasporto', $array);

All but setValueToAll and setValuesToAll methods will throw an exception if $expr/$context don’t return just one element.

Set/Unset stylesheet

Set:

$invoice->setStylesheet('/path/to/xsl');

Unset:

$invoice->unsetStylesheet();

Validate invoice

You need Slamdunk/php-validatore-fattura-elettronica. If you install php-e-invoice-it via-composer, you got it as dependency; otherwise you must download and require it manually.

$invoice->validate();

An exception is thrown (with a message) if the XML is not valid, for example:

DOMDocument::schemaValidateSource(): Element 'DatiTrasmissione': Missing child element(s). Expected is ( CodiceDestinatario ).

Save invoice

Set an optional default destination dir for all invoices:

FatturaElettronica::setDefaultPrefixPath('path/to/dir');

Set an optional destination dir for current invoice:

$invoice->setPrefixPath('path/to/another/dir');

Save invoice:

$invoice->save();

Specify a custom filename:

$invoice->setFilename('my-invoice.xml')->save();

Send invoice to SdI

Setup a \Taocomp\Einvoicing\SdicoopClient\Client object (for connecting to webservice SdIRiceviFile):

use \Taocomp\Einvoicing\SdicoopClient\Client;
use \Taocomp\Einvoicing\SdicoopClient\FileSdIBase;
use \Taocomp\Einvoicing\SdicoopClient\RispostaSdIRiceviFile;

Client::setPrivateKey('/path/to/client.key');
Client::setClientCert('/path/to/client.pem');
Client::setCaCert('/path/to/ca.pem');

$client = new Client(array(
    'endpoint' => 'https://testservizi.fatturapa.it/ricevi_file',
    'wsdl'     => '/path/to/wsdl/SdIRiceviFile_v1.0.wsdl'
));

Send invoice:

$fileSdI = new FileSdIBase();
$fileSdI->load($invoice);
$response = new RispostaSdIRiceviFile($client->RiceviFile($fileSdI));

Notices

Create a new notice

NotificaEsitoCommittente:

$notice = new EsitoCommittente();

Load a notice from file

Set values

// Set some values from invoice, second body:
$notice->setValuesFromInvoice($invoice, 2);

// Set values
$notice->setValue('IdentificativoSdI', 1234567);
$notice->setValue('Esito', EsitoCommittente::EC01);

Set/Unset stylesheet

Set:

$notice->setStylesheet('/path/to/xsl');

Unset:

$notice->unsetStylesheet();

Save notice

// Set filename from invoice
$notice->setFilenameFromInvoice($invoice, '_EC_001');

// Save notice
$notice->save();

Send notice to SdI

Setup a \Taocomp\Einvoicing\SdicoopClient\Client object (for connecting to webservice SdIRiceviNotifica):

use \Taocomp\Einvoicing\SdicoopClient\Client;
use \Taocomp\Einvoicing\SdicoopClient\FileSdI;
use \Taocomp\Einvoicing\SdicoopClient\RispostaSdINotificaEsito;

Client::setPrivateKey('/path/to/client.key');
Client::setClientCert('/path/to/client.pem');
Client::setCaCert('/path/to/ca.pem');

$client = new Client(array(
    'endpoint' => 'https://testservizi.fatturapa.it/ricevi_notifica',
    'wsdl'     => __DIR__ . '/../wsdl/SdIRiceviNotifica_v1.0.wsdl'
));

Send notice:

$fileSdI = new FileSdI();
$fileSdI->load($notice);
$response = new RispostaSdINotificaEsito($client->NotificaEsito($fileSdI));

Tests

From inside the project root dir: ./vendor/bin/phpunit --testdox tests

Credits

We want to thank all contributors of Forum Italia - Fatturazione Elettronica who have shared their snippets and any available info.

Thanks to @Slamdunk for Slamdunk/php-validatore-fattura-elettronica!

License

GPLv3.

php-e-invoice-it's People

Contributors

aded-taocomp avatar albertovincenzi avatar slamdunk avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

php-e-invoice-it's Issues

<Sede>Array</Sede> - Errore nel file xml salvato

Buongiorno, allego uno strano comportamento che sto avendo nella generazione di un xml.
Per comodità passo un array contenente i dati della Sede sia a CedentePrestatore che a CessionarioCommittente.

<?php
$invoice->setValues('CedentePrestatore/Sede', array(
            'Indirizzo' => 'INDIRIZZO',
            'CAP' => '10121',
            'Comune' => 'TORINO',
            'Provincia' => 'TO',
            'Nazione' => 'IT',
        ));
// [..]
$invoice->setValue('CessionarioCommittente/Sede/', array(
            'Indirizzo'=>$this->user->billing_address_1,
            'CAP' => $this->user->billing_postcode,
            'Comune' => $this->user->billing_city,
            'Provincia' =>  $this->search_state($this->user->billing_state),
            'Nazione' => 'IT',
        ));

Il risultato però non è quello sperato.

CedentePrestatore:
image

CessionarioCommittente:
image

Non è un grosso problema perché attribuendo a mano i valori:

<?php
$invoice->setValue('CessionarioCommittente/Sede/Indirizzo', 'INDIRIZZO');
$invoice->setValue('CessionarioCommittente/Sede/NumeroCivico', '00');
$invoice->setValue('CessionarioCommittente/Sede/CAP', '10121');
$invoice->setValue('CessionarioCommittente/Sede/Comune', 'Torino');
$invoice->setValue('CessionarioCommittente/Sede/Provincia', 'TO');
$invoice->setValue('CessionarioCommittente/Sede/Nazione', 'IT');

Funziona tutto a dovere.
Attendo sue.

undefined method Taocomp\Einvoicing\FatturaElettronica::addElementsFrom

Ciao Ale, scusa il disturbo...
Sto provando con il codice nel file Example.
A me ritorna :
Call to undefined method Taocomp\Einvoicing\FatturaElettronica::addElementsFromArray()
Neanche l'editor mi trova la classe =(

Codice dell'errore:

            // Add elements
            // $FatturaElettronica->addElement('PECDestinatario', 'DatiTrasmissione');
            // $FatturaElettronica->setValue('PECDestinatario', '[email protected]');
            // Add elements from array
            $body = $FatturaElettronica->getBody();
            $datiGeneraliDocumento = $FatturaElettronica->getElement('DatiGeneraliDocumento', $body);
            $FatturaElettronica->addElementsFromArray($datiGeneraliDocumento, array(
                'DatiRitenuta' => array(
                    'TipoRitenuta' => '',
                    'ImportoRitenuta' => '23.00',
                    'AliquotaRitenuta' => ''
                )
            ));

validate

Buongiorno,

cercando di validare la fattura, usando $invoice->validate(), ricevo un fatal error quando ovviamente l'xml non è valido, ma questo mi blocca tutto lo script. Non dovrebbe tornare "false" $invoice->validate()?

Grazie

SistemaEmittente (nome applicativo)

L'aggiornamento 2021 ha introdotto nella radice la possibiilità di indicare il SistemaEmittente, a fini statistici e pubblicitari. Sarebbe molto utile poterlo inserire come parametro al constructor dell'oggetto FatturaElettronica.

Allow to add more line of DettagliPagamento

just add these controls?
Something like that:

    /**
     * Add payment detail line item
     */
    public function addPaymentDetailLineItem( int $n, int $bodyIndex = 1 )
    {
        if ($n < 1) {
            return $this;
        }

        if ($bodyIndex < 1) {
            throw new \Exception("Invalid body index '$bodyIndex'");
        }

        $body = $this->getBody($bodyIndex);
        $line = $this->getElement('DatiPagamento', $body);

        for ($i = 0; $i < $n; $i++) {
            $this->addElement($line->cloneNode(true), $body);
        }

        return $this;
    }

    /**
     * Get payment detail line item i
     */
    public function getPaymentDetailLineItem( int $i, int $bodyIndex = 1 )
    {
        $body = $this->getBody($bodyIndex);
        return $this->getElement("DatiPagamento[$i]", $body);
    }

    /**
     * Set number of payment detail line items
     */
    public function setPaymentDetailLineItemCount( int $n, int $bodyIndex = 1 )
    {
        return $this->addPaymentDetailLineItem($n - 1, $bodyIndex);
    } 

Allow invoices and notices to be loaded by contents, not only via-files

Ciao Ale scusa il disturbo.
Abbiamo aggiornato l'applicazione per lavorare esclusivamente con un Filesystem Bucket,
Ci siamo accorti che possiamo passare solamente il contenuto del file e NON la dir.

Esempio funsione load() in FileSdIBase per invio fatture:

       /**
         * Load Signed Invoice from Bucket
         */
        $fileSdI->load(Storage::get($signdir));
        $response = new RispostaSdIRiceviFile($client->RiceviFile($fileSdI));

la funzione GET ci ritorna il contenuto di un file nel Bucket 'PRIVATE': non accessibile.

Ma nella funzione load() file_get_contents ovviamente non può essere instanziato.

    public function load( $invoice )
    {
        if ($invoice instanceOf \Taocomp\Einvoicing\AbstractDocument) {
            $this->NomeFile = $invoice->getFilename();
            $this->File = $invoice->asXML();
        } else if (true === is_readable($invoice)) {
            $this->NomeFile = basename($invoice);
            $this->File = file_get_contents($invoice);
            $this->removeBOM();
        } else {
            throw new \Exception("Invalid file or object '$invoice'");
        }

        return $this;
    }

Anche noi abbiamo dovuto modificare delle librerie perchè non possono più accedere ai file direttamente.

ora, Come facciamo ? Io posso anche modificare la classe, ma le modifiche le perderei all'aggiornamento.

Potresti aggiungere per esempio loadContents() ???
Io l'ho dovuta aggiungere manualmente in DUE FILE:

  • FileSdI.php

    /**
     * @param $filename
     * @param $filecontents
     * @return $this|FileSdIBase
     * @throws \Exception
     */
    public function loadContents($filename, $filecontents)
    {
        parent::loadContents($filename, $filecontents);

        $xml = simplexml_load_string($filecontents);

        if (!$xml->IdentificativoSdI) {
            throw new \Exception("Cannot find 'IdentificativoSdI' File Contents");
        }

        $this->IdentificativoSdI = $xml->IdentificativoSdI;

        return $this;

    }
  • FileSdIBase.php

    /**
     * @param $filename
     * @param $filecontents
     * @return $this
     * @throws \Exception
     */
    public function loadContents($filename, $filecontents)
    {
        $this->NomeFile = $filename;
        $this->File = $filecontents;
        $this->removeBOM();

        return $this;
    }

Ovviamente se l'aggiungi, magari su tutte le funzioni che richiedono una dir file di avere anche la possibilità di passare direttamente anche il contenuto. 😘😘

Aspetto tue, Salutissimi!

DatiDDT - Problema Array "RiferimentoNumeroLinea"

Salve,
non riesco a capire se c'è un errore oppure il campo è singolo.

Al momento con lo script riesco a generare il semplice Array :

<DatiDDT>
        <NumeroDDT>1</NumeroDDT>
        <DataDDT>2020-07-08</DataDDT>
        <RiferimentoNumeroLinea>1</RiferimentoNumeroLinea>
</DatiDDT>

avrei però bisogno di specificare più linee del tipo 👍

<DatiDDT>
        <NumeroDDT>1</NumeroDDT>
        <DataDDT>2020-07-08</DataDDT>
        <RiferimentoNumeroLinea>1</RiferimentoNumeroLinea>
        <RiferimentoNumeroLinea>2</RiferimentoNumeroLinea>
        <RiferimentoNumeroLinea>3</RiferimentoNumeroLinea>
</DatiDDT>

Grazie in anticipo !

aggiungere multipli tag "AltriDatiGestionali"

nell'aggiunta tutto funziona ma nel salvataggio viene lasciato solo l'ultimo tag inserito... esempio:

1 ITEM 12345 PROVA 1.00 NR 176.50 176.50 22.00 PIPPO VALORE TESTO PIPPO PLUTO VALORE TESTO PLUTO 2023-12-20 PAPERINO VALORE TESTO PAPERINO 2024-01-12

diventa:

1 ITEM 12345 PROVA 1.00 NR 176.50 176.50 22.00
			<AltriDatiGestionali>
				<TipoDato>PAPERINO</TipoDato>
				<RiferimentoTesto>VALORE TESTO PAPERINO</RiferimentoTesto>
				<RiferimentoData>2024-01-12</RiferimentoData>
			</AltriDatiGestionali>
		</DettaglioLinee>

Taocomp\Einvoicing\AbstractDocument::setValue(): unterminated entity reference C.

Ciao ale, scusa ancora.
Oggi abbiamo riscontrato questo ISSUE:
Se mettiamo come ragione sociale dei dati con caratteri speciali :

"CedentePrestatore/DatiAnagrafici"
array:5 [▼
  "IdFiscaleIVA/IdPaese" => "IT"
  "IdFiscaleIVA/IdCodice" => "00000000000"
  "CodiceFiscale" => "00000000000"
  "Anagrafica/Denominazione" => "TELONERIA EUGUBINA SNC DI CERBELLA F. & C."
  "RegimeFiscale" => "RF01"
]

Per correggere il bug, abbiamo dovuto rimuovere i caratteri speciali (. & .):

"CedentePrestatore/DatiAnagrafici"
array:5 [▼
  "IdFiscaleIVA/IdPaese" => "IT"
  "IdFiscaleIVA/IdCodice" => "00000000000"
  "CodiceFiscale" => "00000000000"
  "Anagrafica/Denominazione" => preg_replace("/[^a-zA-Z ]/", "", "TELONERIA EUGUBINA SNC DI CERBELLA F. & C.")
  "RegimeFiscale" => "RF01"
]

Potresti aggiornare anche questo ? 😘😘

Call to undefined method DOMNodeList::count()

Buongiorno Alessandro,

segnalo la presenza due errori identici che hanno a che fare con il count() dei record restituiti da una query, ho corretto localmente il il file src/FatturaElettronica.php alla riga 477 come segue:

$causaleCount = count($this->query('DatiGeneraliDocumento/Causale', $body));

e il file src/AbstractDocument.php alla riga 443 come segue:

$count = count($elements);

A me interessa solo la generazione del file XML quindi non sono certo che il problema non si presenti altrove, ma volevo segnalartelo sperando di poterti essere d'aiuto.

Effettuate queste due semplici modifiche il tuo script funziona alla perfezione :)

Se ti può essere di qualche utilità, sto utilizzando Debian 8.11 con ISPConfig e PHP 7.1.2 compilato da noi.

Buona giornata, buon lavoro e BUON 2019!

Aggiunta DatiRiepilogo[$i]

Stavo provando a generare più linee, ed ho visto che in alcuni file xml di altri programmi, ci sono i

i DatiRiepilogo quante volte sono le DettaglioLinee. ho provato questo, ma non va =(

        foreach ($invoice->field()->get() as $key => $field) {
            /** Dettaglio linee, foreach Invoice as Field */
            $FatturaElettronica->setValues('DettaglioLinee[' . ($key + 1) . ']', array(
                'NumeroLinea' => ($key + 1),
                'Descrizione' => $field->description,
                'Quantita' => $field->quantity . '.00',
                'PrezzoUnitario' => $field->amount,
                'PrezzoTotale' => $field->total,
                'AliquotaIVA' => $field->vat . '.00',
            ));
            /** Dati Riepilogo, foreach Invoice as Field */
            $FatturaElettronica->setValues('DatiRiepilogo[' . ($key + 1) . ']', array(
                'AliquotaIVA' => $field->vat . '.00',
                'ImponibileImporto' => $field->total,
                'Imposta' => $field->total_vat,
                'EsigibilitaIVA' => ($invoice->split_pyment > 0) ? 'S' : 'I'
            ));
        }

Supporto a php 8

Salve,

Ho provato a installare questo pacchetto con php 8 e phpunit 9.3.3, ma da un problema di incompatibilita.

[InvalidArgumentException] Package taocomp/php-e-invoice-it has a PHP requirement incompatible with your PHP version, PHP extensions and C omposer version

Sarebbe possibile aggiornarlo per renderlo compatibile?

Hello From Istanbul

Hi Alessandro and Filippo,

I'm working for a e-invoicing service provider in Turkey. We'd like to start electronic invoicing business in Italy. Since you created an electronic invoicing solution, you must know the sector.

Do you mind giving me about half an hour of your time to discuss how e-invoicing works in Italy and possible partnership opportunities. Hope to hear from you. I'll be in Milan on November 8th.

Invoice 1.5 schema support

As far as I understand, this module support the 1.2 format.
Are you going to support also new 1.5 and maybe 1.6 (that should be released within this year?)

If I try to validate current generated xml with validate() function, I get
DOMDocument::schemaValidateSource(): Invalid Schema

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.