From 9ebb89b38a5a994978c8c304684a2ecf5845d372 Mon Sep 17 00:00:00 2001 From: sparkyx Date: Sat, 13 Sep 2025 13:16:14 +0200 Subject: [PATCH] E-INVOICE: check data before generating E-INVOICE: add missing for Belgium : BuyerReference and Due_Date --- include/XMLDocument/Error_Message.php | 136 ++++++++++++++ include/XMLDocument/InvoiceUBL21.php | 163 +++++++++++------ include/XMLDocument/XMLInvoice.php | 170 +++++++++++++----- include/class/acc_tva.class.php | 19 +- include/class/fiche.class.php | 10 +- include/compta_ven.inc.php | 37 +++- include/constant.php | 3 +- .../template/invoiceUBL21-display_error.php | 99 ++++++++++ 8 files changed, 520 insertions(+), 117 deletions(-) create mode 100644 include/XMLDocument/Error_Message.php create mode 100644 include/template/invoiceUBL21-display_error.php diff --git a/include/XMLDocument/Error_Message.php b/include/XMLDocument/Error_Message.php new file mode 100644 index 000000000..d9ecb08a2 --- /dev/null +++ b/include/XMLDocument/Error_Message.php @@ -0,0 +1,136 @@ +'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]; + } +} diff --git a/include/XMLDocument/InvoiceUBL21.php b/include/XMLDocument/InvoiceUBL21.php index 892d46bb1..9b5475ec4 100644 --- a/include/XMLDocument/InvoiceUBL21.php +++ b/include/XMLDocument/InvoiceUBL21.php @@ -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 P01 @@ -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 */ diff --git a/include/XMLDocument/XMLInvoice.php b/include/XMLDocument/XMLInvoice.php index 154bef980..c09862c87 100644 --- a/include/XMLDocument/XMLInvoice.php +++ b/include/XMLDocument/XMLInvoice.php @@ -1,7 +1,7 @@ 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; } } diff --git a/include/class/acc_tva.class.php b/include/class/acc_tva.class.php index e9df34fc0..52318de3d 100644 --- a/include/class/acc_tva.class.php +++ b/include/class/acc_tva.class.php @@ -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; } /** diff --git a/include/class/fiche.class.php b/include/class/fiche.class.php index f718a2f4e..6b706fc17 100644 --- a/include/class/fiche.class.php +++ b/include/class/fiche.class.php @@ -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; } diff --git a/include/compta_ven.inc.php b/include/compta_ven.inc.php index 0904b35c6..746a0e9d9 100644 --- a/include/compta_ven.inc.php +++ b/include/compta_ven.inc.php @@ -79,6 +79,32 @@ if ( isset ($_POST['view_invoice'] ) ) echo '
'; 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) diff --git a/include/constant.php b/include/constant.php index d5245d262..a5121f5cb 100644 --- a/include/constant.php +++ b/include/constant.php @@ -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]; diff --git a/include/template/invoiceUBL21-display_error.php b/include/template/invoiceUBL21-display_error.php new file mode 100644 index 000000000..20e000227 --- /dev/null +++ b/include/template/invoiceUBL21-display_error.php @@ -0,0 +1,99 @@ + +
+

+

+ +

+ +
+ get_message_error(code:$a_error['company'][$i],type:'company')?> +
+ + +

+

+ +

+ +
+ get_message_error(code:$a_error['customer'][$i],type:'customer')?> +
+ + +

+

+ +

+ data['due_date']) || $this->data['due_date']==""): + ?> +
+ +
+ check_VAT(); + $nb_error=count($a_vat_error); + for ($i=0;$i<$nb_error;$i++): + ?> +
+ +
+ + +
\ No newline at end of file