E-INVOICE: check data before generating

E-INVOICE: add missing for Belgium : BuyerReference and Due_Date
This commit is contained in:
sparkyx 2025-09-13 13:16:14 +02:00
parent d776c7bf98
commit 9ebb89b38a
8 changed files with 520 additions and 117 deletions

View file

@ -0,0 +1,136 @@
<?php
namespace Noalyss\XMLDocument;
/*
* This file is part of NOALYSS.
*
* NOALYSS is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* NOALYSS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with NOALYSS; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
// Copyright Author Dany De Bontridder danydb@aevalys.eu 22/10/23
/**
* @file
* @brief Give the error message thanks the code for FacturX and UBL21
*
*/
/**
* @class
* @brief Give the error message thanks the code for FacturX and UBL21
*
property $a_error (double array)
keys :
- general ,
- customer,
- ATTR_DEF_NAME=>'CUST_NAME'
- ATTR_DEF_ADRESS=>'CUST_ADDR'
- ATTR_DEF_POSTCODE=>'CUST_POSTCD'
- ATTR_DEF_CITY=>'CUST_CITY'
- ATTR_DEF_COUNTRY_CODE=>'CUST_CDCOUNTRY'
- ATTR_DEF_NUMTVA=>'CUST_VAT'
- ATTR_DEF_PEPPOLID=>'CUST_PEPPOLID'
- company,
- "INVOICE_EMAIL_COMPANY" company's email
- 'INVOICE_CONTACT_NAME' contact name
- 'COMPANY_LEGAL_ENTITY' legal form of company
- 'COMPANY_LEGAL_REGISTRATION' full name
- 'COMPANY_BANK_IBAN' IBAN bank account
- 'COMPANY_BANK_BIC' BIC bank account
- 'COMPANY_UBL_ID' PEPPOL id
- 'MY_COUNTRY_CODE' country code (normally BE)
- 'MY_NAME' short company name
- 'MY_STREET' address
- 'MY_CITY' address
- 'MY_TVA' VAT number
*
*/
class Error_Message
{
private $a_error;
private $a_message_company;
private $a_message_customer;
/**
* @brief constructo
* @param $a_error (double array)
*/
public function __construct($a_error)
{
$this->a_error = $a_error;
$this->a_message_company = array(
"INVOICE_EMAIL_COMPANY" => _("L'email de la société ")
, 'INVOICE_CONTACT_NAME' => _("Nom du contact")
, 'COMPANY_LEGAL_ENTITY' => _("Type de société (SRL,ASBL,...)")
, 'COMPANY_LEGAL_REGISTRATION' => _("Nom complet de la société")
, 'COMPANY_BANK_IBAN' => _("Compte en banque (IBAN) de la société")
, 'COMPANY_BANK_BIC' => _("Code BIC de compte en banque")
, 'COMPANY_UBL_ID' => _("Identifiant PEPPOL")
, 'MY_COUNTRY_CODE' => _('Code Pays')
, 'MY_NAME' => _("Nom de la société")
, 'MY_STREET' => _("Adresse de la société")
, 'MY_CITY' => _("Ville")
, 'MY_TVA' => _("Numéro de TVA")
);
$this->a_message_customer = array(
'name' => _("Nom")
, 'street' => _("Adresse")
, 'postalzone' => _("Code postal")
, 'city' => _("Ville")
, 'country'=>_("Code pays")
, 'customer_id' => _("Numéro de TVA")
, 'endpoint_id' => _('Identifiant PEPPOL')
);
}
public function get_a_error()
{
return $this->a_error;
}
public function set_a_error($a_error)
{
$this->a_error = $a_error;
return $this;
}
/**
* @brief returns the text of an error
* @param $code (string or number)
* @param $type (string) customer , general or company
*/
function get_message_error($code, $type)
{
if ($type == "customer")
{
$a_error = $this->a_error['customer'];
$a_message = $this->a_message_customer;
} else if ($type == "company")
{
$a_error = $this->a_error['company'];
$a_message = $this->a_message_company;
} else
{
throw new \Exception("EM116: unknow type");
}
return $a_message[$code];
}
}

View file

