[Contenu du PDF encodé en Base64]
@endcode
*
*/
class InvoiceUBL21 extends XMLInvoice {
const EXTRA_PARAMETER = ["INVOICE_EMAIL_COMPANY"
, 'INVOICE_CONTACT_NAME'
, 'COMPANY_LEGAL_ENTITY'
, 'COMPANY_LEGAL_REGISTRATION'
, 'COMPANY_BANK_IBAN'
, 'COMPANY_BANK_BIC'
, 'MY_COUNTRY_CODE'
, 'MY_NAME'
, 'MY_STREET'
, 'MY_CITY'
, 'MY_TVA'
, 'INVOICE_EMAIL_COMPANY'
,'COMPANY_PEPPOL_ID'
];
protected $pdf_filename; //!< PDF file to insert into XML,
// it is the file on the filesystem
public function get_pdf_filename() {
return $this->pdf_filename;
}
/**
* @brief display_error display a warning with all error
*/
function display_error()
{
$a_error=$this->verify();
include NOALYSS_TEMPLATE."/xmlinvoice-display_error.php";
}
/**
* @brief check that mandatory info are saved in the DB for company (seller)
* @param $a_error (array) array of errors, empty if nothing found
*/
function check_company_data() {
// var $a_error (array) contains the errors for the company
$a_error=array();
$company = $this->load_noalyss_parameter();
foreach (InvoiceUBL21::EXTRA_PARAMETER as $item) {
if (!isset($company[$item]) || trim($company[$item]) == '') {
$a_error[]=$item;
}
}
/**
* check that PEPPOL ID is valid
*/
if ( isset($company['COMPANY_PEPPOL_ID']))
{
if ( strpos($company['COMPANY_PEPPOL_ID'],':') == 0 )
{
$a_error[]="COMPANY_PEPPOL_ID";
}
}
return $a_error;
}
/**
* @brief check that mandatory info are saved in the DB for customer
*/
function check_customer_data(){
$a_error=array();
$a_needed=[ATTR_DEF_NAME=>'name'
,ATTR_DEF_ADRESS=>'street'
,ATTR_DEF_POSTCODE=>'postalzone'
,ATTR_DEF_CITY=>'city'
,ATTR_DEF_COUNTRY_CODE=>'country'
,ATTR_DEF_NUMTVA=>'customer_vat_id'
,ATTR_DEF_PEPPOLID=>'endpoint_id'
];
foreach ($a_needed as $item=>$value) {
if ( trim($this->data['customer'][$value])=="") {
$a_error[]=$value;
}
}
if ( $this->data['customer']["endpoint_id"] != "")
{
// check if peppol id has the form 9999:9999...
list($scheme_id,$peppol)=explode(":", $this->data['customer'][$value]);
if (preg_replace('/[0-9]/', '', $scheme_id) != "")
{
$a_error[]=ATTR_DEF_PEPPOLID;
}elseif(\noalyss_trim($peppol) =="")
{
$a_error[]=ATTR_DEF_PEPPOLID;
}
}
return $a_error;
}
/**
* @brief transform an operation ($jr_id) into an array, which contains
* needed information for making an e-invoice
* @param $jr_id (int) operation JRN.JR_ID
* @return array with all info7
* @param type $jr_id
* @see XMLInvoice::build_data
*/
function build_data($jr_id): array
{
$this->data=parent::build_data($jr_id);
return $this->data;
}
/**
* @brief Information customer
*/
function build_customer()
{
$customer=$this->createElement('cac:AccountingCustomerParty');
$customer_party=$customer->appendChild($this->createElement('cac:Party'));
list($scheme_id,$peppol)=explode( ":",$this->data['customer']['endpoint_id']);
$customer_party->appendChild($this->createElement('cbc:EndpointID',$peppol))
->setAttribute('schemeID', $scheme_id);
$party_name=$this->createElement('cac:PartyName');
$party_name->appendChild($this->createElement("cbc:Name", $this->data['customer']['name']));
$customer_party->appendChild($party_name);
$postal_address=$customer_party->appendChild($this->createElement('cac:PostalAddress'));
$postal_address->appendChild($this->createElement("cbc:StreetName", $this->data['customer']['street']));
$postal_address->appendChild($this->createElement("cbc:CityName", $this->data['customer']['city']));
$postal_address->appendChild($this->createElement("cbc:PostalZone", $this->data['customer']['postalzone']));
///@todo customer = countryCode doit être dans les paramètres (voir upgrade.sql)
$country_code =$this->data['customer']['country'];
$country=$postal_address->appendChild($this->createElement("cac:Country"));
$country->appendChild($this->createElement('cbc:IdentificationCode',$country_code));
$postal_address->appendChild($country);
// Tax Schem
$tax=$this->createElement('cac:PartyTaxScheme');
$tax->appendChild($this->createElement('cbc:CompanyID',$this->data["customer"]['customer_vat_id']));
$tax_scheme=$this->createElement('cac:TaxScheme');
$tax_scheme->appendChild($this->createElement('cbc:ID',"VAT"));
$tax->appendChild($tax_scheme);
// LegalEntity
$ple=$this->createElement('cac:PartyLegalEntity');
///@todo customer = name doit être fiche
$ple->appendChild($this->createElement("cbc:RegistrationName", $this->data['customer']['name']??"ERROR"));
///@todo customer_vat_id = numéro de TVA doit être dans fiche
$ple->appendChild($this->createElement("cbc:CompanyID", $this->data['customer']['customer_vat_id']??"ERROR"));
// assemble supplier
$customer_party->appendChild($tax);
$customer_party->appendChild($ple);
$customer->appendChild($customer_party);
return $customer;
}
/**
* @brief Build XML Block for payment
* @code
30
Invoice 2019000005
BE54000000000097
BPOTBEB1
* @endcode
*/
function build_paymentInfo()
{
$company = $this->load_noalyss_parameter();
$payment=$this->createElement("cac:PaymentMeans");
$payment->appendChild($this->createElement('cbc:PaymentMeansCode',30));
///@note cbc:PaymentID est la communication lors du paiement
$payment->appendChild($this->createElement('cbc:PaymentID',$this->data["info"]['communication']));
$f=$this->createElement ('cac:PayeeFinancialAccount');
///@todo customer = IBAN doit être dans les paramètres (voir upgrade.sql)
$f->appendChild($this->createElement("cbc:ID",$company['COMPANY_BANK_IBAN']));
$g=$this->createElement("cac:FinancialInstitutionBranch");
$g->appendChild($this->createElement("cbc:ID", $company['COMPANY_BANK_BIC']));
$f->appendChild($g);
$payment->appendChild($f);
return $payment;
}
/**
* @brief Information supplier
*/
function build_supplier()
{
$company = $this->load_noalyss_parameter();
$supplier=$this->createElement('cac:AccountingSupplierParty');
$supplier_party=$supplier->appendChild($this->createElement('cac:Party'));
list($scheme_id,$peppol)=explode( ":",$company['COMPANY_PEPPOL_ID']);
$supplier_party->appendChild($this->createElement('cbc:EndpointID',$peppol))
->setAttribute('schemeID', $scheme_id);
//$supplier_party->appendChild($this->createElement('cbc:EndpointID',$this->data["supplier"]['supplier_vat_id']))->setAttribute('schemeID', 9925);
$party_name=$this->createElement('cac:PartyName');
$party_name->appendChild($this->createElement('cbc:Name', $this->data['supplier']['name']));
$supplier_party->appendChild($party_name);
$postal_address=$supplier_party->appendChild($this->createElement('cac:PostalAddress'));
$postal_address->appendChild($this->createElement("cbc:StreetName", $this->data['supplier']['street']));
$postal_address->appendChild($this->createElement("cbc:CityName", $this->data['supplier']['city']));
$postal_address->appendChild($this->createElement("cbc:PostalZone", $this->data['supplier']['postalzone']));
$country_code = $company['MY_COUNTRY_CODE'];
$country=$postal_address->appendChild($this->createElement("cac:Country"));
$country->appendChild($this->createElement('cbc:IdentificationCode',$country_code??"ERROR"));
$postal_address->appendChild($country);
// Tax Schem
$tax=$this->createElement('cac:PartyTaxScheme');
$tax->appendChild($this->createElement('cbc:CompanyID',$this->data["supplier"]['supplier_vat_id']));
$tax_scheme=$this->createElement('cac:TaxScheme');
$tax_scheme->appendChild($this->createElement('cbc:ID',"VAT"));
$tax->appendChild($tax_scheme);
// LegalEntity
$ple=$this->createElement('cac:PartyLegalEntity');
$ple->appendChild($this->createElement("cbc:RegistrationName", $company['COMPANY_LEGAL_REGISTRATION']??"ERROR"));
$ple->appendChild($this->createElement("cbc:CompanyID", $this->data['supplier']['supplier_vat_id']??"ERROR"));
$ple->appendChild($this->createElement("cbc:CompanyLegalForm", $company['COMPANY_LEGAL_ENTITY']??"ERROR"));
$contact=$this->createElement('cac:Contact');
$contact->appendChild($this->createElement("cbc:Name",$company['INVOICE_CONTACT_NAME']??"ERROR"));
$contact->appendChild($this->createElement("cbc:ElectronicMail",$company['INVOICE_EMAIL_COMPANY']??"ERROR"));
// assemble supplier
$supplier_party->appendChild($tax);
$supplier_party->appendChild($ple);
$supplier_party->appendChild($contact);
$supplier->appendChild($supplier_party);
return $supplier;
}
/**
* @brief TaxTotal Block
* @code
516
2400
504
S
21
VAT
200
12
S
6
if ID !=Z and ID != S
VAT
* @endcode
*/
function build_taxTotal()
{
$taxTotal=$this->createElement("cac:TaxTotal");
$taxTotal->appendChild($this->createElement('cbc:TaxAmount',sprintf("%.2f",$this->data['TaxAmount'])))
->setAttribute("currencyID",$this->data['currency']);
// for subTotal
$subTotal=$this->data['subTotalVAT'];
$nb_sub=count($subTotal);
for ($i=0;$i<$nb_sub;$i++) {
$subTotalXML=$this->createElement("cac:TaxSubtotal");
$subTotalXML->appendChild($this->createElement('cbc:TaxableAmount',sprintf("%.2f",$subTotal[$i]['amount'])))
->setAttribute("currencyID", $this->data['currency']);
$subTotalXML->appendChild($this->createElement('cbc:TaxAmount',sprintf("%.2f",$subTotal[$i]['vat'])))
->setAttribute("currencyID",$this->data['currency']);
$taxCategory=$this->createElement("cac:TaxCategory");
$taxCategory->appendChild($this->createElement("cbc:ID",$subTotal[$i]['vat_code']));
$taxCategory->appendChild($this->createElement("cbc:Percent",sprintf("%.2f",$subTotal[$i]['percent'])));
if ($subTotal[$i]['vat_code'] != "S"
&& $subTotal[$i]['vat_code'] != "Z")
{
// if cbc:ID not S and not Z then exemption VAT code is needed
$taxCategory->appendChild($this->createElement("cbc:TaxExemptionReasonCode",$subTotal[$i]['vatex']));
}
$taxScheme=$this->createElement("cac:TaxScheme");
$taxScheme->appendChild($this->createElement("cbc:ID", "VAT"));
$taxCategory->appendChild($taxScheme);
$subTotalXML->appendChild($taxCategory);
$taxTotal->appendChild($subTotalXML);
}
return $taxTotal;
}
/**
* @brief legalMonetaryTotal
* @code
2600
2600
3116
3116
* @endcode
*/
function build_legalMonetaryTotal()
{
$result=$this->createElement('cac:LegalMonetaryTotal' );
$result->appendChild($this->createElement("cbc:LineExtensionAmount",sprintf("%.2f",$this->data['LineExtensionAmount'])))
->setAttribute("currencyID",$this->data['currency']);
$result->appendChild($this->createElement("cbc:TaxExclusiveAmount",sprintf("%.2f",$this->data['TaxExclusiveAmount'])))
->setAttribute("currencyID",$this->data['currency']);
$result->appendChild($this->createElement("cbc:TaxInclusiveAmount",sprintf("%.2f",$this->data['TaxInclusiveAmount'])))
->setAttribute("currencyID", $this->data['currency'] );
$result->appendChild($this->createElement("cbc:PayableAmount",sprintf("%.2f",$this->data['PayableAmount'])))
->setAttribute("currencyID", $this->data['currency'] );
return $result;
}
/**
* @brief cac:InvoiceLine
* @code
2
10
200
Good X
S
6
VAT
20
* @endcode
* @param $i (int) idx f $this->data->operation[$i]
*
*/
function build_invoiceLine($i)
{
$result=$this->createElement('cac:InvoiceLine');
$row=$this->data["operation"][$i];
$amount=sprintf("%.2f",$row['price']);
$result->appendChild($this->createElement("cbc:ID", $i));
$amount=sprintf("%.2f",$row['price']);
$result->appendChild(
$this->createElement("cbc:InvoicedQuantity", sprintf("%.2f",$row['quantity'])))
->setAttribute("unitCode", $row["code_quantity"]);
$result->appendChild($this->createElement("cbc:LineExtensionAmount", $amount))
->setAttribute("currencyID",$this->data['currency']);
// ITEM
$item=$this->createElement("cac:Item");
$item->appendChild($this->createElement("cbc:Description",$row['name']));
$item->appendChild($this->createElement("cbc:Name", $row['qcode']));
$classifiedTaxCat=$this->createElement("cac:ClassifiedTaxCategory");
//cbc:ID S = standard rate
/// see TVA_RATE.TVA_PEPPOL_CODE & C0TVA
$classifiedTaxCat->appendChild($this->createElement("cbc:ID", $row['vat_code']));
$classifiedTaxCat->appendChild($this->createElement("cbc:Percent", sprintf("%.2f",$row['vat_percent'])));
$tax_scheme=$this->createElement('cac:TaxScheme');
$tax_scheme->appendChild($this->createElement("cbc:ID", "VAT"));
$classifiedTaxCat->appendChild($tax_scheme);
$item->appendChild($classifiedTaxCat);
$result->appendChild($item);
$price=$result->appendChild($this->createElement("cac:Price"));
$price->appendChild($this->createElement("cbc:PriceAmount",sprintf("%.2f",abs($row['price_unit']))))
->setAttribute("currencyID",$this->data['currency']);
$result->appendChild($price);
return $result;
}
/**
* @brief Insert a PDF in the XML
* the document type is not needed for BELGIUM
*/
/**
*
@code
P01
Facture PDF
$base64Pdf
REF_ODT_001
Fichier OpenDocument
[base64-encodage du fichier]
@endcode
* @return \DOMElement
*/
function build_Invoice():\DOMElement
{
if ( $this->pdf_filename == "") return null;
/** $pdf_filename = 'chemin/vers/votre/fichier.pdf';*/
static $i=0;
$i++;
if ( $this->pdf_filename == null ) {
return null;
}
// Lire le fichier PDF
$pdfContent = file_get_contents( $this->pdf_filename );
$result=$this->createElement("cac:AdditionalDocumentReference");
$id=$this->createElement("cbc:ID",$i);
$document_description=$this->createElement("cbc:DocumentDescription"
, $this->data['description']);
// PDF in base64
$base64Pdf = base64_encode($pdfContent);
$embeddedDocument=$this->createElement("cbc:EmbeddedDocumentBinaryObject",$base64Pdf);
$embeddedDocument->setAttribute("mimeCode", "application/pdf");
$embeddedDocument->setAttribute("filename", "facture.pdf");
$attachment=$this->createElement("cac:Attachment");
$attachment->appendChild($embeddedDocument);
$result->appendChild($id);
$result->appendChild($document_description);
$result->appendChild($attachment);
return $result;
}
/**
* @brief create an XML invoice(UBL2.1) based on JRN.JR_ID operation
* @parameter $jr_id (int) operation JRN.JR_ID operation
* @return XML String
*/
function make_xml($jr_id)
{
$this->data = $this->build_data($jr_id);
// var $company (array) all the parameters of the company
$company = $this->load_noalyss_parameter();
// HEADER
$root=$this->createElementNS('urn:oasis:names:specification:ubl:schema:xsd:Invoice-2',"Invoice",);
$root->setAttributeNS('http://www.w3.org/2000/xmlns/',"xmlns:cac", "urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2");
$root->setAttributeNS('http://www.w3.org/2000/xmlns/',"xmlns:cbc", "urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2");
$root->appendChild($this->createElement('cbc:CustomizationID',"urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0"));
$root->appendChild($this->createElement('cbc:ProfileID',"urn:fdc:peppol.eu:2017:poacc:billing:01:1.0"));
$root->appendChild($this->createElement('cbc:ID',$this->data['id']));
$root->appendChild($this->createElement('cbc:IssueDate',$this->data['issue_date']));
if ($this->data ['due_date'] == '')
{
$this->data ['due_date']=$this->data['issue_date'];
}
$root->appendChild($this->createElement('cbc:DueDate',$this->data['due_date']));
$root->appendChild($this->createElement('cbc:InvoiceTypeCode',380));
$root->appendChild($this->createElement('cbc:DocumentCurrencyCode',$this->data['currency']));
$root->appendChild($this->createElement('cbc:BuyerReference',$this->data['info']['order']));
/**
* insert PDF in the XML
*/
$x = $this->build_Invoice();
if ($x != null ) {
$root->appendChild($x);
}
// add the supplier
$root->appendChild($this->build_supplier());
// add the customer
$root->appendChild($this->build_customer());
// add the payment if there is a bank account
if ( $company['COMPANY_BANK_IBAN'] != "")
$root->appendChild($this->build_paymentInfo());
// Add cac:TaxTotal
$root->appendChild($this->build_taxTotal());
// Add cac:LegalMonetatyTotal
$root->appendChild($this->build_legalMonetaryTotal());
// add all the invoiceline
// operation
$nb_operation=count($this->data["operation"]);
for ($i=0;$i < $nb_operation ; $i++) {
$root->appendChild($this->build_invoiceLine($i));
}
$this->append($root);
$this->formatOutput=true;
return $this->saveXML();
}
/**
* @brief create the invoice in the right format, with PDF if any
* @param $operation_id (int) JRN.JR_ID
* @return string XML Invoice
*/
function create_invoice($operation_id) {
return $this->make_xml($operation_id);
}
}