@ -54,18 +54,22 @@ class InvoiceUBL21 extends XMLInvoice {
, 'MY_STREET'
, 'MY_CITY'
, 'MY_TVA'
, 'INVOICE_EMAIL_COMPANY'
];
protected $pdf_filename; //!< PDF file to insert into XML, it is the file on the filesystem
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 returns the text of an error
* @brief display_error display a warning with all error
*/
function get_message_error($a_code)
function display_error()
{
$a_error=$this->verify();
include NOALYSS_TEMPLATE."/invoiceUBL21-display_error.php";
}
/**
* @brief check that all the data are correct
* @returns null : no errors, string separated with comma of error code
@ -74,15 +78,17 @@ class InvoiceUBL21 extends XMLInvoice {
*/
function verify()
{
///@var $a_error : array of error_code see check_company_error
$a_error=[];
// verify all VAT
$x=parent::verify();
// verify that all needed data in PARAMETER are valid
$this->check_company_data($a_error);
$this->check_customer_data($this->data['customer']['customer_id'], $a_error);
return $a_error;
// verify all VAT
///@var $a_error : array of error_code see check_company_error
$a_error = array();
$a_error['general'] = parent::verify();
$a_error['operation']=[];
// verify that all needed data in PARAMETER are valid
$a_error['company'] = $this->check_company_data();
$a_error['customer'] = $this->check_customer_data($this->data['customer']['card_id']);
return $a_error;
}
/**
* @brief set the PDF
@ -102,17 +108,16 @@ class InvoiceUBL21 extends XMLInvoice {
* @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(&$a_error) {
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]) || $company[$item] == '') {
if (!isset($company[$item]) || trim($company[$item]) == '') {
$a_error[]=$item;
}
}
if (count($a_error) == 0) {
return true;
}
return false;
return $a_error;
}
/**
* @brief check that mandatory info are saved in the DB for customer
@ -120,26 +125,49 @@ class InvoiceUBL21 extends XMLInvoice {
* @param $a_error (array) array of errors, empty if nothing found
* @todo : country code au lieu de country !!
*/
function check_customer_data($customer_id,&$a_error){
$card=new \Fiche($this->cn,$customer_id);
$a_needed=[ATTR_DEF_NAME=>'CUST_NAME'
,ATTR_DEF_ADRESS=>'CUST_ADDR'
,ATTR_DEF_POSTCODE=>'CUST_POSTCD'
,ATTR_DEF_CITY=>'CUST_CITY'
,ATTR_DEF_COUNTRY_CODE=>'CUST_CDCOUNTRY'
,ATTR_DEF_NUMTVA=>'CUST_VAT'
,ATTR_DEF_PEPPOLID=>'CUST_PEPPOLID'
function check_customer_data($customer_id){
$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_id'
,ATTR_DEF_PEPPOLID=>'endpoint_id'
];
foreach ($a_needed as $item=>$value) {
if (\noalyss_trim($card->get_attribute($item))=="") {
$a_error[]=$value;
if ( $this->data['customer'][$value]=="") {
$a_error[]=$value;
}
}
return $a_error;
}
/**
* @brief check that the VAT is using a PEPPOL Code
*/
function check_VAT()
{
$a_error=array();
$nb_operation=count($this->data['operation']);
for ($i=0;$i <$nb_operation;$i++)
{
if ( $this->data['operation'][$i]['vat_code'] == "" ) {
$card=new \Fiche(
$this->cn
,$this->data['operation'][$i]['card_id']
);
$tva= \Acc_Tva::build($this->cn, $this->data['operation'][$i]['vat_id']);
$a_error[]=sprintf(_("%s : %s code TVA pour PEPPOL non configuré code TVA [ %s %s ]")
, $i
, $card->get_quick_code()
,$tva->tva_id
,$tva->tva_code
);
}
}
if (count($a_error) == 0) {
return true;
}
return false;
return $a_error;
}
/**
* @brief transform an operation ($jr_id) into an array, which contains
@ -174,7 +202,7 @@ class InvoiceUBL21 extends XMLInvoice {
$VAT_SubTotal=array();
for ($i=0;$i < $nb_operation;$i++) {
$acc_tva=\Acc_TVA::build($this->cn,$result['operation'][$i]['vat_id'] );
$percent = bcmul($acc_tva->tva_rate,100);
$percent = bcmul($acc_tva->tva_rate,100,2);
// subtotal for VAT
$n = find_idx($VAT_SubTotal,'percent',$percent);
if ($n == -1 ) {
@ -213,7 +241,8 @@ class InvoiceUBL21 extends XMLInvoice {
$customer=$this->createElement('cac:AccountingCustomerParty');
$customer_party=$customer->appendChild($this->createElement('cac:Party'));
///@todo EndPointID doit être dans les paramètres (voir upgrade.sql)
$customer_party->appendChild($this->createElement('cbc:EndpointID',"ERROR"))->setAttribute('schemeID', 9956);
$customer_party->appendChild($this->createElement('cbc:EndpointID',$this->data['customer']['endpoint_id']))
->setAttribute('schemeID', 9925);
$party_name=$this->createElement('cac:PartyName');
$party_name->appendChild($this->createElement("cbc:Name", $this->data['customer']['name']));
$customer_party->appendChild($party_name);
@ -222,9 +251,10 @@ class InvoiceUBL21 extends XMLInvoice {
$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 ="ERROR";
$country_code =$this->data['customer']['country'];
$country=$postal_address->appendChild($this->createElement("cac:Country"));
$country->appendChild($this->createElement('cbc:IdentificationCode',$country_code??"ERROR:COUNTRY_CODE"));
$country->appendChild($this->createElement('cbc:IdentificationCode',$country_code));
$postal_address->appendChild($country);
// Tax Schem
@ -290,7 +320,7 @@ class InvoiceUBL21 extends XMLInvoice {
$supplier=$this->createElement('cac:AccountingSupplierParty');
$supplier_party=$supplier->appendChild($this->createElement('cac:Party'));
$supplier_party->appendChild($this->createElement('cbc:EndpointID',$company['COMPANY_UBL_ID']??"ERROR"))->setAttribute('schemeID', 9956);
$supplier_party->appendChild($this->createElement('cbc:EndpointID',$company['COMPANY_UBL_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);
@ -360,20 +390,20 @@ class InvoiceUBL21 extends XMLInvoice {
function build_taxTotal()
{
$taxTotal=$this->createElement("cac:TaxTotal");
$taxTotal->appendChild($this->createElement('cbc:TaxAmount',$this->data['TaxAmount']))
$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',$subTotal[$i]['amount']))
$subTotalXML->appendChild($this->createElement('cbc:TaxableAmount',sprintf("%.2f",$subTotal[$i]['amount'])))
->setAttribute("currencyID", $this->data['currency']);
$subTotalXML->appendChild($this->createElement('cbc:TaxAmount',$subTotal[$i]['vat']))
$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","S"));
$taxCategory->appendChild($this->createElement("cbc:Percent",$subTotal[$i]['percent']));
$taxCategory->appendChild($this->createElement("cbc:Percent",sprintf("%.2f",$subTotal[$i]['percent'])));
$taxScheme=$this->createElement("cac:TaxScheme");
$taxScheme->appendChild($this->createElement("cbc:ID", "VAT"));
$taxCategory->appendChild($taxScheme);
@ -398,13 +428,13 @@ class InvoiceUBL21 extends XMLInvoice {
function build_legalMonetaryTotal()
{
$result=$this->createElement('cac:LegalMonetaryTotal' );
$result->appendChild($this->createElement("cbc:LineExtensionAmount",$this->data['LineExtensionAmount']))
$result->appendChild($this->createElement("cbc:LineExtensionAmount",sprintf("%.2f",$this->data['LineExtensionAmount'])))
->setAttribute("currencyID",$this->data['currency']);
$result->appendChild($this->createElement("cbc:TaxExclusiveAmount",$this->data['TaxExclusiveAmount']))
$result->appendChild($this->createElement("cbc:TaxExclusiveAmount",sprintf("%.2f",$this->data['TaxExclusiveAmount'])))
->setAttribute("currencyID",$this->data['currency']);
$result->appendChild($this->createElement("cbc:TaxInclusiveAmount",$this->data['TaxInclusiveAmount']))
$result->appendChild($this->createElement("cbc:TaxInclusiveAmount",sprintf("%.2f",$this->data['TaxInclusiveAmount'])))
->setAttribute("currencyID", $this->data['currency'] );
$result->appendChild($this->createElement("cbc:PayableAmount",$this->data['PayableAmount']))
$result->appendChild($this->createElement("cbc:PayableAmount",sprintf("%.2f",$this->data['PayableAmount'])))
->setAttribute("currencyID", $this->data['currency'] );
return $result;
@ -439,29 +469,42 @@ class InvoiceUBL21 extends XMLInvoice {
$result=$this->createElement('cac:InvoiceLine');
$row=$this->data["operation"][$i];
$amount=sprintf("%.2f",abs($row['price']));
$result->appendChild($this->createElement("cbc:ID", $i));
///@todo , les unités de quantités devraient être ajoutés à NOALYSS
/// il faut adapter les fiches
$amount=sprintf("%.2f",abs($row['price']));
$result->appendChild(
$this->createElement("cbc:InvoicedQuantity", $row['quantity']))
$this->createElement("cbc:InvoicedQuantity", sprintf("%.2f",$row['quantity'])))
->setAttribute("unitCode", $row["code_quantity"]);
$result->appendChild($this->createElement("cbc:LineExtensionAmount", $row['price']))
$result->appendChild($this->createElement("cbc:LineExtensionAmount", $amount))
->setAttribute("currencyID",$this->data['currency']);
// @IMPORTANT@ Impossible de connaitre la remise par article, non prévu dans NOALYSS ALLOWANCE
if ( $row['price'] < 0 ) {
$allowanceCharge=$this->createElement("cac:AllowanceCharge");
$allowanceCharge->appendChild($this->createElement("cbc:ChargeIndicator","false"));
$allowanceCharge->appendChild($this->createElement("cbc:Amount",sprintf("%.2f",$amount)));
$result->appendChild($allowanceCharge);
}
// ITEM
$item=$this->createElement("cac:Item");
$card=new \Fiche($this->cn,$row['card_id']);
$item->appendChild($this->createElement("cbc:Name", $card->get_attribute(ATTR_DEF_NAME)));
$item->appendChild($this->createElement("cbc:Description", $card->get_attribute(ATTR_DEF_NAME)));
$item->appendChild($this->createElement("cbc:Name", $card->get_attribute(ATTR_DEF_QUICKCODE)));
$classifiedTaxCat=$this->createElement("cac:ClassifiedTaxCategory");
///@todo cbc:ID S = standard rate et que se passe-t'il pour l'autoliquidation ???
/// Il faut ajouter dans TVA_RATE , un code pour la TVA, voir TVA_RATE.TVA_PEPPOL_CODE
$classifiedTaxCat->appendChild($this->createElement("cbc:ID", "S"));
$classifiedTaxCat->appendChild($this->createElement("cbc:Percent", $row['vat_percent']));
//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", $row['price']))
$price->appendChild($this->createElement("cbc:PriceAmount",sprintf("%.2f",$amount)))
->setAttribute("currencyID",$this->data['currency']);
$result->appendChild($price);
@ -470,6 +513,7 @@ class InvoiceUBL21 extends XMLInvoice {
}
/**
* @brief Insert a PDF in the XML
* the document type is not due for BELGIUM
@code
<cac:AdditionalDocumentReference>
<cbc:ID>P01</cbc:ID>
@ -511,7 +555,6 @@ class InvoiceUBL21 extends XMLInvoice {
$result=$this->createElement("cac:AdditionalDocumentReference");
$id=$this->createElement("cbc:ID",$i);
$documentType=$this->createElement("cbc:DocumentType",'application/pdf');
$document_description=$this->createElement("cbc:DocumentDescription",'INVOICE PDF');
// PDF in base64
@ -523,7 +566,9 @@ class InvoiceUBL21 extends XMLInvoice {
$attachment->appendChild($embeddedDocument);
$result->appendChild($id);
$result->appendChild($documentType);
// ERROR FOR BELGIUM !
// $documentType=$this->createElement("cbc:DocumentType",'application/pdf');
// $result->appendChild($documentType);
$result->appendChild($document_description);
$result->appendChild($attachment);
@ -554,7 +599,7 @@ class InvoiceUBL21 extends XMLInvoice {
}
$root->appendChild($this->createElement('cbc:InvoiceTypeCode',380));
$root->appendChild($this->createElement('cbc:DocumentCurrencyCode',$this->data['currency']));
$root->appendChild($this->createElement('cbc:BuyerReference',"NOALYSS"));
/**
* insert PDF in the XML
*/

View file

@ -1,7 +1,7 @@
<?php
namespace Noalyss\XMLDocument;
use Noalyss\Utility;
//use Noalyss\Utility;
/*
* This file is part of NOALYSS.
*
@ -68,6 +68,7 @@ use Noalyss\Utility;
[vat_id] => 1
[vat_reversed] => 0.0000
[code_quantity]=> EA
[vat_code]=> Code VAT for PEPPOL (S,K,...)
)
[1] => Array
@ -79,6 +80,7 @@ use Noalyss\Utility;
[vat_id] => 1
[vat_reversed] => 0.0000
[code_quantity]=> EA
[vat_code]=> Code VAT for PEPPOL (S,K,...)
)
[2] => Array
@ -90,6 +92,7 @@ use Noalyss\Utility;
[vat_id] => 5
[vat_reversed] => 15.1200
[code_quantity]=> EA
[vat_code]=> Code VAT for PEPPOL (S,K,...)
)
)
@ -165,43 +168,13 @@ abstract class XMLInvoice extends \DOMDocument
$result=array();
$result["id"]= $operation->det->jr_pj_number;
$result["issue_date"]=$operation->det->jr_date;
$result["due_date"]=$operation->det->jr_ech;
$result["due_date"]=($operation->det->jr_ech=="")?$operation->det->jr_date:$operation->det->jr_ech;
// supplier
$result['supplier']=array();
$result['supplier']['name']=$g_parameter->MY_NAME;
$result['supplier']['street']=$g_parameter->MY_STREET;
$result['supplier']['postalzone']=$g_parameter->MY_POSTCODE;
$result['supplier']['city']=$g_parameter->MY_CITY;
$result['supplier']['country']=$g_parameter->MY_COUNTRY;
$result['supplier']['supplier_id']=$g_parameter->MY_TVA;
// official name of the company
$result['supplier']['registration_name']=$g_parameter->MY_NAME;
// official ID , like VAT
$result['supplier']['supplier_id']=str_replace([" ",".","-","/"],"" ,$g_parameter->MY_TVA);
//@todo
//Autre parametre comme email dans Parameter_Extra_SQL
//
//
// == $result['supplier']['supplier_email']=$g_parameter->;
//@todo TESTER S'IL Y A QQ'CHOSE DE VENDU !
$result['supplier']=$this->fill_supplier();
//customer
$customer=new \Fiche($this->cn,$operation->det->array[0]['qs_client']);
$result['customer']=array();
$result['customer']['card_id']=$operation->det->array[0]['qs_client'];
$result['customer']['name']=$customer->get_attribute(ATTR_DEF_NAME);
$result['customer']['street']=$customer->get_attribute(ATTR_DEF_ADRESS);
$result['customer']['postalzone']=$customer->get_attribute(ATTR_DEF_POSTCODE);
$result['customer']['city']=$customer->get_attribute(ATTR_DEF_CITY);
// find country_code of this card
$result['customer']['country']=$customer->get_attribute(ATTR_DEF_COUNTRY);
$result['customer']['customer_id']=str_replace([" ",".","-","/"],"" ,$customer->get_attribute(ATTR_DEF_NUMTVA));
// official name of the company
$result['customer']['registration_name']=$customer->get_attribute(ATTR_DEF_NAME);
// official ID , like VAT
$result['customer']['customer_id']=$customer->get_attribute(ATTR_DEF_NUMTVA);
$result['customer']=$this->fill_customer($operation->det->array[0]['qs_client']);
// currency
$result['currency']=$this->cn->get_value("select cr_code_iso from currency where id=$1"
@ -228,6 +201,11 @@ abstract class XMLInvoice extends \DOMDocument
}
$result['operation'][$i]['vat_id']=$operation->det->array[$i]['qs_vat_code'];
// // tva code for PEPPOL
// $x=$this->cn->get_value("select tva_peppol_code from tva_rate where tva_id=$1"
// ,[ $result['operation'][$i]['vat_id']]);
$result['operation'][$i]['vat_code']=($x=="")?"S":$x;
$result['operation'][$i]['vat_reversed']=$operation->det->array[$i]['qs_vat_sided'];
}
// retrieve currency
@ -237,7 +215,7 @@ abstract class XMLInvoice extends \DOMDocument
/**
* @brief make an array of parameter_extra where pe_code as key and pe_value
* as value
* @return array
* @return array keys : pe_code,pe_value
*/
function load_noalyss_parameter()
{
@ -257,15 +235,13 @@ abstract class XMLInvoice extends \DOMDocument
abstract function make_xml($jr_id);
/**
* @brief check that mandatory info are saved in the DB for company (seller)
* @param $a_error (array) array of errors, empty if nothing found
*/
abstract function check_company_data(&$a_error) ;
abstract function check_company_data() ;
/**
* @brief check that mandatory info are saved in the DB for customer
* @param $customer_id (int) card of the customer FICHE.F_ID
* @param $a_error (array) array of errors, empty if nothing found
*/
abstract function check_customer_data($customer_id,&$a_error) ;
abstract function check_customer_data($customer_id) ;
/**
* @brief create the invoice in the right format, with PDF if any
* @param $operation_id (int) JRN.JR_ID
@ -297,6 +273,118 @@ abstract class XMLInvoice extends \DOMDocument
*/
function verify()
{
return array();
}
/**
* @brief retrieve data from customer and return it into an array
* @param $card_id (int) FICHE.F_ID
* @return array keys :
* - name
* - ,street
* - ,postalzone
* - ,city
* - ,country
* - ,customer_id => VAT Number
* - , registration_name,
* - card_id
*/
function fill_customer($card_id):array
{
$customer =new \Fiche($this->cn,$card_id);
$result=array();
$result['card_id']=$card_id;
$result['name']=$customer->get_attribute(ATTR_DEF_NAME,0);
$result['street']=$customer->get_attribute(ATTR_DEF_ADRESS,0);
$result['postalzone']=$customer->get_attribute(ATTR_DEF_POSTCODE,0);
$result['city']=$customer->get_attribute(ATTR_DEF_CITY,0);
// find country_code of this card
$result['country']=$customer->get_attribute(ATTR_DEF_COUNTRY_CODE,0);
// official ID , like VAT
$result['customer_id']=str_replace([" ",".","-","/"],"" ,$customer->get_attribute(ATTR_DEF_NUMTVA,0));
// official name of the company
$result['registration_name']=$customer->get_attribute(ATTR_DEF_NAME,0);
$result['endpoint_id']=$customer->get_attribute(ATTR_DEF_PEPPOLID,0);
return $result;
}
/**
* @brief complete $this->data from $g_parameter (global variable) for
* Noalyss_Folder_Parameter
* @return array keys :
* - name
* - ,street
* - ,postalzone
* - ,city
* - ,country
* - supplier_id => VAT Number
* - registration_name,
*
*/
function fill_supplier():array
{
$a_parameter=$this->load_noalyss_parameter();
$result=array();
$result['name']=$a_parameter['MY_NAME'];
$result['street']=$a_parameter['MY_STREET'];
$result['postalzone']=$a_parameter['MY_POSTCODE'];
$result['city']=$a_parameter['MY_CITY'];
$result['country']=$a_parameter['MY_COUNTRY'];
// official name of the company
$result['registration_name']=$a_parameter['MY_NAME'];
// official ID , like VAT
$result['supplier_id']=str_replace([" ",".","-","/"],"" ,$a_parameter['MY_TVA']);
$result['COUNTRY_CODE']=$a_parameter['COUNTRY_CODE']??"";
$result['COMPANY_LEGAL_REGISTRATION']=$a_parameter['COMPANY_LEGAL_REGISTRATION']??"";
$result['COMPANY_LEGAL_ENTITY']=$a_parameter['COMPANY_LEGAL_ENTITY']??"";
$result['INVOICE_CONTACT_NAME']=$a_parameter['INVOICE_CONTACT_NAME']??"";
$result['INVOICE_EMAIL_COMPANY']=$a_parameter['INVOICE_EMAIL_COMPANY']??"";
$result['COMPANY_UBL_ID']=$a_parameter['COMPANY_UBL_ID']??"";
return $result;
}
/**
* @brief build operation from array
* key :
* - [e_march0] => Quick code of the item
- [e_march0_label] => Label of item
- [e_march0_price] => Unit Price
- [e_quant0] => Quantity
- [htva_march0] => Price w/0 VAT
- [e_march0_tva_id] => Code VAT
- [e_march0_tva_amount] => Amount VAT
- [tva_march0] => Amount VAT (duplicate -> to remove)
- [tvac_march0] => Total Amount Tax included
* @param type $a_array
* @return type
*/
function fill_operation_from_array($a_array)
{
$result=array();
$http=new \HttpInput();
$http->set_array($a_array);
$nb_item=$http->get_value("nb_item");
for ($i=0;$i<$nb_item;$i++)
{
if ( $http->get_value("e_march{$i}_tva_id") == "")
{
continue;
}
$operation=array();
$card=\Fiche::from_qcode($this->cn,trim($http->get_value("e_march{$i}")));
$operation['card_id']=$card->id;
$operation['quantity']=$http->get_value("e_quant{$i}");
$operation['price']=$http->get_value("e_march{$i}_price");
$operation['vat']=$http->get_value("tvac_march{$i}");
$tva= \Acc_Tva::build($this->cn, $http->get_value("e_march{$i}_tva_id"));
$operation['vat_id']=$tva->tva_id;
$operation['vat_reversed']=($tva->tva_both_side==1)?$operation['vat']:0;
$operation['vat_code']=$tva->tva_peppol_code;
$operation['code_quantity']=$card->get_attribute(ATTR_DEF_QUANTITY_TYPE,0);
$operation['code_quantity']=($operation['code_quantity']=="")?"EA":$operation['code_quantity'];
$result[$i]=$operation;
}
return $result;
}
}

View file

@ -30,7 +30,9 @@
"label"=>"tva_label",
"rate"=>"tva_rate",
"comment"=>"tva_comment",
"account"=>"tva_poste");
"account"=>"tva_poste"
tva_peppol_code
* );
*/
class Acc_Tva
@ -42,7 +44,9 @@ class Acc_Tva
"account"=>"tva_poste",
"both_side"=>'tva_both_side',
'tva_reverse_account'=>'tva_reverse_account',
'tva_code'=>'tva_code');
'tva_code'=>'tva_code',
"tva_peppol_code"=>"tva_peppol_code"
);
public $tva_id,
$tva_label,
$tva_rate,
@ -50,16 +54,18 @@ class Acc_Tva
$tva_poste,
$tva_both_side,
$tva_code,
$tva_reverse_account;
$tva_reverse_account,
$tva_peppol_code
;
private $cn; //!< Database connection
private Tva_Rate_SQL $tva_rate_sql;
function __construct ($p_init,$p_tva_id=-1)
function __construct (Database $cn,$p_tva_id=-1)
{
$this->cn=$p_init;
$this->tva_rate_sql=new Tva_Rate_SQL($p_init,$p_tva_id);
$this->cn=$cn;
$this->tva_rate_sql=new Tva_Rate_SQL($cn,$p_tva_id);
$this->tva_id=$p_tva_id;
$this->tva_label=&$this->tva_rate_sql->tva_label;
$this->tva_rate=&$this->tva_rate_sql->tva_rate;
@ -68,6 +74,7 @@ class Acc_Tva
$this->tva_both_side=&$this->tva_rate_sql->tva_both_side;
$this->tva_code=&$this->tva_rate_sql->tva_code;
$this->tva_reverse_account=&$this->tva_rate_sql->tva_reverse_account;
$this->tva_peppol_code=&$this->tva_rate_sql->tva_peppol_code;
}
/**

View file

@ -329,7 +329,7 @@ class Fiche
* @param int $p_ad_id AD_ID from attr_def.ad_id
* @param int $p_return 1 return NOTFOUND otherwise an empty string
* @see constant.php
* @return string
* @return string
* @note reread data from database and so it reset previous unsaved change
*/
function get_attribute($p_ad_id,$p_return=1)
@ -1966,12 +1966,12 @@ class Fiche
}
/**
* @brief create a card from a qcode and returns a card
* @param string $p_qcode qcode of the card
* @param $cn Database cnx
* @param $p_qcode (string) qcode of the card
*/
static function from_qcode($p_qcode)
static function from_qcode(Database $cn,string $p_qcode)
{
$cn=Dossier::connect();
$card=new Card($cn);
$card=new Fiche($cn);
$card->get_by_qcode($p_qcode);
return $card;
}

View file

@ -79,6 +79,32 @@ if ( isset ($_POST['view_invoice'] ) )
echo '<form class="print" enctype="multipart/form-data" method="post">';
echo dossier::hidden();
echo $Ledger->confirm($_POST );
//----------------------------------------------------------------------------
// Check that INVOICE can be generated
// for e-invoice only
//----------------------------------------------------------------------------
if ($g_parameter->MY_INVOICE_FORMAT != 'BASIC')
{
$xmldocument= \Noalyss\XMLDocument\XMLInvoice::build_xmlinvoice($cn);
$array=[];
$array['supplier']=$xmldocument->fill_supplier();
echo "customer ",$http->post("e_client");
$customer=Fiche::from_qcode($cn,trim($http->post("e_client")));
$array['customer']=$xmldocument->fill_customer($customer->id);
$array['operation']=$xmldocument->fill_operation_from_array($_POST);
$array['due_date']=$http->post("e_ech");
if ( $array['due_date'] == '')
{
$array['due_date']=$http->post("e_date");
}
/////////////////////////////////////////////////
///@todo ajouter date échéance
/////////////////////////////////////////////////
$xmldocument->set_data($array);
$xmldocument->display_error();
}
echo HtmlInput::hidden('ac',$strac);
$Ledger->input_extra_info();
echo HtmlInput::submit("record", _("Enregistrement"), 'onClick="return verify_ca(\'\');"');
@ -133,8 +159,8 @@ if ( isset($_POST['record']) )
/* Save the attachment or generate doc */
if (isset($_FILES['pj'])) {
if (noalyss_strlentrim($_FILES['pj']['name']) != 0)
$cn->save_receipt($seq);
{ $cn->save_receipt($seq);
}
else
/* Generate an invoice and save it into the database */
if (isset($_POST['gen_invoice']))
@ -154,8 +180,10 @@ if ( isset($_POST['record']) )
$xmldocument->build_data($Ledger->jr_id);
$code_error = $xmldocument->verify() ;
if ( ! empty( $code_error ) ) {
echo "Impossible de générer facture : code error $code_error ";
echo $xmldocument->get_message_error($code_error);
echo "Impossible de générer facture : code error ";
\Noalyss\Dbg::echo_var(1, '$code_error is ');
\Noalyss\Dbg::echo_var(1, $code_error);
// echo $xmldocument->get_message_error($code_error);
}
$pdf_filename=$acc_document->transform2pdf();
@ -188,7 +216,6 @@ if ( isset($_POST['record']) )
}
}
}
catch (\Exception $e) {
if ( $e->getCode()==EXC_BALANCE)

View file

@ -430,7 +430,8 @@ function noalyss_class_autoloader($class)
"noalyss\otp"=>"lib/otp.class.php",
'noalyss\xmldocument\xmlinvoice'=>'XMLDocument/XMLInvoice.php',
'noalyss\xmldocument\facturx'=>'XMLDocument/FacturX.php',
'noalyss\xmldocument\invoiceubl21'=>'XMLDocument/InvoiceUBL21.php'
'noalyss\xmldocument\invoiceubl21'=>'XMLDocument/InvoiceUBL21.php',
'noalyss\xmldocument\error_message'=>'XMLDocument/Error_Message.php'
);
if (isset ($aClass[$class])) {
require_once NOALYSS_INCLUDE . "/" . $aClass[$class];

View file

@ -0,0 +1,99 @@
<?php
/*
* This file is part of NOALYSS.
*
* NOALYSS is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* NOALYSS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with NOALYSS; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
// Copyright Author Dany De Bontridder danydb@aevalys.eu 22/10/23
/**
* @file
* @brief display errors for generating e-invoices, called from
* invoiceUBL21-display-error.php
*/
//var @a_error (array) contains error
if ( count($a_error) == 0) return;
$error_message=new \Noalyss\XMLDocument\Error_Message($a_error);
var_dump($a_error);
?>
<div class="notice" >
<h3><?=_("Société")?></h3>
<p class="text-muted">
<?=_("A corriger dans COMPANY")?>
</p>
<?php
//----------------------------------------------------------------------------
// company
//----------------------------------------------------------------------------
$nb_error=count($a_error['company']);
for ($i=0;$i<$nb_error;$i++):
?>
<div class="notice-item">
<?=$error_message->get_message_error(code:$a_error['company'][$i],type:'company')?>
</div>
<?php
endfor;
?>
<h3><?=_("Client")?></h3>
<p class="text-muted">
<?=_("A corriger dans la fiche")?>
</p>
<?php
//----------------------------------------------------------------------------
// Customer
//----------------------------------------------------------------------------
$nb_error=count($a_error['customer']);
for ($i=0;$i<$nb_error;$i++):
?>
<div class="notice-item">
<?=$error_message->get_message_error(code:$a_error['customer'][$i],type:'customer')?>
</div>
<?php
endfor;
?>
<h3><?=_("Opération")?></h3>
<p class="text-muted">
<?=_("A corriger dans l'opération")?>
</p>
<?php
if ( ! isset($this->data['due_date']) || $this->data['due_date']==""):
?>
<div class="notice-item">
<?=_("Date échéance nécessaire")?>
</div>
<?php
endif;
//----------------------------------------------------------------------------
// Item VAT
//----------------------------------------------------------------------------
$a_vat_error=$this->check_VAT();
$nb_error=count($a_vat_error);
for ($i=0;$i<$nb_error;$i++):
?>
<div class="notice-item">
<?=$a_vat_error[$i]?>
</div>
<?php
endfor;
?>
</div>