From 9db299594dc5a7710a3aa920fe14395ec28213f3 Mon Sep 17 00:00:00 2001 From: sparkyx Date: Thu, 30 Oct 2025 11:04:34 +0100 Subject: [PATCH 01/19] E-INVOICE: Peppol id is used to compose the UBL21, improve check and use --- html/js/ajax_fiche.js | 5 ++++ include/XMLDocument/invoiceubl21.class.php | 32 ++++++++++++++++++---- include/XMLDocument/xmlinvoice.class.php | 5 ++-- include/ajax/ajax_search_peppol.php | 11 ++++++-- include/lib/ipeppol_id.class.php | 7 ----- 5 files changed, 43 insertions(+), 17 deletions(-) diff --git a/html/js/ajax_fiche.js b/html/js/ajax_fiche.js index b603d121f..63144c87d 100644 --- a/html/js/ajax_fiche.js +++ b/html/js/ajax_fiche.js @@ -428,9 +428,14 @@ category_card.display_search_peppol = function (p_domid) { var dgbox = "peppol_id_search_div"; waiting_box(); + let peppol_id=id$(p_domid).value; var queryString = { op:'search_peppol' ,ctl:p_domid + ,query:peppol_id + } + if ( peppol_id != "") { + queryString['filter']='peppolid'; } if (id$("peppol_id_search_div_frm")) { queryString['query']=$F("query"); diff --git a/include/XMLDocument/invoiceubl21.class.php b/include/XMLDocument/invoiceubl21.class.php index 6f8db429f..d97629f20 100644 --- a/include/XMLDocument/invoiceubl21.class.php +++ b/include/XMLDocument/invoiceubl21.class.php @@ -83,6 +83,9 @@ class InvoiceUBL21 extends XMLInvoice { $a_error[]=$item; } } + /** + * check that PEPPOL ID is valid + */ return $a_error; } /** @@ -100,11 +103,23 @@ class InvoiceUBL21 extends XMLInvoice { ]; foreach ($a_needed as $item=>$value) { - if ( $this->data['customer'][$value]=="") { + if ( trim($this->data['customer'][$value])=="") { $a_error[]=$value; } } - + if ( $this->data['customer'][ATTR_DEF_PEPPOLID] != "") + { + // 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; } @@ -130,9 +145,10 @@ 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',$this->data['customer']['endpoint_id'])) - ->setAttribute('schemeID', 9925); + 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); @@ -210,7 +226,11 @@ class InvoiceUBL21 extends XMLInvoice { $supplier=$this->createElement('cac:AccountingSupplierParty'); $supplier_party=$supplier->appendChild($this->createElement('cac:Party')); - $supplier_party->appendChild($this->createElement('cbc:EndpointID',$this->data["supplier"]['supplier_vat_id']))->setAttribute('schemeID', 9925); + list($scheme_id,$peppol)=explode( ":",$company['COMPANY_UBL_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); diff --git a/include/XMLDocument/xmlinvoice.class.php b/include/XMLDocument/xmlinvoice.class.php index 47fb983e2..b5b7ec125 100644 --- a/include/XMLDocument/xmlinvoice.class.php +++ b/include/XMLDocument/xmlinvoice.class.php @@ -452,8 +452,9 @@ abstract class XMLInvoice extends \DOMDocument { $result['country']=substr($result['customer_vat_id'],0,2); } - // $result['endpoint_id']=$customer->get_attribute(ATTR_DEF_PEPPOLID,0); - $result['endpoint_id']= $result['customer_vat_id']; + $result['endpoint_id']=$customer->get_attribute(ATTR_DEF_PEPPOLID,0); + + return $result; } /** diff --git a/include/ajax/ajax_search_peppol.php b/include/ajax/ajax_search_peppol.php index be5ad08b5..47fd92e9c 100644 --- a/include/ajax/ajax_search_peppol.php +++ b/include/ajax/ajax_search_peppol.php @@ -78,7 +78,11 @@ echo \HtmlInput::title_box(_("Recherche PEPPOL Directory"), 'peppol_id_search_di $input_query = new IText("query", $query); $select_filter = new ISelect("filter"); - $select_filter->transform(["vatid" => _("Numéro de TVA"), "entid" => _("Numéro entreprise"), "name" => _("Nom")]); + $select_filter->transform(["vatid" => _("Numéro de TVA") + , "entid" => _("Numéro entreprise") + , "peppolid" => _("Endpoint (PEPPOL ID)") + , "name" => _("Nom") + ]); $select_filter->selected = $filter ?> input() ?> @@ -97,7 +101,7 @@ echo \HtmlInput::title_box(_("Recherche PEPPOL Directory"), 'peppol_id_search_di '; // div class content return; @@ -127,6 +131,9 @@ switch ($filter) } $search = http_build_query(["participant" => 'iso6523-actorid-upis::0208:' . $query]); break; + case 'peppolid': + $search = http_build_query(["participant" => 'iso6523-actorid-upis::' . $query]); + break; default: throw new \Exception("ASP129 unknown filter",129); } diff --git a/include/lib/ipeppol_id.class.php b/include/lib/ipeppol_id.class.php index 368752a49..ce82d43d6 100644 --- a/include/lib/ipeppol_id.class.php +++ b/include/lib/ipeppol_id.class.php @@ -76,11 +76,4 @@ class IPEPPOL_ID extends HtmlInput return $this->itext->display(); } - - static function testme() - { - $ivatnumber=new IPEPPOL_ID("av_text13"); - - echo $ivatnumber->input(); - } } \ No newline at end of file From 35d73582b0f3900b8d07e2586e2027c64cd31ef8 Mon Sep 17 00:00:00 2001 From: sparkyx Date: Fri, 7 Nov 2025 18:22:35 +0100 Subject: [PATCH 02/19] E-INVOICE extract all documents from XML and store in DB --- .../document_reference_type.class.php | 90 +++++++++++++++++ include/XMLDocument/invoiceubl21.class.php | 10 +- .../XMLDocument/xmlinvoice_reader.class.php | 63 ++++++++---- include/class/acc_document.class.php | 99 ++++++++----------- include/constant.php | 4 +- .../database/jrn_sup_document_sql.class.php | 71 +++++++++++++ sql/upgrade.sql | 47 +++++---- 7 files changed, 288 insertions(+), 96 deletions(-) create mode 100644 include/XMLDocument/document_reference_type.class.php create mode 100644 include/database/jrn_sup_document_sql.class.php diff --git a/include/XMLDocument/document_reference_type.class.php b/include/XMLDocument/document_reference_type.class.php new file mode 100644 index 000000000..855cdde2f --- /dev/null +++ b/include/XMLDocument/document_reference_type.class.php @@ -0,0 +1,90 @@ +filecontent + . ", mimecode=" . $this->mimecode + . ", filename=" . $this->filename + . "]"; + } +} + +class Document_Reference +{ + protected $id; + protected $description; + protected Binary_Object $binary_object; + function __toString(): string + { + return "Document_Reference[id=" . $this->id + . ", description=" . $this->description + . ", binary_object=" . $this->binary_object + . "]"; + } + public function getId() + { + return $this->id; + } + + public function getDescription() + { + return $this->description; + } + + public function getBinary_object(): Binary_Object + { + return $this->binary_object; + } + + public function setId($id) + { + $this->id = $id; + return $this; + } + + public function setDescription($description) + { + $this->description = $description; + return $this; + } + + public function setBinary_object(Binary_Object $binary_object) + { + $this->binary_object = $binary_object; + return $this; + } + + + +} \ No newline at end of file diff --git a/include/XMLDocument/invoiceubl21.class.php b/include/XMLDocument/invoiceubl21.class.php index d97629f20..7a5bd226f 100644 --- a/include/XMLDocument/invoiceubl21.class.php +++ b/include/XMLDocument/invoiceubl21.class.php @@ -86,6 +86,14 @@ class InvoiceUBL21 extends XMLInvoice { /** * check that PEPPOL ID is valid */ + if ( isset($company['COMPANY_UBL_ID'])) + { + if ( strpos($company['COMPANY_UBL_ID'],':') == 0 ) + { + $a_error[]="COMPANY_UBL_ID"; + } + + } return $a_error; } /** @@ -107,7 +115,7 @@ class InvoiceUBL21 extends XMLInvoice { $a_error[]=$value; } } - if ( $this->data['customer'][ATTR_DEF_PEPPOLID] != "") + if ( $this->data['customer']["endpoint_id"] != "") { // check if peppol id has the form 9999:9999... list($scheme_id,$peppol)=explode(":", $this->data['customer'][$value]); diff --git a/include/XMLDocument/xmlinvoice_reader.class.php b/include/XMLDocument/xmlinvoice_reader.class.php index 7d723841e..d7b714777 100644 --- a/include/XMLDocument/xmlinvoice_reader.class.php +++ b/include/XMLDocument/xmlinvoice_reader.class.php @@ -56,6 +56,9 @@ class XMLInvoice_Reader { $this->domDocument = $domDocument; $this->xpath = new \DOMXPath($this->domDocument); + $this->xpath->registerNamespace("ns4", 'urn:oasis:names:specification:ubl:schema:xsd:Invoice-2'); + $this->xpath->registerNamespace("cac", 'urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2'); + $this->xpath->registerNamespace("cbc", 'urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2'); } /** @@ -136,35 +139,53 @@ class XMLInvoice_Reader /** * @brief returns the embedded document in an array (keys : filecontent (BYTES),mimecode , filename) * or false if there is no document - * @return bool|array keys : filecontent (BYTES),mimecode , filename) - * or false if there is no document + * @return bool|array of array : filecontent (BYTES),mimecode , filename) * @throws \Exception if there are several documents * */ public function get_embedded_document() { - if (($node = $this->get_node("//cac:AdditionalDocumentReference")) == false) - { - return false; - } - if ($node->length > 1) - { - throw new \Exception("XR110 Too many documents found", 110); - } - ///@var DOMNodeList $data //cac:AdditionalDocumentReference[1]/cac:Attachment[1]/cbc:EmbeddedDocumentBinaryObject[1] - if ( - ($data = $this->get_node("//cac:AdditionalDocumentReference[1]/cac:Attachment[1]/cbc:EmbeddedDocumentBinaryObject[1]")) == null + $a_document_reference=array(); + foreach (array( + "AdditionalDocumentReference", + "ReceiptDocumentReference", + "StatementDocumentReference", + "OriginatorDocumentReference", + "ContractDocumentReference" ) + as $document) { + $document=$this->domDocument->getElementsByTagName($document); + if ( $document->length == 0 ) { + continue; + } + for ($e=0;$e < $document->count();$e++) + { + $item=$document->item($e); + + if ( $item->nodeType == XML_ELEMENT_NODE ) + { + $id=$item->getElementsByTagName("ID")[0]?->nodeValue; + $description=$item->getElementsByTagName("DocumentDescription")[0]?->nodeValue; + $embedded_document=$item->getElementsByTagName("EmbeddedDocumentBinaryObject"); + if ( $embedded_document->length == 0) + { + continue; + } + $binary_object=new Binary_Object; + $binary_object->filecontent=base64_decode($embedded_document[0]->nodeValue); + $binary_object->mimecode=$embedded_document[0]->getAttribute("mimeCode"); + $binary_object->filename=$embedded_document[0]->getAttribute("filename"); + $document_reference=new Document_Reference(); + $document_reference->setId($id) + ->setDescription($description) + ->setBinary_object($binary_object); + $a_document_reference[]=clone $document_reference; + } + } - throw new \Exception("X116 Document corrupted", 116); } - $filecontent = base64_decode($data->item(0)->nodeValue); - $mimecode = $data->item(0)->getAttribute("mimeCode"); - $filename = $data->item(0)->getAttribute("filename"); - return array("filecontent" => $filecontent, - "mimecode" => $mimecode, - "filename" => $filename); + return $a_document_reference; } /** @@ -192,6 +213,7 @@ class XMLInvoice_Reader $result['postcode'] = $this->get_node_value('//cac:AccountingCustomerParty[1]/cac:Party[1]/cac:PostalAddress[1]/cbc:PostalZone[1]'); $result['country_code'] = $this->get_node_value("//cac:AccountingCustomerParty[1]/cac:Party[1]/cac:PostalAddress[1]/cac:Country[1]/cbc:IdentificationCode[1]"); $result['company_id'] = $this->get_node_value("//cac:AccountingCustomerParty[1]/cac:Party[1]/cac:PartyTaxScheme[1]/cbc:CompanyID[1]"); + $result['scheme']=$this->get_node("//cac:AccountingCustomerParty[1]/cac:Party[1]/cbc:EndpointID[1]")[0]->getAttribute("schemeID"); return $result; } @@ -209,6 +231,7 @@ class XMLInvoice_Reader $result['postcode'] = $this->get_node_value("//cac:AccountingSupplierParty[1]/cac:Party[1]/cac:PostalAddress[1]/cbc:PostalZone[1]"); $result['country_code'] = $this->get_node_value("//cac:AccountingSupplierParty[1]/cac:Party[1]/cac:PostalAddress[1]/cac:Country[1]/cbc:IdentificationCode[1]"); $result['company_id'] = $this->get_node_value("//cac:AccountingSupplierParty[1]/cac:Party[1]/cac:PartyTaxScheme[1]/cbc:CompanyID[1]"); + $result['scheme']=$this->get_node("//cac:AccountingSupplierParty[1]/cac:Party[1]/cbc:EndpointID[1]")[0]->getAttribute("schemeID"); return $result; } diff --git a/include/class/acc_document.class.php b/include/class/acc_document.class.php index f9ff6ab0d..111df5d5d 100644 --- a/include/class/acc_document.class.php +++ b/include/class/acc_document.class.php @@ -320,70 +320,55 @@ class Acc_Document extends Document { //,mimecode mimetype and filename (string) try { - $embedded_file=$xmlreader->get_embedded_document(); - if ($embedded_file == false) - { - $embedded_file=array(); - // create a PDF with standard information - $pdf=$xmlreader->to_pdf($this->db); - $file_oid=$this->db->lo_write($pdf->Output("S")); - $embedded_file['filename']="invoice.pdf"; - $embedded_file['mimecode']="application/pdf"; - } - else - { - $file_oid=$this->db->lo_write($embedded_file['filecontent']); - if ( $file_oid == false ) - { - // create a PDF with standard information - $pdf=$xmlreader->to_pdf($this->db); - $file_oid=$this->db->lo_write($pdf->Output("S")); - } - } + + // create a PDF with standard information + $pdf=$xmlreader->to_pdf($this->db); + $file_oid=$this->db->lo_write($pdf->Output("S")); + //@var $file_oid OID of the large object saved in DB + $this->d_name="invoice.pdf"; + $this->d_description="Auto generated invoice"; + $this->d_lob=$file_oid; + $this->d_mimetype="application/pdf"; + // save extracted document into DB + $this->db->exec_sql("update jrn set jr_pj=$1 , jr_pj_name=$2, + jr_pj_type=$3 where jr_id=$4", + array( + $this->d_lob + , $this->d_name + , $this->d_description + , $this->d_id + ) + ); + // save all the documents into the DB + // @var embedded_file (array of Noalyss\XML\Document_Reference) + $embedded_file=$xmlreader->get_embedded_document(); + $nb_file = count($embedded_file); + for ($i=0;$i <$nb_file ; $i++) + { + // other documents save in jrn_sup_document + // @var $file (Binary File from XML) + $binary=$embedded_file[$i]->getBinary_object(); + + //@var $sup_oid : oid of the supplemental + $sup_oid=$this->db->lo_write($binary->filecontent); + + $jrn_sup_document=new Jrn_Sup_Document_SQL($this->db); + $jrn_sup_document->jr_id=$this->d_id; + $jrn_sup_document->js_mimetype=$binary->mimecode; + $jrn_sup_document->js_filename=$binary->filename; + $jrn_sup_document->js_lob=$sup_oid; + $jrn_sup_document->js_description=$embedded_file[$i]->getDescription(); + $jrn_sup_document->js_cbc_id=$embedded_file[$i]->getId(); + $jrn_sup_document->save(); + } - $this->d_name=$embedded_file['filename']; - $this->d_description=$embedded_file['filename']; - $this->d_lob=$file_oid; - $this->d_mimetype=$embedded_file['mimecode']; - // save extracted document into DB - $this->db->exec_sql("update jrn set jr_pj=$1 , jr_pj_name=$2, - jr_pj_type=$3 where jr_id=$4", - array( - $this->d_lob - , $this->d_name - , $this->d_description - , $this->d_id - ) - ); - return $file_oid; } catch (\Exception $e ) { \record_log($e); - // if exception is not too many document or document corrupted - // then rethrow the exception - if ( !in_array(e->getCode(),[110,116]) ) - { - throw new \Exception("X281 ",281,$e); - } + throw new \Exception("X281 ",281,$e); } - } - - // save new document - $this->db->exec_sql("update jrn set jr_pj=$1 , jr_pj_name=$2, - jr_pj_type=$3 where jr_id=$4", - array( - $oid - , $_FILES['pj']['name'] - , $_FILES['pj']['type'] - , $this->d_id - ) - ); - $this->d_name=$_FILES['pj']['name']; - $this->d_description=$_FILES['pj']['name']; - $this->d_lob=$oid; - $this->d_mimetype=$_FILES['pj']['type']; $this->db->commit(); return $oid; diff --git a/include/constant.php b/include/constant.php index e9d5c9053..0f37c02b8 100644 --- a/include/constant.php +++ b/include/constant.php @@ -441,7 +441,9 @@ function noalyss_class_autoloader($class) 'noalyss\xmldocument\xmlinvoice_reader'=>'XMLDocument/xmlinvoice_reader.class.php', 'noalyss\mail_parameter'=>'lib/mail_parameter.class.php', 'noalyss\smtpmail'=>'lib/smtpmail.class.php', - 'noalyss\iban_number'=>'lib/iban_number.class.php' + 'noalyss\iban_number'=>'lib/iban_number.class.php', + 'noalyss\xmldocument\document_reference'=>'XMLDocument/document_reference_type.class.php', + 'noalyss\xmldocument\binary_object'=>'XMLDocument/document_reference_type.class.php' ); if (isset ($aClass[$class])) { require_once NOALYSS_INCLUDE . "/" . $aClass[$class]; diff --git a/include/database/jrn_sup_document_sql.class.php b/include/database/jrn_sup_document_sql.class.php new file mode 100644 index 000000000..69c41cec7 --- /dev/null +++ b/include/database/jrn_sup_document_sql.class.php @@ -0,0 +1,71 @@ +table = "public.jrn_sup_document"; + $this->primary_key = "js_id"; + /* + * List of columns + */ + $this->name = array( + "js_id" => "js_id" + , "js_mimetype" => "js_mimetype" + , "js_lob" => "js_lob" + , "jr_id" => "jr_id" + ,'js_filename'=>'js_filename' + ,'js_description'=>'js_description' + ,'js_cbc_id'=>'js_cbc_id' + ); + + /* + * Type of columns + */ + $this->type = array( + "js_id" => "numeric" + , "js_mimetype" => "text" + , "js_lob" => "oid" + , "jr_id" => "numeric" + ,'js_filename'=>'text' + ,'js_description'=>'text' + ,'js_cbc_id'=>'text' + ); + + $this->default = array("js_id" => "auto"); + + $this->date_format = "DD.MM.YYYY"; + parent::__construct($p_cn, $p_id); + } +} diff --git a/sql/upgrade.sql b/sql/upgrade.sql index e004a0162..ccc8c7fe5 100644 --- a/sql/upgrade.sql +++ b/sql/upgrade.sql @@ -233,7 +233,7 @@ insert into parameter_extra(pe_code,pe_label) values ('COMPANY_LEGAL_REGISTRATIO insert into parameter_extra(pe_code,pe_label) values ('COMPANY_LEGAL_ENTITY','Forme légal de la société (SRL,ASBL,AISBL,...') on conflict do nothing; insert into parameter_extra(pe_code,pe_label) values ('COMPANY_BANK_IBAN','Compte en banque (IBAN)') on conflict do nothing; insert into parameter_extra(pe_code,pe_label) values ('COMPANY_BANK_BIC','BIC Bank Identification Code') on conflict do nothing; -insert into parameter_extra(pe_code,pe_label) values ('COMPANY_UBL_ID','ID PEPPOL') on conflict do nothing; +insert into parameter_extra(pe_code,pe_label) values ('COMPANY_UBL_ID','Identifiant PEPPOL') on conflict do nothing; insert into parameter_extra(pe_code,pe_label) values ('SIREN','n° SIREN') on conflict do nothing; insert into parameter_extra(pe_code,pe_label) values ('SIRET','n° SIRET') on conflict do nothing; @@ -438,24 +438,14 @@ insert into jnt_fic_attr (fd_id,ad_id,jnt_order) -- CREATE OR REPLACE FUNCTION comptaproc.update_peppol () RETURNS int2 - /*- SETOF menu_tree : (TABLE FUNCTION) menu_tree is a type of a rowtype create type menu_tree as (code text,description text); - - int2, text,varchar... - - trigger : if called from a trigger - - void : nothing - - boolean - */ AS + AS $BODY$ declare -/* - * Section for variables - */ -n_card_id int; -vat_number text; -record_card RECORD; -result_fct int; -country_code text; + n_card_id int; + vat_number text; + record_card RECORD; + result_fct int; begin -select pi_value into country_code from parameter_internal where pi_id ='COUNTRY_CODE'; result_fct := 0; @@ -478,4 +468,27 @@ end; $BODY$ LANGUAGE plpgsql; -select comptaproc.update_peppol(); \ No newline at end of file +select comptaproc.update_peppol(); + + +CREATE TABLE public.jrn_sup_document ( + js_id int8 GENERATED BY DEFAULT AS IDENTITY( INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START 1 CACHE 1 NO CYCLE) NOT NULL, -- PK + js_mimetype text NOT NULL, -- Mimetype + js_lob oid NOT NULL, -- OID + jr_id int8 NOT NULL, -- FK to jrn + js_filename text not null, + js_description text , + js_cbc_id text, + CONSTRAINT jrn_sup_document_pkey PRIMARY KEY (js_id), + CONSTRAINT jrn_sup_document_jrn_fk FOREIGN KEY (jr_id) REFERENCES public.jrn(jr_id) ON DELETE SET NULL ON UPDATE CASCADE +); +COMMENT ON TABLE public.jrn_sup_document IS 'Supplement document for operation'; + +-- Column comments + +COMMENT ON COLUMN public.jrn_sup_document.js_id IS 'PK'; +COMMENT ON COLUMN public.jrn_sup_document.js_mimetype IS 'Mimetype'; +COMMENT ON COLUMN public.jrn_sup_document.js_lob IS 'OID'; +COMMENT ON COLUMN public.jrn_sup_document.jr_id IS 'FK to jrn'; +COMMENT ON COLUMN public.jrn_sup_document.js_description IS 'Description of document'; +COMMENT ON COLUMN public.jrn_sup_document.js_cbc_id IS 'ID in XML'; \ No newline at end of file From b5f55a129ae3fe53e84fd6245b20efdb7b00af16 Mon Sep 17 00:00:00 2001 From: sparkyx Date: Sat, 8 Nov 2025 15:41:54 +0100 Subject: [PATCH 03/19] Improve: message for uploading , compute ini value and color red for the trash can --- include/lib/ac_common.php | 21 +++++++++++++++++++++ include/lib/icon_action.class.php | 2 +- include/lib/message_javascript.php | 2 +- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/include/lib/ac_common.php b/include/lib/ac_common.php index ae4a3396f..c761b969c 100644 --- a/include/lib/ac_common.php +++ b/include/lib/ac_common.php @@ -1959,3 +1959,24 @@ function check_iban(&$iban): bool return false; } + +/** +* @brief convert a value in Mbytes, kb ... in byte +* @param (string) $p_value containing K , M, G +* @return int in bytes +*/ +function convert_ini_unit($p_value) +{ + if ($p_value=="") return 0; + $a_convert=["k"=>1024,"m"=>1024**2,"g"=>1024**3]; + + $p_value=trim($p_value); + $last=strtolower($p_value[strlen($p_value)-1]); + $p_value=substr($p_value,0,strlen($p_value)-1); + if ( isset($a_convert[$last])) { + $p_value=$p_value*$a_convert[$last]; + } + + return $p_value; +} + \ No newline at end of file diff --git a/include/lib/icon_action.class.php b/include/lib/icon_action.class.php index d3fa269a1..055b72494 100644 --- a/include/lib/icon_action.class.php +++ b/include/lib/icon_action.class.php @@ -272,7 +272,7 @@ class Icon_Action */ static function trash($p_id,$p_javascript) { - $r=''; + $r=''; return $r; } /** diff --git a/include/lib/message_javascript.php b/include/lib/message_javascript.php index a6866c7bc..55539721b 100644 --- a/include/lib/message_javascript.php +++ b/include/lib/message_javascript.php @@ -109,7 +109,7 @@ content[74]=""; content[76]=""; content[77]=""; - + content[78]=""; content[79]=""; From 5b337881e5bd00f0bd9bf47aad76602cdbcbb508 Mon Sep 17 00:00:00 2001 From: sparkyx Date: Sat, 8 Nov 2025 15:41:15 +0100 Subject: [PATCH 04/19] Supplementary documents for an operation, an operation can have several attached documents --- html/js/acc_ledger.js | 168 ++++++++++++++++++ .../XMLDocument/xmlinvoice_reader.class.php | 4 +- include/ajax/ajax_ledger.php | 87 +++++++++ include/export/export_suppl-document.php | 151 ++++++++++++++++ include/template/ajax_ledger+input_file.php | 68 +++++++ include/template/ledger_detail_bottom.php | 11 +- include/template/ledger_detail_sup_files.php | 91 ++++++++++ sql/upgrade.sql | 11 +- 8 files changed, 586 insertions(+), 5 deletions(-) create mode 100644 include/export/export_suppl-document.php create mode 100644 include/template/ajax_ledger+input_file.php create mode 100644 include/template/ledger_detail_sup_files.php diff --git a/html/js/acc_ledger.js b/html/js/acc_ledger.js index 57fb48df1..8f6abcd9f 100644 --- a/html/js/acc_ledger.js +++ b/html/js/acc_ledger.js @@ -2050,3 +2050,171 @@ var operation_exercice = { } } } + + +var Supplement_Document={ + +}; +/** + * see ledger_detail_sup_files.php + * $rowid=sprintf("row_js_%s_%s",$div,$item->js_id); + * @param {int} nDossier + * @param {string} sDiv + * @param {int} nJS_ID + * @returns {void} + */ +Supplement_Document.delete_document=function (nDossier,sDiv,nJS_ID,nJR_ID) +{ + confirm_box(null + ,content[47] + ,function (){ + try + { + var queryString = { + gDossier:nDossier, + div:sDiv, + jr_id:nJR_ID, + js_id:nJS_ID, + op:"ledger", + act:"rmsup" + }; + var action = new Ajax.Request( + "ajax_misc.php", + { + method: 'POST', + parameters: queryString, + onSuccess: function (req) { + remove_waiting_box(); + if (req.responseText == 'NOCONX') { + reconnect(); + return; + } + id$("row_js_"+sDiv+"_"+nJS_ID).remove(); + + } + } + ); + } catch (e) + { + alert_box(e.message); + } + } + ); + +} +Supplement_Document.input_file=function(nDossier,sDiv,nJR_ID) +{ + try + { + var dgbox = "sup_doc_input_file"+sDiv; + waiting_box(); + removeDiv(dgbox); + var queryString = { + gDossier:nDossier, + div:sDiv, + jr_id:nJR_ID, + op:"ledger", + act:"input_file", + dgbox:dgbox + }; + var action = new Ajax.Request( + "ajax_misc.php", + { + method: 'GET', + parameters: queryString, + onFailure: ajax_misc_failure, + onSuccess: function (req) { + remove_waiting_box(); + if (req.responseText == 'NOCONX') { + reconnect(); + return; + } + var y = calcy(15); + var div_style = "position:absolute;" + ";top:" + y + "px"+";z-index:"+get_next_layer(); + add_div({id: dgbox, cssclass: 'inner_box2', html: loading(), style: div_style, drag: true}); + $(dgbox).innerHTML = req.responseText; + + } + } + ); + } catch (e) + { + alert_box(e.message); + } + +} +/** + * + * @param {string} FORM ID + * @returns {Boolean} + */ +Supplement_Document.save_file=function(form_dom_id) +{ + try + { + waiting_box(); + var form_data=$(form_dom_id).serialize(); + var xhr = new XMLHttpRequest(); + var div=id$(form_dom_id).elements["div"].value; + // check size + var total_size=0; + var file_to_upload=id$("doc_sup"); + var max_size=id$(form_dom_id).elements["MAX_FILE_SIZE"].value; + var post_max_size=id$(form_dom_id).elements["post_max_size"].value; + var feedback_div=id$('feedback'+div); + + for (var e=0;e max_size) { + // if size > accepted size , push filename with error in an array feedback, + feedback_div.innerHTML += '

' + file_to_upload.files[e].name + +content[78]+ "

"; + remove_waiting_box(); + return false; + } else if ( total_size+file_to_upload.files[e].size >= post_max_size ) + { + feedback_div.innerHTML += '

limite '+content[78]+"

"; + remove_waiting_box(); + return false; + } + else { + total_size+=file_to_upload.files[e].size; + } + } + feedback_div.innerHTML="loading...."; + document.getElementById("progress_upload1b").setAttribute("value", 0); + + xhr.upload.onprogress = function (e) { + document.getElementById("progress_upload1b").setAttribute("max", e.total) + "
"; + document.getElementById("progress_upload1b").setAttribute("value", e.loaded) + "
"; + + } + + xhr.onreadystatechange = function (event) { + if (this.readyState == XMLHttpRequest.DONE) + { + remove_waiting_box(); + + if (this.status === 200) { + document.getElementById("supplement_div_list"+div).innerHTML += this.responseText ; + let dgbox=id$(form_dom_id).elements["dgbox"].value; + removeDiv(dgbox); + + } else { + document.getElementById("supplement_div_list"+div).innerHTML += "status" + this.statusText + "
"; + } + } + } + + xhr.open("POST", "ajax_misc.php?"+form_data, true); + // works xhr.send(new FormData(input.parentElement)); + var formData = new FormData(document.getElementById(form_dom_id)); + xhr.send(formData); + }catch(e) + { + console.error("Supplement_Document.save_file "+e.message) + } + remove_waiting_box(); + return false; +} diff --git a/include/XMLDocument/xmlinvoice_reader.class.php b/include/XMLDocument/xmlinvoice_reader.class.php index d7b714777..47c96a81e 100644 --- a/include/XMLDocument/xmlinvoice_reader.class.php +++ b/include/XMLDocument/xmlinvoice_reader.class.php @@ -29,7 +29,7 @@ namespace Noalyss\XMLDocument; */ /** - * @file + * @class * @brief Get information from an XML * Exception code : * - 55 : XML Invalid @@ -139,7 +139,7 @@ class XMLInvoice_Reader /** * @brief returns the embedded document in an array (keys : filecontent (BYTES),mimecode , filename) * or false if there is no document - * @return bool|array of array : filecontent (BYTES),mimecode , filename) + * @return bool|array of Document_Reference * @throws \Exception if there are several documents * */ diff --git a/include/ajax/ajax_ledger.php b/include/ajax/ajax_ledger.php index 93549280e..515c4dd99 100644 --- a/include/ajax/ajax_ledger.php +++ b/include/ajax/ajax_ledger.php @@ -566,6 +566,93 @@ switch ($action) { $acc_operation_note= Acc_Operation_Note::build_jrn_id($jr_id); echo substr($acc_operation_note->getNote()??"",0,120); + return; + case "rmsup": + //------------------------------------------------ + // Remove a document from JRN_SUP_DOCUMENT + //------------------------------------------------ + $js_id=$http->post("js_id","number"); + $jrn_sup= new Jrn_Sup_Document_SQL($cn,$js_id); + $jrn_sup->delete(); + // no answer, we stop here + return; + case 'input_file': + //------------------------------------------------ + // Display form for adding file , directly HTML + // + //------------------------------------------------ + require_once NOALYSS_TEMPLATE."/ajax_ledger+input_file.php"; + return; + case 'save_file': + //------------------------------------------------ + // Add a document to the operation + //------------------------------------------------ + if (sizeof($_FILES)==0) { + return; + } + $nb=count($_FILES['document_supplemental']["name"]); + $cn->start(); + for ($i=0;$i<$nb;$i++) + { + $file= tempnam($_ENV["TMP"], "sup_file"); + if ( move_uploaded_file($_FILES['document_supplemental']['tmp_name'][$i],$file)) + { + if ( ($oid=$cn->lo_import($file)) != false ) + { + $jrn_sup=new Jrn_Sup_Document_SQL($cn); + $jrn_sup->jr_id=$jr_id; + $jrn_sup->js_lob=$oid; + $jrn_sup->js_mimetype=$_FILES['document_supplemental']['type'][$i]; + $jrn_sup->js_filename=$_FILES['document_supplemental']['name'][$i]; + $jrn_sup->insert(); + $rowid=sprintf("row_js_%s_%s",$div,$jrn_sup->js_id); + // @var $download (url) to send file + $download="export.php?". http_build_query( + [ + "act"=>"RAW:suppl-document" + ,"js_id"=>$jrn_sup->js_id + ,"gDossier"=>$gDossier + ]); + + + $script_remove="Supplement_Document.delete_document('$gDossier','$div','{$jrn_sup->js_id}','$jr_id')"; + $icon_remove=\Icon_Action::trash(uniqid("sdd"),$script_remove); + echo << + +
+ {$jrn_sup->js_description} +
+
+ {$icon_remove} +
+ +EOF; + } + else + { + // failed + print '
'; + echo_warning(_("1 Echec ").$_FILES["name"]); + print '
'; + } + + } + else + { + //failed + // failed + print '
'; + echo_warning(_("2 Echec ").$_FILES["name"]); + print '
'; + } + } + + $cn->commit(); return; } $html = escape_xml($html); diff --git a/include/export/export_suppl-document.php b/include/export/export_suppl-document.php new file mode 100644 index 000000000..763614375 --- /dev/null +++ b/include/export/export_suppl-document.php @@ -0,0 +1,151 @@ +get('js_id',"number"); + $operation_id=$http->get("operation_id",'number',0); +} +catch (Exception $exc) +{ + record_log($exc); + return; +} +$cn=Dossier::connect(); + +//------------------------------------------------ +// Download only one document +//------------------------------------------------ +if ( $js_id > 0) +{ + $jrn_def_id=$cn->get_value("select jr_def_id from jrn_sup_document join jrn using (jr_id) where js_id=$1",array($js_id)); + + global $g_user; + if ($jrn_def_id =="" || $g_user->check_jrn($jrn_def_id) == 'X' ) + { + /* Cannot Access */ + NoAccess(); + exit -1; + } + + $jrn=new Jrn_Sup_Document_SQL($cn,$js_id); + $cn->start(); + ini_set('zlib.output_compression','Off'); + header("Pragma: public"); + header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); + header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); + header("Cache-Control: must-revalidate"); + header('Content-type: '.$jrn->js_mimetype); + header('Content-Disposition: attachment;filename="'.$jrn->js_filename.'"',FALSE); + header("Accept-Ranges: bytes"); + echo $cn->lo_read($jrn->js_lob); + $cn->commit(); + return; +} +//------------------------------------------------ +// Download all documents +//------------------------------------------------ +if ( $operation_id > 0) +{ + $jrn_def_id=$cn->get_value("select jr_def_id from jrn where jr_id=$1",array($operation_id)); + + global $g_user; + if ($jrn_def_id =="" || $g_user->check_jrn($jrn_def_id) == 'X' ) + { + /* Cannot Access */ + NoAccess(); + exit -1; + } + $jr_internal=$cn->get_value("select jr_internal from jrn where jr_id=$1",array($operation_id)); + + try { + $cn->start(); + // find all the documents for this operation + $a_file=$cn->get_array("select js_lob,js_filename from jrn_sup_document where jr_id=$1", + [$operation_id]); + // save them into a temp folder + if ( count ($a_file) == 0 ) { + // @TODO send an empty file + return; + } + $a_file_dwn=array(); + $store = tempnam($_ENV['TMP'], 'pdf_'); + unlink($store); + mkdir($store); + $nb_file=count($a_file); + for($i=0;$i < $nb_file;$i++) + { + $filename=sprintf("%s".DIRECTORY_SEPARATOR."%s", + $store, + $a_file[$i]['js_filename']); + + // avoid to overwrite a previous file with same name + if (file_exists($filename)) { + $dup=1; + do { + $filename=sprintf("%s".DIRECTORY_SEPARATOR."%s-%s", + $store, + $dup, + $a_file[$i]['js_filename']); + $dup++; + }while ( file_exists($filename)); + + } + $cn->lo_export($a_file[$i]['js_lob'], $filename); + $a_file_dwn[]=$filename; + + } + $cn->commit(); + // zip the folder + $zip = new Zip_Extended(); + $zip_file=sprintf("%s".DIRECTORY_SEPARATOR."%s.zip" + ,$store + ,$jr_internal); + + chdir($store); + $res=$zip->open($zip_file, ZipArchive::CREATE); + foreach ($a_file_dwn as $item) + { + $zip->addFile(basename($item)); + } + $zip->close(); + // send the folder to browser + header('Content-Type: application/zip'); + header('Content-Disposition: attachment; filename="document-'.$jr_internal.'.zip"'); + header('Cache-Control: private, max-age=0, must-revalidate'); + header('Pragma: public'); + echo file_get_contents($zip_file); + + } catch (Exception $exc) { + record_log($exc); + } + +} \ No newline at end of file diff --git a/include/template/ajax_ledger+input_file.php b/include/template/ajax_ledger+input_file.php new file mode 100644 index 000000000..5bcd27a7a --- /dev/null +++ b/include/template/ajax_ledger+input_file.php @@ -0,0 +1,68 @@ + list of files to update after uploading + */ + +$dgbox=$http->request("dgbox"); +$form_id="save_file{$div}"; +$progress="progress{$div}"; +$max_post_size=convert_ini_unit(ini_get("post_max_size")); + +echo \HtmlInput::title_box(_("Ajout de fichier"), $dgbox); +?> +
+ + +

+ +

+
+ + + + + + + + + +
+
    +
  • + " onclick="return Supplement_Document.save_file('');"> +
  • +
  • + +
  • +
+
+ +
\ No newline at end of file diff --git a/include/template/ledger_detail_bottom.php b/include/template/ledger_detail_bottom.php index cdea88b46..ce803a7bc 100644 --- a/include/template/ledger_detail_bottom.php +++ b/include/template/ledger_detail_bottom.php @@ -44,6 +44,7 @@ $a_tab['linked_operation_div']=array('id'=>'linked_operation_div'.$div,'label'=> $a_tab['document_operation_div']=array('id'=>'document_operation_div'.$div,'label'=>_('Document').'('.$nb_document.')','display'=>'block'); $a_tab['linked_action_div']=array('id'=>'linked_action_div'.$div,'label'=>_('Actions Gestion').'('.count($a_followup).')','display'=>'none'); $a_tab['analytic_div']=array('id'=>'analytic_div'.$div,'label'=>_('Comptabilité Analytique'),'display'=>'none'); +$a_tab['supplemental_doc_div']=array('id'=>'supplemental_doc_div'.$div,'label'=>_('Documents supplémentaires'),'display'=>'none'); //var $g_parameter \Noalyss_Parameter_Folder global $g_parameter; @@ -293,9 +294,17 @@ echo ''; ?> +
diff --git a/include/template/ledger_detail_sup_files.php b/include/template/ledger_detail_sup_files.php new file mode 100644 index 000000000..1506c1590 --- /dev/null +++ b/include/template/ledger_detail_sup_files.php @@ -0,0 +1,91 @@ + +
+
+collect_objects(" where jr_id=$1 order by js_cbc_id", [$jr_id]); +$javascript=sprintf("Supplement_Document.input_file('%s','%s','%s')", + $gDossier, + $div, + $jr_id); +if ( count($a_row) > 0) +{ + foreach ($a_row as $item) { + $export="export.php?"; + $script="Supplement_Document.delete_document('$gDossier','$div','{$item->js_id}','$jr_id')"; + $rowid=sprintf("row_js_%s_%s",$div,$item->js_id); + // @var $download (url) to send file + $download="export.php?". http_build_query( + [ + "act"=>"RAW:suppl-document" + ,"js_id"=>$item->js_id + ,"gDossier"=>$gDossier + ]); + ?> +
+ +
+ js_description?> +
+
+ +
+
+ "RAW:suppl-document" + ,"js_id"=>0 + ,"gDossier"=>$gDossier + ,'operation_id'=>$jr_id + ]); + +?> + + +
+
    +
  • + +
  • +
+
\ No newline at end of file diff --git a/sql/upgrade.sql b/sql/upgrade.sql index ccc8c7fe5..8b45c75cc 100644 --- a/sql/upgrade.sql +++ b/sql/upgrade.sql @@ -303,7 +303,7 @@ comment on sequence seq_doc_type_stdinv is 'Sequence for standard invoice'; INSERT INTO public.menu_ref (me_code, me_menu, me_file, me_url, me_description, me_parameter, me_javascript, me_type, me_description_etendue) -VALUES('RAW:xml-invoice', 'Exporte la facture XML', 'export_xml-invoice.php', NULL, 'export la facture électronique en XML', NULL, NULL, 'PR', NULL); +VALUES('RAW:xml-invoice', 'Exporte la facture XML', 'export_xml-invoice.php', NULL, 'exporte la facture électronique en XML', NULL, NULL, 'PR', NULL); insert into profile_menu (me_code,p_id, p_type_display) select 'RAW:xml-invoice',p_id,'P' from profile; @@ -491,4 +491,11 @@ COMMENT ON COLUMN public.jrn_sup_document.js_mimetype IS 'Mimetype'; COMMENT ON COLUMN public.jrn_sup_document.js_lob IS 'OID'; COMMENT ON COLUMN public.jrn_sup_document.jr_id IS 'FK to jrn'; COMMENT ON COLUMN public.jrn_sup_document.js_description IS 'Description of document'; -COMMENT ON COLUMN public.jrn_sup_document.js_cbc_id IS 'ID in XML'; \ No newline at end of file +COMMENT ON COLUMN public.jrn_sup_document.js_cbc_id IS 'ID in XML'; + + + +INSERT INTO public.menu_ref (me_code, me_menu, me_file, me_url, me_description, me_parameter, me_javascript, me_type, me_description_etendue) +VALUES('RAW:suppl-document', 'Exporte Document supplementaire', 'export_suppl-document.php', NULL, 'télécharge le document supp', NULL, NULL, 'PR', NULL); + +insert into profile_menu (me_code,p_id, p_type_display) select 'RAW:suppl-document',p_id,'P' from profile; From 48459533e18541fec3a2bb4ad9eb2f4e9c29fe15 Mon Sep 17 00:00:00 2001 From: sparkyx Date: Tue, 11 Nov 2025 10:59:14 +0100 Subject: [PATCH 05/19] e-invoice : make a PDF with credit note --- include/XMLDocument/xml_reader.class.php | 535 ++++++++++++++++++ include/XMLDocument/xmlcreditnote.class.php | 31 + .../xmlcreditnote_reader.class.php | 210 +++++++ .../XMLDocument/xmlinvoice_reader.class.php | 502 ++-------------- include/constant.php | 4 +- 5 files changed, 830 insertions(+), 452 deletions(-) create mode 100644 include/XMLDocument/xml_reader.class.php create mode 100644 include/XMLDocument/xmlcreditnote.class.php create mode 100644 include/XMLDocument/xmlcreditnote_reader.class.php diff --git a/include/XMLDocument/xml_reader.class.php b/include/XMLDocument/xml_reader.class.php new file mode 100644 index 000000000..b0d5623f2 --- /dev/null +++ b/include/XMLDocument/xml_reader.class.php @@ -0,0 +1,535 @@ +domDocument = $domDocument; + $this->xpath = new \DOMXPath($this->domDocument); + } + /** + * @brief before executing xpath->query the namespace must be registered + * the NS are different for each type of doc + * @param $xml (null , DOMXPath simpleXML) + */ + public function registerNS($xml=null) + { + $a_namespace=$this->get_namespace(); + if ($xml == null) + { + foreach ($a_namespace as $pf=>$ns) { + $this->xpath->registerNamespace($pf,$ns); + } + } + elseif ( get_class($xml) == "SimpleXMLElement" ) + { + foreach ($a_namespace as $pf=>$ns) { + $xml->registerXPathNamespace($pf,$ns); + } + } + elseif (get_class($xml) == "DOMXPath" ) + { + foreach ($a_namespace as $pf=>$ns) { + $xml->registerNamespace($pf,$ns); + } + + } + else + { + throw new \Exception ("unknown object ".get_class($xml),57); + } + } + /** + * + * @return \DOMDocument + */ + public function get_domDocument(): \DOMDocument + { + return $this->domDocument; + } + + public function get_xpath(): \DOMXPath + { + return $this->xpath; + } + + public function set_domDocument($domDocument) + { + $this->domDocument = $domDocument; + return $this; + } + + /** + * @brief Build an XMLInvoice_Reader object from an XML file + * @param $filename (string) file and path to the file + * @return \Noalyss\XMLDocument\XMLInvoice_Reader + * @throws \Exception if xml not valid + */ + abstract static function build_from_file($filename); + + /** + * @brief Build an XMLInvoice_Reader object from an XML string + * @param $string (string) XML + * @return \Noalyss\XMLDocument\XMLInvoice_Reader + * @throws \Exception if xml not valid + */ + abstract static function build_from_string($string); + + + + /** + * @brief returns the embedded document in an array (keys : filecontent (BYTES),mimecode , filename) + * or false if there is no document + * @return bool|array of Document_Reference + * @throws \Exception if there are several documents + * + */ + public function get_embedded_document() + { + $a_document_reference = array(); + foreach (array( + "AdditionalDocumentReference", + "ReceiptDocumentReference", + "StatementDocumentReference", + "OriginatorDocumentReference", + "ContractDocumentReference" + ) + as $document) + { + $document = $this->domDocument->getElementsByTagName($document); + if ($document->length == 0) + { + continue; + } + for ($e = 0; $e < $document->count(); $e++) + { + $item = $document->item($e); + + if ($item->nodeType == XML_ELEMENT_NODE) + { + $id = $item->getElementsByTagName("ID")[0]?->nodeValue; + $description = $item->getElementsByTagName("DocumentDescription")[0]?->nodeValue; + $embedded_document = $item->getElementsByTagName("EmbeddedDocumentBinaryObject"); + if ($embedded_document->length == 0) + { + continue; + } + $binary_object = new Binary_Object; + $binary_object->filecontent = base64_decode($embedded_document[0]->nodeValue); + $binary_object->mimecode = $embedded_document[0]->getAttribute("mimeCode"); + $binary_object->filename = $embedded_document[0]->getAttribute("filename"); + $document_reference = new Document_Reference(); + $document_reference->setId($id) + ->setDescription($description) + ->setBinary_object($binary_object); + $a_document_reference[] = clone $document_reference; + } + } + } + return $a_document_reference; + } + /** + * @brief execute a XPATH query of the DOMDocument and return the result + * or null if nothing found + * @param $query (string) valid XPath Query + * @return null or DOMNodeList or DOMElement + */ + public function get_node($query) + { + + $this->registerNS(); + if ($this->xpath->query($query)->length == 0) + { + return null; + } + return $this->xpath->query($query); + } + /** + * @brief get the node value + * @param $query (string) valid XPath query + * @param $ix (int) the item number + * @return string + */ + function get_node_value($query, $ix = 0) + { + return $this->get_node($query)?->item($ix)?->nodeValue; + } + + /** + * @brief get the customer info from XML + * @return array + */ + function get_customer(): array + { + $result = []; + $result['ID'] = $this->get_node_value("//cac:AccountingCustomerParty[1]/cac:Party[1]/cbc:EndpointID[1]"); + $result['name'] = $this->get_node_value('//cac:AccountingCustomerParty[1]/cac:Party[1]/cac:PartyName[1]/cbc:Name[1]'); + $result['street'] = $this->get_node_value('//cac:AccountingCustomerParty[1]/cac:Party[1]/cac:PostalAddress[1]/cbc:StreetName[1]'); + $result['city'] = $this->get_node_value('//cac:AccountingCustomerParty[1]/cac:Party[1]/cac:PostalAddress[1]/cbc:CityName[1]'); + $result['postcode'] = $this->get_node_value('//cac:AccountingCustomerParty[1]/cac:Party[1]/cac:PostalAddress[1]/cbc:PostalZone[1]'); + $result['country_code'] = $this->get_node_value("//cac:AccountingCustomerParty[1]/cac:Party[1]/cac:PostalAddress[1]/cac:Country[1]/cbc:IdentificationCode[1]"); + $result['company_id'] = $this->get_node_value("//cac:AccountingCustomerParty[1]/cac:Party[1]/cac:PartyTaxScheme[1]/cbc:CompanyID[1]"); + $result['scheme'] = $this->get_node("//cac:AccountingCustomerParty[1]/cac:Party[1]/cbc:EndpointID[1]")[0]->getAttribute("schemeID"); + return $result; + } + + /** + * @brief get the supplier info from XML + * @return array + */ + function get_supplier(): array + { + $result = []; + $result['ID'] = $this->get_node_value("//cac:AccountingSupplierParty[1]/cac:Party[1]/cbc:EndpointID[1]"); + $result['name'] = $this->get_node_value("//cac:AccountingSupplierParty[1]/cac:Party[1]/cac:PartyName[1]/cbc:Name[1]"); + $result['street'] = $this->get_node_value("//cac:AccountingSupplierParty[1]/cac:Party[1]/cac:PostalAddress[1]/cbc:StreetName[1]"); + $result['city'] = $this->get_node_value("//cac:AccountingSupplierParty[1]/cac:Party[1]/cac:PostalAddress[1]/cbc:CityName[1]"); + $result['postcode'] = $this->get_node_value("//cac:AccountingSupplierParty[1]/cac:Party[1]/cac:PostalAddress[1]/cbc:PostalZone[1]"); + $result['country_code'] = $this->get_node_value("//cac:AccountingSupplierParty[1]/cac:Party[1]/cac:PostalAddress[1]/cac:Country[1]/cbc:IdentificationCode[1]"); + $result['company_id'] = $this->get_node_value("//cac:AccountingSupplierParty[1]/cac:Party[1]/cac:PartyTaxScheme[1]/cbc:CompanyID[1]"); + $result['scheme'] = $this->get_node("//cac:AccountingSupplierParty[1]/cac:Party[1]/cbc:EndpointID[1]")[0]->getAttribute("schemeID"); + return $result; + } + + + + /** + * @brief get the Payment Means info from XML + * @return array + */ + function get_payment_mean(): array + { + $result = []; + $result['payment_code'] = $this->get_node_value("//cac:PaymentMeans[1]/cbc:PaymentMeansCode[1]"); + $result['label'] = $this->get_node_value("//cac:PaymentMeans[1]/cbc:PaymentID[1]"); + $result['iban'] = $this->get_node_value("//cac:PaymentMeans[1]/cac:PayeeFinancialAccount[1]/cbc:ID[1]"); + $result['bic'] = $this->get_node_value("//cac:PaymentMeans[1]/cac:PayeeFinancialAccount[1]/cac:FinancialInstitutionBranch[1]/cbc:ID[1]"); + $result['note'] = []; + $note = $this->get_node("//cac:PaymentTerms/cbc:Note"); + if ($note != null) + { + $a = $note->length; + for ($i = 0; $i < $a; $i++) + { + $result['note'][] = $note->item(0)->nodeValue; + } + } + /** + + In geval van betaling binnen 14 dagen is 2% (52.00€) betalingskorting van + toepassing en het te betalen bedrag = 3053.68€ + En cas de paiement dans les 14 jours, l'escompte conditionnel de 2% (52.00€) est appliqué et le montant payable = 3053.68€ + In case of payment within 14 days, 2% (52.00€) conditional cash/payment discount applies and the payable amount = 3053.68€ + + + */ + return $result; + } + + /** + * @brief get the node value from simpleXML + * @param $xml (\SimpleXMLElement) fragment of XML dom + * @param $query (string) XPath query + * @return string + */ + protected function get_simple_xml_value(\SimpleXMLElement $xml, $query): string + { + $this->registerNS($xml); + + $x = $xml->xpath($query); + $result = (count($x) == 0) ? "" : $x[0]; + return (string) $result; + } + + /** + * @brief get the amount summary info from XML + * @return array + */ + function get_amount_summary(): array + { + $result = []; + $node = $this->get_node("//cac:LegalMonetaryTotal"); + $xml = simplexml_import_dom($node->item(0)); + $result['LineExtensionAmount'] = $this->get_simple_xml_value($xml, "cbc:LineExtensionAmount"); + $result['TaxExclusiveAmount'] = $this->get_simple_xml_value($xml, "cbc:TaxExclusiveAmount"); + $result['TaxInclusiveAmount'] = $this->get_simple_xml_value($xml, "cbc:TaxInclusiveAmount"); + $result['PayableAmount'] = $this->get_simple_xml_value($xml, "cbc:PayableAmount"); + $result['AllowanceTotalAmount'] = $this->get_simple_xml_value($xml, "cbc:AllowanceTotalAmount"); + $result['AllowanceTotalAmount'] = ($result['AllowanceTotalAmount'] == "") ? 0 : $result['AllowanceTotalAmount']; + $result['ChargeTotalAmount'] = $this->get_simple_xml_value($xml, "cbc:ChargeTotalAmount"); + $result['ChargeTotalAmount'] = ($result['ChargeTotalAmount'] == "") ? 0 : $result['ChargeTotalAmount']; + return $result; + } + + /** + * @brief get the customer info from XML + * @return array + */ + + /** + * + @code + + false + 64 + Conditional cash/payment discount | Korting contant | Escompte Conditionnel 2% + 4.00 + + S + 6.00 + + VAT + + + + @encode + */ + function get_allowance(): array + { + $result = []; + return $result; + } + /** + * @brief return the code of the document (cbc:"document"TypeCode) + * @return string "document string + cbc:"document"TypeCode" + */ + abstract function get_document_type_code(); + /** + * + * @return array + * @TODODNY + * Implémenter les allowances + */ + function get_info(): array + { + $result = []; + $result['id'] = $this->get_node_value('cbc:ID'); + $result['IssueDate'] = $this->get_node_value('cbc:IssueDate'); + $result['DueDate'] = $this->get_node_value('cbc:DueDate'); + $result['InvoiceTypeCode'] =$this->get_document_type_code(); + $result['DocumentCurrencyCode'] = $this->get_node_value('cbc:DocumentCurrencyCode'); + $result['BuyerReference'] = $this->get_node_value('cbc:BuyerReference'); + $result['ActualDeliveryDate'] = $this->get_node_value('//cac:Delivery[1]/cbc:ActualDeliveryDate[1]'); + /* + + 2018-07-01 + + */ + return $result; + } + + /** + * @brief make a PDF with the information from XMLInvoice + * @param \Database $cn + * @returns \PDF + */ + public function to_pdf(\Database $cn): \PDF + { + $pdf = new \PDF($cn); + $result = $this->get_info(); + + $pdf->setDossierInfo(_(" id ") . " " . $result['id']); + $pdf->AliasNbPages(); + $pdf->setAuthor("Noalyss"); + $pdf->AddPage(); + // 180 mm large + $pdf->setFont("DejaVu", "B", 12); + $pdf->write_cell(60, 4, _("Information facture")); + $pdf->line_new(10); + $pdf->setFont("DejaVu", "", 7); + $pdf->write(4, sprintf(_("Document ID %s"), $result['id'])); + $pdf->ln(); + $pdf->write(4, sprintf(_("Date facture %s"), $result['IssueDate'])); + $pdf->ln(); + $pdf->write(4, sprintf(_("Date échéance %s"), $result['DueDate'])); + $pdf->ln(); + $pdf->write(4, sprintf(_("Type et code document %s"), $result['InvoiceTypeCode'])); + $pdf->ln(); + $pdf->write(4, sprintf(_("Devise document %s"), $result['DocumentCurrencyCode'])); + $pdf->ln(); + $pdf->write(4, sprintf(_("Référence client %s"), $result['BuyerReference'])); + $pdf->ln(); + $pdf->write(4, sprintf(_("Date Livraison %s"), $result['ActualDeliveryDate'])); + $pdf->ln(10); + + $pdf->setFont("DejaVu", "B", 12); + $pdf->write_cell(60, 4, _("Fournisseur")); + $pdf->line_new(12); + $supplier = $this->get_supplier(); + $pdf->setFont("DejaVu", "", 7); + $pdf->write_cell(60, 4, $supplier['name']); + $pdf->write_cell(60, 4, $supplier['company_id']); + $pdf->write_cell(60, 4, $supplier['ID']); + $pdf->line_new(); + $pdf->write_cell(60, 4, $supplier['street']); + $pdf->write_cell(30, 4, $supplier['postcode']); + $pdf->write_cell(60, 4, $supplier['city']); + $pdf->write_cell(20, 4, $supplier['country_code']); + $pdf->line_new(10); + + $customer = $this->get_customer(); + $pdf->setFont("DejaVu", "B", 12); + $pdf->write_cell(60, 4, _("Client")); + $pdf->line_new(10); + $pdf->setFont("DejaVu", "", 7); + $pdf->write_cell(60, 4, $customer['name']); + $pdf->write_cell(60, 4, $customer['company_id']); + $pdf->write_cell(60, 4, $customer['ID']); + $pdf->line_new(); + $pdf->write_cell(60, 4, $customer['street']); + $pdf->write_cell(40, 4, $customer['postcode']); + $pdf->write_cell(60, 4, $customer['city']); + $pdf->write_cell(20, 4, $customer['country_code']); + $pdf->line_new(10); + + $pdf->setFont("DejaVu", "B", 12); + $pdf->write_cell(60, 4, _("Articles")); + $pdf->line_new(10); + $pdf->setFont("DejaVu", "", 7); + $result = $this->get_invoiceLine(); + $pdf->write_cell(50, 4, _("Code"), border: 'B'); + $pdf->write_cell(50, 4, _("Description"), border: 'B'); + $pdf->write_cell(30, 4, _("TVA"), border: 'B', align: 'R'); + $pdf->write_cell(25, 4, _("Quantité"), border: 'B', align: 'R'); + $pdf->write_cell(25, 4, _("Montant HT"), border: 'B', align: 'R'); + $pdf->line_new(); + + $nb_inline = count($result); + for ($i = 0; $i < $nb_inline; $i++) + { + $pdf->write_cell(50, 4, $result[$i]['name']); + $pdf->write_cell(50, 4, $result[$i]['description']); + $pdf->write_cell(25, 4, $result[$i]['tva_percent'], align: 'R'); + $pdf->write_cell(5, 4, $result[$i]['tva_id']); + $pdf->write_cell(25, 4, $result[$i]['quantity'], align: 'R'); + $pdf->write_cell(25, 4, $result[$i]['amount'], align: 'R'); + $pdf->line_new(); + } + $pdf->line_new(10); + + $pdf->setFont("DejaVu", "B", 12); + $pdf->write_cell(60, 4, _("Totaux")); + $pdf->line_new(10); + $pdf->setFont("DejaVu", "", 7); + $result = $this->get_amount_summary(); +// @TODO DNY : Qu'est-ce que LineExtension Amount ?? + $pdf->write_cell(50, 4, _("Base taxe")); + $pdf->write_cell(50, 4, $result['LineExtensionAmount']); + $pdf->line_new(); + $pdf->write_cell(50, 4, _("Total Hors Taxe")); + $pdf->write_cell(50, 4, $result['TaxExclusiveAmount']); + $pdf->line_new(); + $pdf->write_cell(50, 4, _("Total avec Taxe")); + $pdf->write_cell(50, 4, $result['TaxInclusiveAmount']); + $pdf->line_new(); + $pdf->write_cell(50, 4, _("Total à payer")); + $pdf->write_cell(50, 4, $result['PayableAmount']); + $pdf->line_new(); + $pdf->write_cell(50, 4, _("Total réduction")); + $pdf->write_cell(50, 4, $result['AllowanceTotalAmount']); + $pdf->line_new(); + $pdf->write_cell(50, 4, _("Total charge")); + $pdf->write_cell(50, 4, $result['ChargeTotalAmount']); + $pdf->line_new(); + + $pdf->line_new(10); + $pdf->setFont("DejaVu", "B", 12); + $pdf->write_cell(60, 4, _("TVA")); + $pdf->line_new(10); + $pdf->setFont("DejaVu", "", 7); + $result = $this->get_taxes(); + $pdf->write_cell(25, 4, _("% Taxe"), align: 'R', border: 'B'); + $pdf->write_cell(5, 4, _("Code taxe"), border: 'B'); + $pdf->write_cell(50, 4, _("Base"), align: 'R', border: 'B'); + $pdf->write_cell(50, 4, _("Taxe"), align: 'R', border: 'B'); + $pdf->line_new(); + $nb_inline = count($result); + for ($i = 0; $i < $nb_inline; $i++) + { + $pdf->write_cell(25, 4, $result[$i]['tax_percent'], align: 'R'); + $pdf->write_cell(5, 4, $result[$i]['tax_id']); + $pdf->write_cell(50, 4, $result[$i]['taxable_amount'], align: 'R'); + $pdf->write_cell(50, 4, $result[$i]['tax'], align: 'R'); + + $pdf->line_new(); + } + $pdf->line_new(10); + $pdf->setFont("DejaVu", "B", 12); + $pdf->write_cell(60, 4, _("Paiement")); + $pdf->line_new(10); + $pdf->setFont("DejaVu", "", 7); + $result = $this->get_payment_mean(); + $pdf->write_cell(50, 4, _("Code")); + $pdf->write_cell(50, 4, $result['payment_code']); + $pdf->line_new(); + $pdf->write_cell(50, 4, _("IBAN")); + $pdf->write_cell(50, 4, $result['iban']); + $pdf->line_new(); + $pdf->write_cell(50, 4, _("BIC")); + $pdf->write_cell(50, 4, $result['bic']); + $pdf->line_new(); + $pdf->write_cell(50, 4, _("Communication")); + $pdf->write_cell(50, 4, $result['label']); + $pdf->line_new(); + $nb_note = count($result['note']); + for ($i = 0; $i < $nb_note; $i++) + { + $pdf->write_cell(40, 4, _("note") . " " . $i); + $pdf->write_multi(140, 4, str_replace(["\n", "\r", "\t"], " ", $result['note'][$i])); + $pdf->line_new(); + } + /** + * display name of embedded from files + * @var $result(array of Document_Reference) + */ + $result=$this->get_embedded_document(); + if ( count($result) > 0) + { + $pdf->setFont("DejaVu", "B", 12); + $pdf->write_cell(50,4,_('Documents')); + $pdf->line_new(); + $pdf->setFont("DejaVu", "", 7); + $nb_result=count($result); + for ($i=0;$i< $nb_result;$i++) + { + $binary=$result[$i]->getBinary_object(); + $pdf->write_cell(50,4,$result[$i]->getId()); + $pdf->write_cell(50,4,$binary->filename); + $pdf->write_cell(50,4,$result[$i]->getDescription()); + $pdf->line_new(); + + } + + } + return $pdf; + } +} diff --git a/include/XMLDocument/xmlcreditnote.class.php b/include/XMLDocument/xmlcreditnote.class.php new file mode 100644 index 000000000..0ec62cdb1 --- /dev/null +++ b/include/XMLDocument/xmlcreditnote.class.php @@ -0,0 +1,31 @@ + string 'urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2' + 'ns2' => string 'urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2' + 'cac' => string 'urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2' + 'ns4' => string 'urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2' + ) + * + */ +class XMLCreditNote_Reader extends XML_Reader +{ + + public function __construct(\DOMDocument $domDocument) + { + parent::__construct($domDocument); + } + + /** + * @brief Build an XMLInvoice_Reader object from an XML string + * @param $string (string) XML + * @return \Noalyss\XMLDocument\XMLInvoice_Reader + * @throws \Exception if xml not valid + */ + static function build_from_string($string) + { + $dm = new \DOMDocument(); + if ($dm->loadXML($string) != false) + { + return new XMLCreditNote_Reader($dm); + } else + { + throw new \Exception("XC55: not a valid XML", 55); + } + } + + /** + * @brief Build an XMLInvoice_Reader object from an XML file + * @param $filename (string) file and path to the file + * @return \Noalyss\XMLDocument\XMLInvoice_Reader + * @throws \Exception if xml not valid + */ + static function build_from_file($filename) + { + if (!file_exists($filename)) + { + throw new \Exception("XC62: file not found $filename", 62); + } + $dm = new \DOMDocument(); + if ($dm->load($filename) != false) + { + return new XMLCreditNote_Reader($dm); + } else + { + throw new \Exception("XC55: not a valid XML", 55); + } + } + + /** + * @brief retrieve InvoiceLines + @code + + 2 + -3 + -1500 + + 123 + + + Description 2 + item name 2 + + 21382183120983 + + + NO + + + 09348023 + + + S + 25.0 + + VAT + + + + + 500 + + + @endcode + */ + function get_invoiceLine(): array + { + $result = []; + $node = $this->get_node("//ns3:CreditNoteLine"); + for ($e = 0; $e < $node->length; $e++) + { + $row = []; + $xml = simplexml_import_dom($node->item($e)); + $row ['quantity'] = $this->get_node_value("//cbc:CreditedQuantity", $e); + $row ['amount'] = $this->get_node_value("//cbc:LineExtensionAmount", $e); + $row ['description'] = $this->get_node_value("//cac:Item/cbc:Description", $e); + $row ['name'] = $this->get_node_value("//cac:CreditNoteLine/cac:Item/cbc:Name", $e); + $row ['unit_price'] = $this->get_node_value("//cac:CreditNoteLine/cac:Price/cbc:PriceAmount", $e); + $row ['tva_id'] = $this->get_node_value("//cac:CreditNoteLine/cac:Item/cac:ClassifiedTaxCategory/cbc:ID", $e); + $row ['tva_percent'] = $this->get_node_value("//cac:CreditNoteLine/cac:Item/cac:ClassifiedTaxCategory/cbc:Percent", $e); + + $result[] = $row; + } + return $result; + } + /** + * @brief return the code of the document + * @return string credit_node + code + */ + + function get_document_type_code() + { + return "credit note ".$this->get_node_value('cbc:CreditNoteTypeCode'); + } + /** + * @brief before executing xpath->query the namespace must be registered + * the NS are different for each type of doc + */ + + public function get_namespace() + { + + $a_namespace=array( + "cac"=> 'urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2' + ,"cbc"=> 'urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2' + ,"ns4"=>'urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2' + ,"ns2"=>'urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2' + ); + return $a_namespace; + } + /** + * @brief get the Taxes info from XML + * @return array + */ + function get_taxes(): array + { + $result = []; + $node = $this->get_node("//cac:TaxTotal/cac:TaxSubtotal"); + + for ($e = 0; $e < $node->length; $e++) + { + $row = []; + $xml = simplexml_import_dom($node->item($e)); + \Noalyss\Dbg::echo_var(0, htmlspecialchars($xml->asXML())); + // /Invoice/cac:TaxTotal[1]/cac:TaxSubtotal[1]/cbc:TaxableAmount[1] + $this->registerNS($xml); + + $row ['taxable_amount'] = $xml->xpath("//cbc:TaxableAmount")[$e] . ""; + $row ['tax'] = $xml->xpath("//cbc:TaxAmount")[$e] . ""; + $row ['tax_id'] = $xml->xpath("//cac:TaxCategory/cbc:ID")[$e] . ""; + $row ['tax_percent'] = $xml->xpath("//cac:TaxCategory/cbc:Percent")[$e] . ""; + $row ['name'] =$xml->xpath("//cac:CreditNoteLine/cac:Item/cbc:Name")[$e].""; + /** + * @TODODNY + * Implémenter les allowances + */ + $result[] = $row; + } + return $result; + } + +} diff --git a/include/XMLDocument/xmlinvoice_reader.class.php b/include/XMLDocument/xmlinvoice_reader.class.php index 47c96a81e..64cef8549 100644 --- a/include/XMLDocument/xmlinvoice_reader.class.php +++ b/include/XMLDocument/xmlinvoice_reader.class.php @@ -37,50 +37,23 @@ namespace Noalyss\XMLDocument; * Namespace standard (from XSD) * * Array -( - [xml] => http://www.w3.org/XML/1998/namespace - [cbc] => urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2 - [cac] => urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2 - [xmlns] => urn:oasis:names:specification:ubl:schema:xsd:Invoice-2 -) + ( + [xml] => http://www.w3.org/XML/1998/namespace + [cbc] => urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2 + [cac] => urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2 + [xmlns] => urn:oasis:names:specification:ubl:schema:xsd:Invoice-2 + ) * */ -class XMLInvoice_Reader +class XMLInvoice_Reader extends XML_Reader { - protected \DOMDocument $domDocument; - protected readonly \DOMXPath $xpath; - public function __construct(\DOMDocument $domDocument) { - $this->domDocument = $domDocument; - $this->xpath = new \DOMXPath($this->domDocument); - $this->xpath->registerNamespace("ns4", 'urn:oasis:names:specification:ubl:schema:xsd:Invoice-2'); - $this->xpath->registerNamespace("cac", 'urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2'); - $this->xpath->registerNamespace("cbc", 'urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2'); + parent::__construct($domDocument); + } - - /** - * - * @return \DOMDocument - */ - public function get_domDocument(): \DOMDocument - { - return $this->domDocument; - } - - public function get_xpath(): \DOMXPath - { - return $this->xpath; - } - - public function set_domDocument($domDocument) - { - $this->domDocument = $domDocument; - return $this; - } - /** * @brief Build an XMLInvoice_Reader object from an XML string * @param $string (string) XML @@ -98,8 +71,7 @@ class XMLInvoice_Reader throw new \Exception("XR55: not a valid XML",55); } } - - /** + /** * @brief Build an XMLInvoice_Reader object from an XML file * @param $filename (string) file and path to the file * @return \Noalyss\XMLDocument\XMLInvoice_Reader @@ -120,150 +92,6 @@ class XMLInvoice_Reader throw new \Exception("XR55: not a valid XML", 55); } } - - /** - * @brief execute a XPATH query of the DOMDocument and return the result - * or null if nothing found - * @param $query (string) valid XPath Query - * @return null or DOMNodeList or DOMElement - */ - public function get_node($query) - { - if ($this->xpath->query($query)->length == 0) - { - return null; - } - return $this->xpath->query($query); - } - - /** - * @brief returns the embedded document in an array (keys : filecontent (BYTES),mimecode , filename) - * or false if there is no document - * @return bool|array of Document_Reference - * @throws \Exception if there are several documents - * - */ - public function get_embedded_document() - { - $a_document_reference=array(); - foreach (array( - "AdditionalDocumentReference", - "ReceiptDocumentReference", - "StatementDocumentReference", - "OriginatorDocumentReference", - "ContractDocumentReference" - ) - as $document) - { - $document=$this->domDocument->getElementsByTagName($document); - if ( $document->length == 0 ) { - continue; - } - for ($e=0;$e < $document->count();$e++) - { - $item=$document->item($e); - - if ( $item->nodeType == XML_ELEMENT_NODE ) - { - $id=$item->getElementsByTagName("ID")[0]?->nodeValue; - $description=$item->getElementsByTagName("DocumentDescription")[0]?->nodeValue; - $embedded_document=$item->getElementsByTagName("EmbeddedDocumentBinaryObject"); - if ( $embedded_document->length == 0) - { - continue; - } - $binary_object=new Binary_Object; - $binary_object->filecontent=base64_decode($embedded_document[0]->nodeValue); - $binary_object->mimecode=$embedded_document[0]->getAttribute("mimeCode"); - $binary_object->filename=$embedded_document[0]->getAttribute("filename"); - $document_reference=new Document_Reference(); - $document_reference->setId($id) - ->setDescription($description) - ->setBinary_object($binary_object); - $a_document_reference[]=clone $document_reference; - } - } - - } - return $a_document_reference; - } - - /** - * @brief get the node value - * @param $query (string) valid XPath query - * @param $ix (int) the item number - * @return string - */ - function get_node_value($query, $ix = 0) - { - return $this->get_node($query)?->item($ix)?->nodeValue; - } - - /** - * @brief get the customer info from XML - * @return array - */ - function get_customer(): array - { - $result = []; - $result['ID'] = $this->get_node_value("//cac:AccountingCustomerParty[1]/cac:Party[1]/cbc:EndpointID[1]"); - $result['name'] = $this->get_node_value('//cac:AccountingCustomerParty[1]/cac:Party[1]/cac:PartyName[1]/cbc:Name[1]'); - $result['street'] = $this->get_node_value('//cac:AccountingCustomerParty[1]/cac:Party[1]/cac:PostalAddress[1]/cbc:StreetName[1]'); - $result['city'] = $this->get_node_value('//cac:AccountingCustomerParty[1]/cac:Party[1]/cac:PostalAddress[1]/cbc:CityName[1]'); - $result['postcode'] = $this->get_node_value('//cac:AccountingCustomerParty[1]/cac:Party[1]/cac:PostalAddress[1]/cbc:PostalZone[1]'); - $result['country_code'] = $this->get_node_value("//cac:AccountingCustomerParty[1]/cac:Party[1]/cac:PostalAddress[1]/cac:Country[1]/cbc:IdentificationCode[1]"); - $result['company_id'] = $this->get_node_value("//cac:AccountingCustomerParty[1]/cac:Party[1]/cac:PartyTaxScheme[1]/cbc:CompanyID[1]"); - $result['scheme']=$this->get_node("//cac:AccountingCustomerParty[1]/cac:Party[1]/cbc:EndpointID[1]")[0]->getAttribute("schemeID"); - return $result; - } - - /** - * @brief get the supplier info from XML - * @return array - */ - function get_supplier(): array - { - $result = []; - $result['ID'] = $this->get_node_value("//cac:AccountingSupplierParty[1]/cac:Party[1]/cbc:EndpointID[1]"); - $result['name'] = $this->get_node_value("//cac:AccountingSupplierParty[1]/cac:Party[1]/cac:PartyName[1]/cbc:Name[1]"); - $result['street'] = $this->get_node_value("//cac:AccountingSupplierParty[1]/cac:Party[1]/cac:PostalAddress[1]/cbc:StreetName[1]"); - $result['city'] = $this->get_node_value("//cac:AccountingSupplierParty[1]/cac:Party[1]/cac:PostalAddress[1]/cbc:CityName[1]"); - $result['postcode'] = $this->get_node_value("//cac:AccountingSupplierParty[1]/cac:Party[1]/cac:PostalAddress[1]/cbc:PostalZone[1]"); - $result['country_code'] = $this->get_node_value("//cac:AccountingSupplierParty[1]/cac:Party[1]/cac:PostalAddress[1]/cac:Country[1]/cbc:IdentificationCode[1]"); - $result['company_id'] = $this->get_node_value("//cac:AccountingSupplierParty[1]/cac:Party[1]/cac:PartyTaxScheme[1]/cbc:CompanyID[1]"); - $result['scheme']=$this->get_node("//cac:AccountingSupplierParty[1]/cac:Party[1]/cbc:EndpointID[1]")[0]->getAttribute("schemeID"); - return $result; - } - - /** - * @brief get the Taxes info from XML - * @return array - */ - function get_taxes(): array - { - $result = []; - $node = $this->get_node("//cac:TaxTotal/cac:TaxSubtotal"); - - for ($e = 0; $e < $node->length; $e++) - { - $row = []; - $xml = simplexml_import_dom($node->item($e)); - // /Invoice/cac:TaxTotal[1]/cac:TaxSubtotal[1]/cbc:TaxableAmount[1] - - $row ['taxable_amount'] = $xml->xpath("//cbc:TaxableAmount")[$e] . ""; - $row ['tax'] = $xml->xpath("//cbc:TaxAmount")[$e] . ""; - $row ['tax_id'] = $xml->xpath("//cac:TaxCategory/cbc:ID")[$e] . ""; - $row ['tax_percent'] = $xml->xpath("//cac:TaxCategory/cbc:Percent")[$e] . ""; - // $row ['name'] =$xml->xpath("//cac:InvoiceLine/cac:Item/cbc:Name")[$e]."
"; - /** - * @TODODNY - * Implémenter les allowances - */ - $result[] = $row; - } - return $result; - } - /** * @brief retrieve InvoiceLines * @TODO XMLInvoice_Reader->get_invoiceLine * Implémenter les allowances @@ -288,286 +116,58 @@ class XMLInvoice_Reader } return $result; } - /** - * @brief get the Payment Means info from XML - * @return array + * @brief return the code of the document + * @return string invoice + code */ - function get_payment_mean(): array + function get_document_type_code() { - $result = []; - $result['payment_code'] = $this->get_node_value("//cac:PaymentMeans[1]/cbc:PaymentMeansCode[1]"); - $result['label'] = $this->get_node_value("//cac:PaymentMeans[1]/cbc:PaymentID[1]"); - $result['iban'] = $this->get_node_value("//cac:PaymentMeans[1]/cac:PayeeFinancialAccount[1]/cbc:ID[1]"); - $result['bic'] = $this->get_node_value("//cac:PaymentMeans[1]/cac:PayeeFinancialAccount[1]/cac:FinancialInstitutionBranch[1]/cbc:ID[1]"); - $result['note'] = []; - $note = $this->get_node("//cac:PaymentTerms/cbc:Note"); - if ($note != null) - { - $a = $note->length; - for ($i = 0; $i < $a; $i++) - { - $result['note'][] = $note->item(0)->nodeValue; - } - } - /** - - In geval van betaling binnen 14 dagen is 2% (52.00€) betalingskorting van - toepassing en het te betalen bedrag = 3053.68€ - En cas de paiement dans les 14 jours, l'escompte conditionnel de 2% (52.00€) est appliqué et le montant payable = 3053.68€ - In case of payment within 14 days, 2% (52.00€) conditional cash/payment discount applies and the payable amount = 3053.68€ - - - */ - return $result; + return "invoice ".$this->get_node_value('cbc:InvoiceTypeCode'); } - /** - * @brief get the node value from simpleXML - * @param $xml (\SimpleXMLElement) fragment of XML dom - * @param $query (string) XPath query - * @return string + * @brief before executing xpath->query the namespace must be registered + * the NS are different for each type of doc + * @param $xml (null or simpleXML) */ - protected function get_simple_xml_value(\SimpleXMLElement $xml, $query): string + + public function get_namespace() { - $x = $xml->xpath($query); - $result = (count($x) == 0) ? "" : $x[0]; - return (string) $result; - } - - /** - * @brief get the amount summary info from XML - * @return array - */ - function get_amount_summary(): array - { - $result = []; - $node = $this->get_node("//cac:LegalMonetaryTotal"); - $xml = simplexml_import_dom($node->item(0)); - $result['LineExtensionAmount'] = $this->get_simple_xml_value($xml, "cbc:LineExtensionAmount"); - $result['TaxExclusiveAmount'] = $this->get_simple_xml_value($xml, "cbc:TaxExclusiveAmount"); - $result['TaxInclusiveAmount'] = $this->get_simple_xml_value($xml, "cbc:TaxInclusiveAmount"); - $result['PayableAmount'] = $this->get_simple_xml_value($xml, "cbc:PayableAmount"); - $result['AllowanceTotalAmount'] = $this->get_simple_xml_value($xml, "cbc:AllowanceTotalAmount"); - $result['AllowanceTotalAmount'] = ($result['AllowanceTotalAmount'] == "") ? 0 : $result['AllowanceTotalAmount']; - $result['ChargeTotalAmount'] = $this->get_simple_xml_value($xml, "cbc:ChargeTotalAmount"); - $result['ChargeTotalAmount'] = ($result['ChargeTotalAmount'] == "") ? 0 : $result['ChargeTotalAmount']; - return $result; - } - - /** - * @brief get the customer info from XML - * @return array - */ - - /** - * - @code - - false - 64 - Conditional cash/payment discount | Korting contant | Escompte Conditionnel 2% - 4.00 - - S - 6.00 - - VAT - - - - @encode - */ - function get_allowance(): array - { - $result = []; - return $result; - } - - /** - * - * @return array - * @TODODNY - * Implémenter les allowances - */ - function get_info(): array - { - $result = []; - $result['id'] = $this->get_node_value('cbc:ID'); - $result['IssueDate'] = $this->get_node_value('cbc:IssueDate'); - $result['DueDate'] = $this->get_node_value('cbc:DueDate'); - $result['InvoiceTypeCode'] = $this->get_node_value('cbc:InvoiceTypeCode'); - $result['DocumentCurrencyCode'] = $this->get_node_value('cbc:DocumentCurrencyCode'); - $result['BuyerReference'] = $this->get_node_value('cbc:BuyerReference'); - $result['ActualDeliveryDate'] = $this->get_node_value('//cac:Delivery[1]/cbc:ActualDeliveryDate[1]'); - /* - - 2018-07-01 - - */ - return $result; - } - - /** - * @brief make a PDF with the information from XMLInvoice - * @param \Database $cn - * @returns \PDF - */ - public function to_pdf(\Database $cn): \PDF - { - $pdf = new \PDF($cn); - $result = $this->get_info(); + $a_namespace=array( + "cac"=>'urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2' + ,"cbc"=>'urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2' + ,"ns4"=>'urn:oasis:names:specification:ubl:schema:xsd:Invoice-2' + ); + return $a_namespace; - $pdf->setDossierInfo(_(" id ")." ".$result['id']); - $pdf->AliasNbPages(); - $pdf->setAuthor("Noalyss"); - $pdf->AddPage(); - // 180 mm large - $pdf->setFont("DejaVu", "B", 12); - $pdf->write_cell(60, 4, _("Information facture")); - $pdf->line_new(10); - $pdf->setFont("DejaVu", "", 7); - $pdf->write(4, sprintf(_("Document ID %s"), $result['id'])); - $pdf->ln(); - $pdf->write(4, sprintf(_("Date facture %s"), $result['IssueDate'])); - $pdf->ln(); - $pdf->write(4, sprintf(_("Date échéance %s"), $result['DueDate'])); - $pdf->ln(); - $pdf->write(4, sprintf(_("Code facture %s"), $result['InvoiceTypeCode'])); - $pdf->ln(); - $pdf->write(4, sprintf(_("Devise docuemnt %s"), $result['DocumentCurrencyCode'])); - $pdf->ln(); - $pdf->write(4, sprintf(_("Référence client %s"), $result['BuyerReference'])); - $pdf->ln(); - $pdf->write(4, sprintf(_("Date Livraison %s"), $result['ActualDeliveryDate'])); - $pdf->ln(10); + } + /** + * @brief get the Taxes info from XML + * @return array + */ + function get_taxes(): array + { + $result = []; + $node = $this->get_node("//cac:TaxTotal/cac:TaxSubtotal"); - $pdf->setFont("DejaVu", "B", 12); - $pdf->write_cell(60, 4, _("Fournisseur")); - $pdf->line_new(12); - $supplier = $this->get_supplier(); - $pdf->setFont("DejaVu", "", 7); - $pdf->write_cell(60, 4, $supplier['name']); - $pdf->write_cell(60, 4, $supplier['company_id']); - $pdf->write_cell(60, 4, $supplier['ID']); - $pdf->line_new(); - $pdf->write_cell(60, 4, $supplier['street']); - $pdf->write_cell(30, 4, $supplier['postcode']); - $pdf->write_cell(60, 4, $supplier['city']); - $pdf->write_cell(20, 4, $supplier['country_code']); - $pdf->line_new(10); - - $customer = $this->get_customer(); - $pdf->setFont("DejaVu", "B", 12); - $pdf->write_cell(60, 4, _("Client")); - $pdf->line_new(10); - $pdf->setFont("DejaVu", "", 7); - $pdf->write_cell(60, 4, $customer['name']); - $pdf->write_cell(60, 4, $customer['company_id']); - $pdf->write_cell(60, 4, $customer['ID']); - $pdf->line_new(); - $pdf->write_cell(60, 4, $customer['street']); - $pdf->write_cell(40, 4, $customer['postcode']); - $pdf->write_cell(60, 4, $customer['city']); - $pdf->write_cell(20, 4, $customer['country_code']); - $pdf->line_new(10); - - $pdf->setFont("DejaVu", "B", 12); - $pdf->write_cell(60, 4, _("Articles")); - $pdf->line_new(10); - $pdf->setFont("DejaVu", "", 7); - $result = $this->get_invoiceLine(); - $pdf->write_cell(50, 4, _("Code"), border: 'B'); - $pdf->write_cell(50, 4, _("Description"), border: 'B'); - $pdf->write_cell(30, 4, _("TVA"), border: 'B', align: 'R'); - $pdf->write_cell(25, 4, _("Quantité"), border: 'B', align: 'R'); - $pdf->write_cell(25, 4, _("Montant HT"), border: 'B', align: 'R'); - $pdf->line_new(); - - $nb_inline = count($result); - for ($i = 0; $i < $nb_inline; $i++) + for ($e = 0; $e < $node->length; $e++) { - $pdf->write_cell(50, 4, $result[$i]['name']); - $pdf->write_cell(50, 4, $result[$i]['description']); - $pdf->write_cell(25, 4, $result[$i]['tva_percent'], align: 'R'); - $pdf->write_cell(5, 4, $result[$i]['tva_id']); - $pdf->write_cell(25, 4, $result[$i]['quantity'], align: 'R'); - $pdf->write_cell(25, 4, $result[$i]['amount'], align: 'R'); - $pdf->line_new(); - } - $pdf->line_new(10); + $row = []; + $xml = simplexml_import_dom($node->item($e)); + \Noalyss\Dbg::echo_var(0, htmlspecialchars($xml->asXML())); + // /Invoice/cac:TaxTotal[1]/cac:TaxSubtotal[1]/cbc:TaxableAmount[1] + $this->registerNS($xml); - $pdf->setFont("DejaVu", "B", 12); - $pdf->write_cell(60, 4, _("Totaux")); - $pdf->line_new(10); - $pdf->setFont("DejaVu", "", 7); - $result = $this->get_amount_summary(); -// @TODO DNY : Qu'est-ce que LineExtension Amount ?? - $pdf->write_cell(50, 4, _("Base taxe")); - $pdf->write_cell(50, 4, $result['LineExtensionAmount']); - $pdf->line_new(); - $pdf->write_cell(50, 4, _("Total Hors Taxe")); - $pdf->write_cell(50, 4, $result['TaxExclusiveAmount']); - $pdf->line_new(); - $pdf->write_cell(50, 4, _("Total avec Taxe")); - $pdf->write_cell(50, 4, $result['TaxInclusiveAmount']); - $pdf->line_new(); - $pdf->write_cell(50, 4, _("Total à payer")); - $pdf->write_cell(50, 4, $result['PayableAmount']); - $pdf->line_new(); - $pdf->write_cell(50, 4, _("Total réduction")); - $pdf->write_cell(50, 4, $result['AllowanceTotalAmount']); - $pdf->line_new(); - $pdf->write_cell(50, 4, _("Total charge")); - $pdf->write_cell(50, 4, $result['ChargeTotalAmount']); - $pdf->line_new(); - - $pdf->line_new(10); - $pdf->setFont("DejaVu", "B", 12); - $pdf->write_cell(60, 4, _("TVA")); - $pdf->line_new(10); - $pdf->setFont("DejaVu", "", 7); - $result = $this->get_taxes(); - $pdf->write_cell(25, 4, _("% Taxe"), align: 'R', border: 'B'); - $pdf->write_cell(5, 4, _("Code taxe"), border: 'B'); - $pdf->write_cell(50, 4, _("Base"), align: 'R', border: 'B'); - $pdf->write_cell(50, 4, _("Taxe"), align: 'R', border: 'B'); - $pdf->line_new(); - $nb_inline = count($result); - for ($i = 0; $i < $nb_inline; $i++) - { - $pdf->write_cell(25, 4, $result[$i]['tax_percent'], align: 'R'); - $pdf->write_cell(5, 4, $result[$i]['tax_id']); - $pdf->write_cell(50, 4, $result[$i]['taxable_amount'], align: 'R'); - $pdf->write_cell(50, 4, $result[$i]['tax'], align: 'R'); - - $pdf->line_new(); + $row ['taxable_amount'] = $xml->xpath("//cbc:TaxableAmount")[$e] . ""; + $row ['tax'] = $xml->xpath("//cbc:TaxAmount")[$e] . ""; + $row ['tax_id'] = $xml->xpath("//cac:TaxCategory/cbc:ID")[$e] . ""; + $row ['tax_percent'] = $xml->xpath("//cac:TaxCategory/cbc:Percent")[$e] . ""; + $row ['name'] =$xml->xpath("//cac:InvoiceLine/cac:Item/cbc:Name")[$e].""; + /** + * @TODODNY + * Implémenter les allowances + */ + $result[] = $row; } - $pdf->line_new(10); - $pdf->setFont("DejaVu", "B", 12); - $pdf->write_cell(60, 4, _("Paiement")); - $pdf->line_new(10); - $pdf->setFont("DejaVu", "", 7); - $result = $this->get_payment_mean(); - $pdf->write_cell(50, 4, _("Code")); - $pdf->write_cell(50, 4, $result['payment_code']); - $pdf->line_new(); - $pdf->write_cell(50, 4, _("IBAN")); - $pdf->write_cell(50, 4, $result['iban']); - $pdf->line_new(); - $pdf->write_cell(50, 4, _("BIC")); - $pdf->write_cell(50, 4, $result['bic']); - $pdf->line_new(); - $pdf->write_cell(50, 4, _("Communication")); - $pdf->write_cell(50, 4, $result['label']); - $pdf->line_new(); - $nb_note = count($result['note']); - for ($i = 0; $i < $nb_note; $i++) - { - $pdf->write_cell(40, 4, _("note") . " " . $i); - $pdf->write_multi(140, 4, str_replace(["\n","\r","\t"]," ",$result['note'][$i])); - $pdf->line_new(); - } - return $pdf; + return $result; } } diff --git a/include/constant.php b/include/constant.php index 0f37c02b8..b6c622733 100644 --- a/include/constant.php +++ b/include/constant.php @@ -443,7 +443,9 @@ function noalyss_class_autoloader($class) 'noalyss\smtpmail'=>'lib/smtpmail.class.php', 'noalyss\iban_number'=>'lib/iban_number.class.php', 'noalyss\xmldocument\document_reference'=>'XMLDocument/document_reference_type.class.php', - 'noalyss\xmldocument\binary_object'=>'XMLDocument/document_reference_type.class.php' + 'noalyss\xmldocument\binary_object'=>'XMLDocument/document_reference_type.class.php', + 'noalyss\xmldocument\xml_reader'=>'XMLDocument/xml_reader.class.php', + "noalyss\xmldocument\xmlcreditnote_reader"=>"XMLDocument/xmlcreditnote_reader.class.php" ); if (isset ($aClass[$class])) { require_once NOALYSS_INCLUDE . "/" . $aClass[$class]; From d0ef7a305739dd79b76dc08a3ef72dda276c3b16 Mon Sep 17 00:00:00 2001 From: sparkyx Date: Wed, 12 Nov 2025 11:00:59 +0100 Subject: [PATCH 06/19] use protected instead of private, remove debug info --- include/lib/pdf_core.class.php | 8 ++++---- include/template/ledger_detail_ven.php | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/lib/pdf_core.class.php b/include/lib/pdf_core.class.php index 754ea5fdc..8392ccf62 100644 --- a/include/lib/pdf_core.class.php +++ b/include/lib/pdf_core.class.php @@ -64,7 +64,7 @@ class PDF_Core extends TFPDF - private $cells=array(); + protected $cells=array(); protected $bigger; public function __construct ( $orientation = 'P', $unit = 'mm', $format = 'A4') @@ -119,7 +119,7 @@ class PDF_Core extends TFPDF * @param $p_text String * @param $p_colSize size of the column in User Unit */ - private function count_nb_row($p_text,$p_colSize) + protected function count_nb_row($p_text,$p_colSize) { // If colSize is bigger than the size of the string then it takes 1 line if ( $this->GetStringWidth($p_text) <= $p_colSize) return 1; @@ -153,7 +153,7 @@ class PDF_Core extends TFPDF * Check if a page must be added due a MultiCell * @return boolean */ - private function check_page_add() + protected function check_page_add() { // break on page $size=count($this->cells); @@ -260,7 +260,7 @@ class PDF_Core extends TFPDF } $this->cells=array(); } - private function add_cell(Cellule $Ce) + protected function add_cell(Cellule $Ce) { $size=count($this->cells); $this->cells[$size]=$Ce; diff --git a/include/template/ledger_detail_ven.php b/include/template/ledger_detail_ven.php index 64fcd1f9b..f13d4653c 100644 --- a/include/template/ledger_detail_ven.php +++ b/include/template/ledger_detail_ven.php @@ -105,7 +105,7 @@ $str_anc = ""; $ipaid = new ICheckBox("ipaid", 'paid'); $ipaid->selected = ($obj->det->jr_rapt == 'paid'); echo $ipaid->input(); - if ( DEBUGNOALYSS > 0) echo \Noalyss\Dbg::hidden_info("obj", $obj); + ?> From aaf9a1685102274d59a76f4b966d77fdf781b2d0 Mon Sep 17 00:00:00 2001 From: sparkyx Date: Wed, 12 Nov 2025 11:32:19 +0100 Subject: [PATCH 07/19] e-invoice : import creditNote and create a PDF --- html/js/acc_ledger.js | 28 +++++++++++++ include/XMLDocument/xml_reader.class.php | 40 +++++++++++++++++- .../xmlcreditnote_reader.class.php | 8 ++-- .../XMLDocument/xmlinvoice_reader.class.php | 10 +++-- include/ajax/ajax_ledger.php | 7 ++++ include/class/acc_document.class.php | 41 ++++++++++++++++++- include/template/ledger_detail_bottom.php | 8 +++- include/template/ledger_detail_sup_files.php | 22 +++++++--- 8 files changed, 147 insertions(+), 17 deletions(-) diff --git a/html/js/acc_ledger.js b/html/js/acc_ledger.js index 8f6abcd9f..f23e31f10 100644 --- a/html/js/acc_ledger.js +++ b/html/js/acc_ledger.js @@ -2218,3 +2218,31 @@ Supplement_Document.save_file=function(form_dom_id) remove_waiting_box(); return false; } +Supplement_Document.refresh_list=function (nDossier,sDiv,nJR_id) +{ + try + { + var queryString = { + gDossier:nDossier, + div:sDiv, + jr_id:nJR_id, + op:"ledger", + act:"refresh_file" + }; + var action = new Ajax.Request( + "ajax_misc.php", + { + method: 'GET', + parameters: queryString, + onSuccess: function (req) { + $("supplement_div_list"+sDiv).update(req.responseText); + + } + } + ); + } catch (e) + { + console.error(e.message); + } + +} diff --git a/include/XMLDocument/xml_reader.class.php b/include/XMLDocument/xml_reader.class.php index b0d5623f2..f4d458c2a 100644 --- a/include/XMLDocument/xml_reader.class.php +++ b/include/XMLDocument/xml_reader.class.php @@ -94,7 +94,27 @@ abstract class XML_Reader * @return \Noalyss\XMLDocument\XMLInvoice_Reader * @throws \Exception if xml not valid */ - abstract static function build_from_file($filename); + static function build_from_file($filename) + { + if (!file_exists($filename)) + { + throw new \Exception("XR101: file not found $filename", 101); + } + $dm = new \DOMDocument(); + if ($dm->load($filename) == false) + { + throw new \Exception("XR106: cannot load file", 106); + } + if ( $dm->getElementsByTagName("CreditNote")->length == 1) + { + return new XMLCreditNote_Reader($dm); + } + if ( $dm->getElementsByTagName("Invoice")->length == 1) + { + return new XMLInvoice_Reader($dm); + } + throw new \Exception("XR116: unknown XML", 116); + } /** * @brief Build an XMLInvoice_Reader object from an XML string @@ -102,7 +122,23 @@ abstract class XML_Reader * @return \Noalyss\XMLDocument\XMLInvoice_Reader * @throws \Exception if xml not valid */ - abstract static function build_from_string($string); + static function build_from_string($string) + { + $dm = new \DOMDocument(); + if ($dm->loadXML($string) == false) + { + throw new \Exception("XR130: cannot load XML", 130); + } + if ( $dm->getElementsByTagName("CreditNote")->length == 1) + { + return new XMLCreditNote_Reader($dm); + } + if ( $dm->getElementsByTagName("Invoice")->length == 1) + { + return new XMLInvoice_Reader($dm); + } + throw new \Exception("XR120: unknown XML", 120); + } diff --git a/include/XMLDocument/xmlcreditnote_reader.class.php b/include/XMLDocument/xmlcreditnote_reader.class.php index 0dd7052c0..747df8fb6 100644 --- a/include/XMLDocument/xmlcreditnote_reader.class.php +++ b/include/XMLDocument/xmlcreditnote_reader.class.php @@ -134,7 +134,7 @@ class XMLCreditNote_Reader extends XML_Reader function get_invoiceLine(): array { $result = []; - $node = $this->get_node("//ns3:CreditNoteLine"); + $node = $this->get_node("//cac:CreditNoteLine"); for ($e = 0; $e < $node->length; $e++) { $row = []; @@ -189,7 +189,6 @@ class XMLCreditNote_Reader extends XML_Reader { $row = []; $xml = simplexml_import_dom($node->item($e)); - \Noalyss\Dbg::echo_var(0, htmlspecialchars($xml->asXML())); // /Invoice/cac:TaxTotal[1]/cac:TaxSubtotal[1]/cbc:TaxableAmount[1] $this->registerNS($xml); @@ -197,7 +196,10 @@ class XMLCreditNote_Reader extends XML_Reader $row ['tax'] = $xml->xpath("//cbc:TaxAmount")[$e] . ""; $row ['tax_id'] = $xml->xpath("//cac:TaxCategory/cbc:ID")[$e] . ""; $row ['tax_percent'] = $xml->xpath("//cac:TaxCategory/cbc:Percent")[$e] . ""; - $row ['name'] =$xml->xpath("//cac:CreditNoteLine/cac:Item/cbc:Name")[$e].""; + if (isset($xml->xpath("//cac:CreditNoteLine/cac:Item/cbc:Name")[$e])) + $row ['name'] =$xml->xpath("//cac:CreditNoteLine/cac:Item/cbc:Name")[$e].""; + else + $row['name']=""; /** * @TODODNY * Implémenter les allowances diff --git a/include/XMLDocument/xmlinvoice_reader.class.php b/include/XMLDocument/xmlinvoice_reader.class.php index 64cef8549..fd1d346bb 100644 --- a/include/XMLDocument/xmlinvoice_reader.class.php +++ b/include/XMLDocument/xmlinvoice_reader.class.php @@ -153,15 +153,19 @@ class XMLInvoice_Reader extends XML_Reader { $row = []; $xml = simplexml_import_dom($node->item($e)); - \Noalyss\Dbg::echo_var(0, htmlspecialchars($xml->asXML())); // /Invoice/cac:TaxTotal[1]/cac:TaxSubtotal[1]/cbc:TaxableAmount[1] $this->registerNS($xml); - + $row ['taxable_amount'] = $xml->xpath("//cbc:TaxableAmount")[$e] . ""; $row ['tax'] = $xml->xpath("//cbc:TaxAmount")[$e] . ""; $row ['tax_id'] = $xml->xpath("//cac:TaxCategory/cbc:ID")[$e] . ""; $row ['tax_percent'] = $xml->xpath("//cac:TaxCategory/cbc:Percent")[$e] . ""; - $row ['name'] =$xml->xpath("//cac:InvoiceLine/cac:Item/cbc:Name")[$e].""; + if (isset($xml->xpath("//cac:InvoiceLine/cac:Item/cbc:Name")[$e])) + { + $row ['name'] =$xml->xpath("//cac:InvoiceLine/cac:Item/cbc:Name")[$e].""; + }else { + $row ['name'] =""; + } /** * @TODODNY * Implémenter les allowances diff --git a/include/ajax/ajax_ledger.php b/include/ajax/ajax_ledger.php index 515c4dd99..fa2fae901 100644 --- a/include/ajax/ajax_ledger.php +++ b/include/ajax/ajax_ledger.php @@ -654,6 +654,13 @@ EOF; $cn->commit(); return; + case 'refresh_file': + //------------------------------------------------ + // refresh list of suppemental files + //------------------------------------------------ + Acc_Document::display_supplementary_doc($cn, $http->get("div"), $http->get("jr_id")); + + return; } $html = escape_xml($html); if (!headers_sent()) { diff --git a/include/class/acc_document.class.php b/include/class/acc_document.class.php index 111df5d5d..a0a405847 100644 --- a/include/class/acc_document.class.php +++ b/include/class/acc_document.class.php @@ -314,7 +314,7 @@ class Acc_Document extends Document { jr_id=$2", [$oid,$this->d_id]); - $xmlreader= XMLInvoice_Reader::build_from_file($a_file['filename']); + $xmlreader= \Noalyss\XMLDocument\XML_Reader::build_from_file($a_file['filename']); //@var $embedded_file (array) keys = filecontent: binary data //,mimecode mimetype and filename (string) @@ -395,5 +395,42 @@ class Acc_Document extends Document { .''; return $r; } - + static function display_supplementary_doc($cn,$div,$jr_id) + { + $gDossier=\Dossier::id(); + $q=new Jrn_Sup_Document_SQL($cn); + $a_row=$q->collect_objects(" where jr_id=$1 order by js_cbc_id", [$jr_id]); + + if ( count($a_row) > 0) + { + foreach ($a_row as $item) { + $export="export.php?"; + $script="Supplement_Document.delete_document('$gDossier','$div','{$item->js_id}','$jr_id')"; + $rowid=sprintf("row_js_%s_%s",$div,$item->js_id); + // @var $download (url) to send file + $download="export.php?". http_build_query( + [ + "act"=>"RAW:suppl-document" + ,"js_id"=>$item->js_id + ,"gDossier"=>$gDossier + ]); + ?> +
+ O +
+ js_description?> +
+
+ +
+
+
  • - + + + + +
  • @@ -301,7 +305,7 @@ require_once NOALYSS_TEMPLATE.'/ledger_detail_file.php'; ?> diff --git a/include/template/ledger_detail_sup_files.php b/include/template/ledger_detail_sup_files.php index 1506c1590..6bd2b1f93 100644 --- a/include/template/ledger_detail_sup_files.php +++ b/include/template/ledger_detail_sup_files.php @@ -34,14 +34,12 @@ /** * show existing supplemental files for this operation */ - +Acc_Document::display_supplementary_doc($cn,$div,$jr_id); +/* $q=new Jrn_Sup_Document_SQL($cn); $a_row=$q->collect_objects(" where jr_id=$1 order by js_cbc_id", [$jr_id]); -$javascript=sprintf("Supplement_Document.input_file('%s','%s','%s')", - $gDossier, - $div, - $jr_id); + if ( count($a_row) > 0) { foreach ($a_row as $item) { @@ -79,6 +77,20 @@ if ( count($a_row) > 0) ,'operation_id'=>$jr_id ]); + * + */ +$javascript=sprintf("Supplement_Document.input_file('%s','%s','%s')", + $gDossier, + $div, + $jr_id); + + $download="export.php?". http_build_query( + [ + "act"=>"RAW:suppl-document" + ,"js_id"=>0 + ,"gDossier"=>$gDossier + ,'operation_id'=>$jr_id + ]); ?> From 5bfffcce9744691490c822910a2b19df402aed9d Mon Sep 17 00:00:00 2001 From: sparkyx Date: Mon, 17 Nov 2025 10:33:23 +0100 Subject: [PATCH 08/19] Code improvement and ergonomy for menu display --- html/css/style-classic7.css | 13 +++++++- html/do.php | 3 +- include/anc_od.inc.php | 4 +-- include/lib/ac_common.php | 64 +++++++++++++++++++++++++++++++++---- include/lib/user_menu.php | 2 +- include/template/menu.php | 3 +- include/upgrade.inc.php | 2 +- 7 files changed, 77 insertions(+), 14 deletions(-) diff --git a/html/css/style-classic7.css b/html/css/style-classic7.css index aeb0271b2..c0429d58b 100644 --- a/html/css/style-classic7.css +++ b/html/css/style-classic7.css @@ -3519,7 +3519,18 @@ margin:1px; .nav-level2 .active { border-radius:0px !important; } - +/******************************************/ +/* MENU LEVEL > 3 */ +/******************************************/ +.nav-level4{ + --border-size:2px; +} +.nav-level4 .nav-item:hover{ + background-color: aliceblue; +} +.nav-level4 .active { + background-color: aliceblue; +} /****************************************/ /* Module for smartphone menu */ /***************************************/ diff --git a/html/do.php b/html/do.php index e09e51cbc..7db6f5f2a 100644 --- a/html/do.php +++ b/html/do.php @@ -281,7 +281,8 @@ if (isset($_REQUEST['ac'])) // Show module and highligt selected one show_module($module_id); - + global $level; + $level = 0; show_menu( $amenu_id[0]['pm_id_v3']); show_menu( $amenu_id[0]['pm_id_v2']); diff --git a/include/anc_od.inc.php b/include/anc_od.inc.php index c7bad6b60..416e948c3 100644 --- a/include/anc_od.inc.php +++ b/include/anc_od.inc.php @@ -50,7 +50,7 @@ echo ' //---------------------------------------------------------------------- // show the menu //---------------------------------------------------------------------- -echo ShowItem(array( +echo show_menu_extension(array( array( "?".http_build_query(["ac"=>$http->request("ac"),"new"=>1,"gDossier"=>$dossier_id]), _("Nouveau"), @@ -61,7 +61,7 @@ echo ShowItem(array( _("Liste"), _("Liste opérations") ) -), "H", "nav-item", "nav-link", "", "nav nav-pills nav-level3"); +)); //---------------------------------------------------------------------- diff --git a/include/lib/ac_common.php b/include/lib/ac_common.php index c761b969c..82152711c 100644 --- a/include/lib/ac_common.php +++ b/include/lib/ac_common.php @@ -519,7 +519,57 @@ function sql_string($p_string) $p_string = noalyss_str_replace('\\', '\\\\', $p_string); return $p_string; } +/** + * @brief Same menu for all extensions, with the right level, it calls + * ShowItem with the right parameters + * @global $level (int) global variable of the menu level + * @param $p_array (array) + * @param $default (string) selected item + * @param $p_extra (string) extra code for the table tag (CSS or javascript) + * @see ShowItem + */ +function show_menu_extension($p_array,$default="",$p_extra="") +{ + global $level; + + + $level++; + switch ($level) { + case 3: + $p_dir='H'; + $class="nav-item nav-item-underline"; + $class_ref="nav-link"; + $p_extra="noprint nav nav-pills nav-level3"; + $class_div="menu3"; + break; + case 2: + $p_dir='H'; + $class="nav-item nav-item-underline"; + $class_ref="nav-link"; + $p_extra="noprint nav nav-pills nav-level2"; + $class_div="menu2"; + break; + case 1: + $p_dir='H'; + $class="nav-item nav-item-underline"; + $class_ref="nav-link"; + $p_extra='noprint nav nav-pills nav-fill flex-row '; + $class_div="top_menu"; + break; + default: + $p_dir='H'; + $class="nav-item nav-item-underline"; + $class_ref="nav-link"; + $p_extra="noprint nav nav-level4"; + $class_div="menu3"; + break; + } + return "
    " + . ShowItem($p_array,$p_dir,$class,$class_ref,$default,$p_extra) + ."
    "; + +} /** * \brief store the string which print * the content of p_array in a table @@ -536,6 +586,7 @@ function sql_string($p_string) function ShowItem($p_array, $p_dir='V', $class="nav-item", $class_ref="nav-link", $default="", $p_extra="nav nav-pills nav-fill") { + $ret = ''; // for comptability with old application mtitle for anchor is replace by nav-link @@ -543,9 +594,9 @@ function ShowItem($p_array, $p_dir='V', $class="nav-item", $class_ref="nav-link" // direction Vertical if ($p_dir == 'V') { - $ret .= "
      "; + $ret .= "
        "; } else { - $ret .= "
          "; + $ret .= "
            "; } @@ -565,11 +616,11 @@ function ShowItem($p_array, $p_dir='V', $class="nav-item", $class_ref="nav-link" if ($set==$default) { - $ret.=''; + $ret.='
          • '.$href[1].'
          • '; } else { - $ret.=''; + $ret.='
          • '.$href[1].'
          • '; } } @@ -1072,8 +1123,7 @@ function find_default_module() function show_menu($module) { if ($module == 0)return; - static $level=0; - global $g_user; + global $level, $g_user; $http=new HttpInput(); $access_code=$http->request("ac"); $cn = Dossier::connect(); @@ -1100,7 +1150,7 @@ function show_menu($module) if (!empty($amenu) && count($amenu) > 1) { $a_style_menu=array('topmenu','menu2','menu3'); - if ( $level > count($a_style_menu)) + if ( $level >= count($a_style_menu)) $style_menu='menu3'; else { $style_menu=$a_style_menu[$level]; diff --git a/include/lib/user_menu.php b/include/lib/user_menu.php index 4b189f892..5148f8666 100644 --- a/include/lib/user_menu.php +++ b/include/lib/user_menu.php @@ -101,7 +101,7 @@ function MenuAdmin() ); } - $menu=ShowItem($item,'H',"nav-item","nav-link",$def,'nav nav-pills nav-fill '); + $menu=show_menu_extension($item,$def); return $menu; } diff --git a/include/template/menu.php b/include/template/menu.php index 64345b00c..9fa4447ad 100644 --- a/include/template/menu.php +++ b/include/template/menu.php @@ -36,7 +36,7 @@ $js=""; $class_list_element="nav-item nav-item-underline"; $class_link="nav-link"; - + $url=""; if ( $amenu[$i]['me_url']!='') { $url=$amenu[$i]['me_url']; @@ -86,6 +86,7 @@ ?> diff --git a/include/upgrade.inc.php b/include/upgrade.inc.php index dc2c1495f..28620b488 100644 --- a/include/upgrade.inc.php +++ b/include/upgrade.inc.php @@ -36,7 +36,7 @@ $menu=array( ); $sb=$http->request("sb", "string", "application"); echo '
            '; -echo ShowItem($menu, "H", "nav-item", "nav-link", $sb,"nav nav-pills nav-level2"); +echo show_menu_extension($menu, $sb); echo '
            '; $sc=$http->get("sc", "string", "none"); From 3d2c9c53cf188937d670519b8f8e3c42fd93942e Mon Sep 17 00:00:00 2001 From: sparkyx Date: Mon, 17 Nov 2025 11:41:43 +0100 Subject: [PATCH 09/19] PHP8.2 deprecated: dynamic properties + cosmetic menu --- include/anc_od.inc.php | 17 +++++++++-------- include/class/anc_balance_double.class.php | 4 +++- include/class/anc_table.class.php | 4 ++++ include/class/periode.class.php | 2 +- 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/include/anc_od.inc.php b/include/anc_od.inc.php index 416e948c3..e5892d216 100644 --- a/include/anc_od.inc.php +++ b/include/anc_od.inc.php @@ -41,12 +41,9 @@ if ( ! $m ) } $dossier_id=Dossier::id(); - - -echo ' -'; console.error(e.message); } } +var vat_code = new VAT_Code('') diff --git a/sql/upgrade.sql b/sql/upgrade.sql index 8b45c75cc..13255c7fa 100644 --- a/sql/upgrade.sql +++ b/sql/upgrade.sql @@ -244,23 +244,6 @@ insert into parameter_extra(pe_code,pe_label) values ('SIRET','n° SIRET') on co ALTER TABLE TVA_RATE ADD TVA_PEPPOL_CODE varchar(3); comment on column tva_rate.Tva_peppol_code is 'Code for Peppol : S standard,Z zéro, AE Autoliquidation ,K autoliquidation intra, G : exempté TVA pour export'; -drop VIEW public.v_tva_rate; - -CREATE OR REPLACE VIEW public.v_tva_rate -AS SELECT tva_id, - tva_rate, - tva_code, - tva_label, - tva_comment, - tva_reverse_account, - split_part(tva_poste, ','::text, 1) AS tva_purchase, - split_part(tva_poste, ','::text, 2) AS tva_sale, - tva_both_side, - tva_payment_purchase, - tva_payment_sale, - tva_peppol_code -FROM tva_rate; - create table quantity_code_ref( qc_code text not null primary key, qc_label text not null @@ -499,3 +482,137 @@ INSERT INTO public.menu_ref (me_code, me_menu, me_file, me_url, me_description, VALUES('RAW:suppl-document', 'Exporte Document supplementaire', 'export_suppl-document.php', NULL, 'télécharge le document supp', NULL, NULL, 'PR', NULL); insert into profile_menu (me_code,p_id, p_type_display) select 'RAW:suppl-document',p_id,'P' from profile; + + +alter table tva_rate add column vx_code text; + +COMMENT ON COLUMN public.tva_rate.vx_code IS 'Peppol: exemption code for VAT'; + + +-- public.vatex_code definition + +-- Drop table + +-- DROP TABLE public.vatex_code; + + +CREATE TABLE public.vatex_code ( vx_code text primary key, vx_country varchar(5) , vx_code_name varchar(256) , vx_description text, vx_remark text); + +INSERT INTO public.vatex_code (vx_code,vx_country,vx_code_name,vx_description,vx_remark) VALUES + ('VATEX-EU-79-C','eu','Exonération en vertu de l''article 79, point c), de la directive 2006/112/CE du Conseil','Exemptions relatives au remboursement des dépenses.','Le remboursement des dépenses ne constitue pas une exonération au sens de la directive TVA, mais peut être traité comme tel dans le cadre de la norme EN16931.'), + ('VATEX-EU-132','eu','Exonération fondée sur l''article 132 de la directive 2006/112/CE du Conseil','Exemptions pour certaines activités d''intérêt public.',''), + ('VATEX-EU-132-1A','eu','Exonération en vertu de l''article 132, paragraphe 1, point a), de la directive 2006/112/CE du Conseil','La fourniture par les services postaux publics de services autres que le transport de passagers et les services de télécommunications, ainsi que la fourniture de biens accessoires à ces services.',''), + ('VATEX-EU-132-1B','eu','Exonération en vertu de l''article 132, paragraphe 1, point b), de la directive 2006/112/CE du Conseil','Soins hospitaliers et médicaux et activités étroitement liées exercées par des organismes de droit public ou, dans des conditions sociales comparables à celles applicables aux organismes de droit public, par des hôpitaux, des centres de traitement ou de diagnostic médical et d''autres établissements dûment reconnus de nature similaire.',''), + ('VATEX-EU-132-1C','eu','Exonération sur la base de l''article 132, paragraphe 1, point c), de la directive 2006/112/CE du Conseil','La prestation de soins médicaux dans l''exercice des professions médicales et paramédicales telles que définies par l''État membre concerné.',''), + ('VATEX-EU-132-1D','eu','Exonération en vertu de l''article 132, paragraphe 1, point d), de la directive 2006/112/CE du Conseil','L''approvisionnement en organes humains, en sang et en lait.',''), + ('VATEX-EU-132-1E','eu','Exonération en vertu de l''article 132, paragraphe 1, point e), de la directive 2006/112/CE du Conseil','La prestation de services par les techniciens dentaires dans le cadre de leur activité professionnelle et la fourniture de prothèses dentaires par les dentistes et les techniciens dentaires.',''), + ('VATEX-EU-132-1F','eu','Exonération en vertu de l''article 132, paragraphe 1, point f), de la directive 2006/112/CE du Conseil','La prestation de services par des groupements autonomes de personnes, qui exercent une activité exonérée de la TVA ou pour laquelle ils ne sont pas assujettis, dans le but de fournir à leurs membres les services directement nécessaires à l''exercice de cette activité, lorsque ces groupements se contentent de réclamer à leurs membres le remboursement exact de leur part des dépenses communes, à condition que cette exonération ne soit pas susceptible de provoquer des distorsions de concurrence.',''), + ('VATEX-EU-132-1G','eu','Exonération fondée sur l''article 132, paragraphe 1, point g), de la directive 2006/112/CE du Conseil','La fourniture de services et de biens étroitement liés à l''action sociale et à la protection sociale, y compris ceux fournis par les maisons de retraite, par des organismes de droit public ou par d''autres organismes reconnus par l''État membre concerné comme étant consacrés au bien-être social.',''), + ('VATEX-EU-132-1H','eu','Exonération sur la base de l''article 132, paragraphe 1, point h), de la directive 2006/112/CE du Conseil','La fourniture de services et de biens étroitement liés à la protection des enfants et des jeunes par des organismes de droit public ou par d''autres organisations reconnues par l''État membre concerné comme étant consacrées au bien-être social ;',''); +INSERT INTO public.vatex_code (vx_code,vx_country,vx_code_name,vx_description,vx_remark) VALUES + ('VATEX-EU-132-1I','eu','Exonération en vertu de l''article 132, paragraphe 1, point i), de la directive 2006/112/CE du Conseil',' La fourniture d''enseignement aux enfants ou aux jeunes, d''enseignement scolaire ou universitaire, de formation professionnelle ou de reconversion, y compris la fourniture de services et de biens étroitement liés à ces activités, par des organismes de droit public ayant pour but ou par d''autres organismes reconnus par l''État membre concerné comme ayant des objectifs similaires.',''), + ('VATEX-EU-132-1J','eu','Exonération sur la base de l''article 132, paragraphe 1, point j), de la directive 2006/112/CE du Conseil','Cours particuliers dispensés par des enseignants et couvrant l''enseignement scolaire ou universitaire.',''), + ('VATEX-EU-132-1K','eu','Exonération en vertu de l''article 132, paragraphe 1, point k), de la directive 2006/112/CE du Conseil','La mise à disposition de personnel par des institutions religieuses ou philosophiques aux fins des activités visées aux points b), g), h) et i) et dans le but d''assurer le bien-être spirituel.',''), + ('VATEX-EU-132-1L','eu','Exonération en vertu de l''article 132, paragraphe 1, point l), de la directive 2006/112/CE du Conseil','La fourniture de services, et la fourniture de biens qui y sont étroitement liés, à leurs membres dans leur intérêt commun, en contrepartie d''une cotisation fixée conformément à leurs règles, par des organisations sans but lucratif à caractère politique, syndical, religieux, patriotique, philosophique, philanthropique ou civique, à condition que cette exonération ne soit pas susceptible de provoquer des distorsions de concurrence.',''), + ('VATEX-EU-132-1M','eu','Exonération en vertu de l''article 132, paragraphe 1, point m), de la directive 2006/112/CE du Conseil + ','La prestation de certains services étroitement liés au sport ou à l''éducation physique par des organismes sans but lucratif à des personnes participant à des activités sportives ou d''éducation physique.',''), + ('VATEX-EU-132-1N','eu','Exonération en vertu de l''article 132, paragraphe 1, point n), de la directive 2006/112/CE du Conseil','La prestation de certains services culturels et la fourniture de biens étroitement liés à ces services par des organismes de droit public ou d''autres organismes culturels reconnus par l''État membre concerné.',''), + ('VATEX-EU-132-1O','eu','Exonération en vertu de l''article 132, paragraphe 1, point o), de la directive 2006/112/CE du Conseil','La fourniture de services et de biens, par des organisations dont les activités sont exonérées en vertu des points b), g), h), i), l), m) et n), dans le cadre d''événements organisés exclusivement à des fins de collecte de fonds pour leur propre bénéfice, à condition que cette exonération ne soit pas susceptible de provoquer des distorsions de concurrence.',''), + ('VATEX-EU-132-1P','eu','Exonération en vertu de l''article 132, paragraphe 1, point p), de la directive 2006/112/CE du Conseil','Prestation de services de transport de personnes malades ou blessées dans des véhicules spécialement conçus à cet effet, par des organismes dûment agréés.',''), + ('VATEX-EU-132-1Q','eu','Exonération en vertu de l''article 132, paragraphe 1, point q), de la directive 2006/112/CE du Conseil','Les activités, autres que celles à caractère commercial, exercées par les organismes publics de radio et de télévision.',''), + ('VATEX-EU-143','eu','Exonération en vertu de l''article 143 de la directive 2006/112/CE du Conseil','Exonérations à l''importation.',''); +INSERT INTO public.vatex_code (vx_code,vx_country,vx_code_name,vx_description,vx_remark) VALUES + ('VATEX-EU-143-1A','eu','Exonération en vertu de l''article 143, paragraphe 1, point a), de la directive 2006/112/CE du Conseil','Importation définitive de biens dont la livraison par un assujetti serait en tout état de cause exonérée sur leur territoire respectif.',''), + ('VATEX-EU-143-1B','eu','Exonération en vertu de l''article 143, paragraphe 1, point b), de la directive 2006/112/CE du Conseil','L''importation définitive de biens régis par les directives 69/169/CEE (1), 83/181/CEE (2) et 2006/79/CE (3) du Conseil.',''), + ('VATEX-EU-143-1C','eu','Exonération en vertu de l''article 143, paragraphe 1, point c), de la directive 2006/112/CE du Conseil','L''importation définitive de biens, en libre pratique à partir d''un territoire tiers faisant partie du territoire douanier de la Communauté, qui bénéficieraient de l''exonération prévue au point b) s''ils avaient été importés au sens de l''article 30, premier alinéa.',''), + ('VATEX-EU-143-1D','eu','Exonération en vertu de l''article 143, paragraphe 1, point d), de la directive 2006/112/CE du Conseil','L''importation de biens expédiés ou transportés à partir d''un territoire tiers ou d''un pays tiers vers un État membre autre que celui où l''expédition ou le transport des biens prend fin, lorsque la livraison de ces biens par l''importateur désigné ou reconnu comme redevable de la TVA en vertu de l''article 201 est exonérée en vertu de l''article 138.',''), + ('VATEX-EU-143-1E','eu','Exonération en vertu de l''article 143, paragraphe 1, point e), de la directive 2006/112/CE du Conseil','La réimportation, par la personne qui les a exportées, de marchandises dans l''état où elles ont été exportées, lorsque ces marchandises sont exonérées de droits de douane.',''), + ('VATEX-EU-143-1F','eu','Exonération en vertu de l''article 143, paragraphe 1, point f), de la directive 2006/112/CE du Conseil','L''importation, dans le cadre d''accords diplomatiques et consulaires, de marchandises exonérées de droits de douane.',''), + ('VATEX-EU-143-1FA','eu','Exonération en vertu de l''article 143, paragraphe 1, point f bis), de la directive 2006/112/CE du Conseil','L''importation de marchandises par la Communauté européenne, la Communauté européenne de l''énergie atomique, la Banque centrale européenne ou la Banque européenne d''investissement, ou par les organismes créés par les Communautés auxquels s''applique le protocole du 8 avril 1965 sur les privilèges et immunités des Communautés européennes, dans les limites et conditions prévues par ce protocole et les accords pris pour son application ou les accords de siège, dans la mesure où cela n''entraîne pas de distorsion de concurrence ;',''), + ('VATEX-EU-143-1G','eu','Exonération fondée sur l''article 143, paragraphe 1, point g), de la directive 2006/112/CE du Conseil',' L''importation de biens par des organismes internationaux, autres que ceux visés au point f bis), reconnus comme tels par les autorités publiques de l''État membre d''accueil, ou par les membres de ces organismes, dans les limites et conditions fixées par les conventions internationales instituant les organismes ou par les accords de siège ;',''), + ('VATEX-EU-143-1H','eu','Exonération sur la base de l''article 143, paragraphe 1, point h), de la directive 2006/112/CE du Conseil','L''importation de biens, dans les États membres parties au traité de l''Atlantique Nord, par les forces armées d''autres États parties à ce traité pour l''usage de ces forces ou du personnel civil qui les accompagne ou pour l''approvisionnement de leurs mess ou cantines lorsque ces forces participent à l''effort commun de défense.',''), + ('VATEX-EU-143-1I','eu','Exonération sur la base de l''article 143, paragraphe 1, point i), de la directive 2006/112/CE du Conseil','Importation de biens par les forces armées du Royaume-Uni stationnées sur l''île de Chypre en vertu du traité d''établissement concernant la République de Chypre, daté du 16 août 1960, destinés à l''usage de ces forces ou du personnel civil qui les accompagne ou à l''approvisionnement de leurs mess ou cantines.',''); +INSERT INTO public.vatex_code (vx_code,vx_country,vx_code_name,vx_description,vx_remark) VALUES + ('VATEX-EU-143-1J','eu','Exonération en vertu de l''article 143, paragraphe 1, point j), de la directive 2006/112/CE du Conseil','L''importation dans les ports, par les entreprises de pêche maritime, de leurs captures, non transformées ou après avoir subi une conservation en vue de leur commercialisation, mais avant leur livraison.',''), + ('VATEX-EU-143-1K','eu','Exonération en vertu de l''article 143, paragraphe 1, point k), de la directive 2006/112/CE du Conseil','Importation d''or par les banques centrales.',''), + ('VATEX-EU-143-1L','eu','Exonération en vertu de l''article 143, paragraphe 1, point l), de la directive 2006/112/CE du Conseil','Importation de gaz par un réseau de gaz naturel ou tout réseau raccordé à un tel réseau ou alimenté par un navire transportant du gaz dans un réseau de gaz naturel ou tout réseau de gazoduc en amont, d''électricité ou d''énergie thermique ou frigorifique par des réseaux de chauffage ou de refroidissement.',''), + ('VATEX-EU-144','eu','Exonération en vertu de l''article 144 de la directive 2006/112/CE du Conseil','Exonérations pour les services liés à l''importation de biens',''), + ('VATEX-EU-146-1E','eu','Exonération en vertu de l''article 146, paragraphe 1, point e), de la directive 2006/112/CE du Conseil','Exonérations pour les services liés à l''exportation de biens',''), + ('VATEX-EU-148','eu','Exonération en vertu de l''article 148 de la directive 2006/112/CE du Conseil','Exonérations liées au transport international.',''), + ('VATEX-EU-148-A','eu','Exonération en vertu de l''article 148, point a), de la directive 2006/112/CE du Conseil','Fournitures de carburant pour les navires de transport international commercial',''), + ('VATEX-EU-148-B','eu','Exonération en vertu de l''article 148, point b), de la directive 2006/112/CE du Conseil','Fournitures de carburant pour les navires de combat effectuant des transports internationaux.',''), + ('VATEX-EU-148-C','eu','Exonération en vertu de l''article 148, paragraphe c), de la directive 2006/112/CE du Conseil','Entretien, modification, affrètement et location de navires de transport international.',''), + ('VATEX-EU-148-D','eu','Exonération en vertu de l''article 148, paragraphe d), de la directive 2006/112/CE du Conseil','Prestation d''autres services à des navires de transport commercial international.',''); +INSERT INTO public.vatex_code (vx_code,vx_country,vx_code_name,vx_description,vx_remark) VALUES + ('VATEX-EU-148-E','eu','Exonération en vertu de l''article 148, paragraphe e), de la directive 2006/112/CE du Conseil','Fourniture de carburant pour les aéronefs effectuant des vols internationaux.',''), + ('VATEX-EU-148-F','eu','Exonération en vertu de l''article 148, paragraphe f), de la directive 2006/112/CE du Conseil','Entretien, modification, affrètement et location d''aéronefs sur les liaisons internationales.',''), + ('VATEX-EU-148-G','eu','Exonération en vertu de l''article 148, point g), de la directive 2006/112/CE du Conseil','Prestation d''autres services aux aéronefs sur les routes internationales.',''), + ('VATEX-EU-151','eu','Exonération en vertu de l''article 151 de la directive 2006/112/CE du Conseil','Exonérations relatives à certaines opérations assimilées à des exportations.',''), + ('VATEX-EU-151-1A','eu','Exonération en vertu de l''article 151, paragraphe 1, point a), de la directive 2006/112/CE du Conseil','La fourniture de biens ou de services dans le cadre d''accords diplomatiques et consulaires.',''), + ('VATEX-EU-151-1AA','eu','Exonération en vertu de l''article 151, paragraphe 1, point a bis), de la directive 2006/112/CE du Conseil','La livraison de biens ou la prestation de services à la Communauté européenne, à la Communauté européenne de l''énergie atomique, à la Banque centrale européenne ou à la Banque européenne d''investissement, ou aux organismes créés par les Communautés auxquels s''applique le protocole du 8 avril 1965 sur les privilèges et immunités des Communautés européennes, dans les limites et conditions prévues par ledit protocole et les accords pris pour son application ou les accords de siège, dans la mesure où cela n''entraîne pas de distorsion de concurrence.',''), + ('VATEX-EU-151-1B','eu','Exonération fondée sur l''article 151, paragraphe 1, point b), de la directive 2006/112/CE du Conseil','Les livraisons de biens ou les prestations de services effectuées à des organismes internationaux, autres que ceux visés au point a bis), reconnus comme tels par les autorités publiques des États membres d''accueil, et aux membres de ces organismes, dans les limites et conditions prévues par les conventions internationales instituant ces organismes ou par les accords de siège.',''), + ('VATEX-EU-151-1C','eu','Exonération sur la base de l''article 151, paragraphe 1, point c), de la directive 2006/112/CE du Conseil','La fourniture de biens ou de services dans un État membre partie au traité de l''Atlantique Nord, destinés soit aux forces armées d''autres États parties à ce traité pour l''usage de ces forces, soit au personnel civil qui les accompagne, soit à l''approvisionnement de leurs mess ou cantines lorsque ces forces participent à l''effort commun de défense.',''), + ('VATEX-EU-151-1D','eu','Exonération en vertu de l''article 151, paragraphe 1, point d), de la directive 2006/112/CE du Conseil','La livraison de biens ou la prestation de services à un autre État membre, destinés aux forces armées de tout État partie au traité de l''Atlantique Nord, autre que l''État membre de destination lui-même, pour l''usage de ces forces ou du personnel civil qui les accompagne, ou pour l''approvisionnement de leurs mess ou cantines lorsque ces forces participent à l''effort commun de défense.',''), + ('VATEX-EU-151-1E','eu','Exonération en vertu de l''article 151, paragraphe 1, point e), de la directive 2006/112/CE du Conseil','La livraison de biens ou la prestation de services aux forces armées du Royaume-Uni stationnées sur l''île de Chypre en vertu du traité d''établissement concernant la République de Chypre, daté du 16 août 1960, qui sont destinés à l''usage de ces forces ou du personnel civil qui les accompagne, ou à l''approvisionnement de leurs mess ou cantines.',''); +INSERT INTO public.vatex_code (vx_code,vx_country,vx_code_name,vx_description,vx_remark) VALUES + ('VATEX-EU-153','eu','Exonération en vertu de l''article 153 de la directive 2006/112/CE du Conseil','Prestation de services par des intermédiaires agissant au nom et pour le compte d''une autre personne. À l''exclusion des agences de voyages.',''), + ('VATEX-EU-159','eu','Exonération en vertu de l''article 159 de la directive 2006/112/CE du Conseil','Exonérations pour les services liés à la livraison de biens destinés à être placés sous le régime des entrepôts douaniers, des entrepôts autres que les entrepôts douaniers et des régimes similaires.',''), + ('VATEX-EU-309','eu','Exonération en vertu de l''article 309 de la directive 2006/112/CE du Conseil','Services d''agences de voyages fournis en dehors de l''UE.',''), + ('VATEX-EU-AE','eu','Autoliquidation','Prend en charge la règle EN 16931-1 BR-AE-10','À utiliser uniquement avec le code de catégorie TVA AE'), + ('VATEX-EU-D','eu','Acquisition intracommunautaire à partir de moyens de transport d''occasion','Moyens de transport d''occasion - Indication que la TVA a été acquittée conformément aux dispositions transitoires applicables','À utiliser uniquement avec le code de catégorie TVA E'), + ('VATEX-EU-F','eu','Acquisition intracommunautaire de biens d''occasion','Biens d''occasion - Indication que le régime de la marge bénéficiaire pour les biens d''occasion a été appliqué.','À utiliser uniquement avec le code de catégorie TVA E'), + ('VATEX-EU-G','eu','Exportation hors de l''UE','Conforme à la norme EN 16931-1 BR-G-10','À utiliser uniquement avec le code de catégorie TVA G'), + ('VATEX-EU-I','eu','Acquisition intracommunautaire d''œuvres d''art','Œuvres d''art - Indication que le régime de la marge bénéficiaire pour les œuvres d''art a été appliqué.','À utiliser uniquement avec le code de catégorie TVA E'), + ('VATEX-EU-IC','eu','Livraison intracommunautaire','Conforme à la norme EN 16931-1 BR-IC-10','À utiliser uniquement avec le code de catégorie TVA K'), + ('VATEX-EU-J','eu','Acquisition intracommunautaire d''objets de collection et d''antiquités','Objets de collection et antiquités - Indication que le régime de marge de TVA pour les objets de collection et les antiquités a été appliqué.','À utiliser uniquement avec le code de catégorie TVA E'); +INSERT INTO public.vatex_code (vx_code,vx_country,vx_code_name,vx_description,vx_remark) VALUES + ('VATEX-EU-O','eu','Non soumis à la TVA','Conforme à la norme EN 16931-1 BR-O-10','À utiliser uniquement avec le code de catégorie TVA O'), + ('VATEX-FR-FRANCHISE','fr','Franchise de TVA intérieure française','Assujettis à la TVA sur le territoire national qui bénéficient d''une franchise de base (article 293 B du code général des impôts. Dans ce cas, ils sont exonérés de TVA).','Pour la facturation nationale en France'), + ('VATEX-FR-CNWVAT','fr','France nationale Notes de crédit sans TVA, dues au fournisseur pour la perte de TVA liée à la remise','Doctrine administrative BOI TVA DECLA 30 20 20 §260 et 270 pour les notes de crédit et §280 et 290 pour les remises','Pour les notes de crédit nationales uniquement en France'), + ('VATEX-FR-CGI261-1','fr','Exonération sur la base de l''article 261, paragraphe 1, du Code général des impôts (CGI)','Exonération de TVA pour les opérations soumises à d''autres taxes','Uniquement pour la facturation nationale en France'), + ('VATEX-FR-CGI261-2','fr','Exonération en vertu de l''article 261, paragraphe 2, du Code général des impôts (CGI)','Exonération de TVA pour les activités agricoles et de pêche','Uniquement pour la facturation nationale en France'), + ('VATEX-FR-CGI261-3','fr','Exonération en vertu de l''article 261, paragraphe 3, du Code général des impôts (CGI)','Exonération de TVA pour les ventes de biens d''occasion','Uniquement pour la facturation nationale en France'), + ('VATEX-FR-CGI261-4','fr','Exonération en vertu de l''article 261, paragraphe 4, du Code général des impôts (CGI)','Exonération de TVA pour les activités libérales et diverses','Uniquement pour la facturation nationale en France'), + ('VATEX-FR-CGI261-5','fr','Exonération en vertu de l''article 261, paragraphe 5, du Code général des impôts (CGI)','Exonération de TVA pour les opérations immobilières','Uniquement pour la facturation nationale en France'), + ('VATEX-FR-CGI261-7','fr','Exonération en vertu de l''article 261, paragraphe 7, du Code général des impôts (CGI)','Exonération de TVA pour les opérations des organismes à but non lucratif','Uniquement pour la facturation nationale en France'), + ('VATEX-FR-CGI261-8','fr','Exonération en vertu de l''article 261, paragraphe 8, du Code général des impôts (CGI)','Exonération de TVA, en cas de catastrophe affectant le territoire d''un État membre de l''Union européenne, pour les livraisons de biens et les prestations de services liées à ces livraisons, lorsque l''importation de ces biens par le destinataire de ces livraisons ou par le preneur de ces prestations aurait été exonérée en vertu de l''article 291 II 2° bis du CGI','Uniquement pour la facturation nationale en France'); +INSERT INTO public.vatex_code (vx_code,vx_country,vx_code_name,vx_description,vx_remark) VALUES + ('VATEX-FR-CGI261A','fr','Exonération en vertu de l''article 261 A du Code général des impôts (CGI)','Exonération de TVA pour les opérations effectuées par des personnes morales soumises à l''impôt sur les sociétés qui ont pour objet de céder gratuitement à leurs membres la jouissance de biens mobiliers ou immobiliers (article 239 octies du CGI)','Uniquement pour la facturation nationale en France'), + ('VATEX-FR-CGI261B','fr','Exonération en vertu de l''article 261 B du Code général des impôts (CGI)','Exonération de TVA pour les prestations de services fournies à leurs membres par des groupements constitués de personnes physiques ou morales exerçant certaines activités exonérées de TVA sur la base des 4, à l''exception du 10°, et 7 de l''article 261 du CGI (professions et activités diverses, activités d''organismes à but non lucratif)','Uniquement pour la facturation nationale en France'), + ('VATEX-FR-CGI261C-1','fr','Exonération sur la base du 1° de l''article 261 C du Code général des impôts (CGI)','Exonération de TVA pour les opérations bancaires et financières','Uniquement pour la facturation nationale en France'), + ('VATEX-FR-CGI261C-2','fr','Exonération en vertu du 2° de l''article 261 C du Code général des impôts (CGI)','Exonération de TVA pour les opérations d''assurance et de réassurance et les services connexes effectués par les courtiers et intermédiaires d''assurance','Uniquement pour la facturation nationale en France'), + ('VATEX-FR-CGI261C-3','fr','Exonération en vertu du 3° de l''article 261 C du Code général des impôts (CGI)','Exonération de TVA pour les ventes à leur valeur officielle de timbres fiscaux et de timbres-poste ayant une valeur d''affranchissement ou une valeur fiscale en France','Uniquement pour la facturation nationale en France'), + ('VATEX-FR-CGI261D-1','fr','Exonération en vertu du 1° de l''article 261 D du Code général des impôts (CGI)','Exonération de TVA pour les locations de terrains et de bâtiments à usage agricole','Uniquement pour la facturation nationale en France'), + ('VATEX-FR-CGI261D-1BIS','fr','Exonération en vertu du 1°bis de l''article 261 D du Code général des impôts (CGI)','Exonération de TVA pour les locations de biens immobiliers résultant d''un bail conférant un droit réel','Uniquement pour la facturation nationale en France'), + ('VATEX-FR-CGI261D-2','fr','Exonération en vertu du 2° de l''article 261 D du Code général des impôts (CGI)','Exonération de TVA pour certaines locations de terrains non bâtis et de locaux nus, à l''exception des places de stationnement pour véhicules','Uniquement pour la facturation nationale en France'), + ('VATEX-FR-CGI261D-3','fr','Exonération en vertu de l''article 261 D, alinéa 3, du Code général des impôts (CGI) Exonération de TVA - Article 261 D-3° du Code Général des Impôts','Exonération de TVA pour la location ou la concession de droits sur les biens visés aux 1° et 2° de l''article 261 D du CGI dans la mesure où ils concernent la gestion d''un portefeuille immobilier','Uniquement pour la facturation nationale en France'), + ('VATEX-FR-CGI261D-4','fr','Exonération sur la base du 4° de l''article 261 D du Code général des impôts (CGI)','Exonération de TVA pour les locations occasionnelles, permanentes ou saisonnières de logements meublés ou non meublés à usage d''habitation','Uniquement pour la facturation nationale en France'); +INSERT INTO public.vatex_code (vx_code,vx_country,vx_code_name,vx_description,vx_remark) VALUES + ('VATEX-FR-CGI261E-1','fr','Exonération en vertu du 1° de l''article 261 E du Code général des impôts (CGI)','Exonération de TVA pour l''organisation de jeux de hasard ou d''argent soumis aux prélèvements progressifs','Uniquement pour la facturation nationale en France'), + ('VATEX-FR-CGI261E-2','fr','Exonération en vertu du 2° de l''article 261 E du Code général des impôts (CGI)','Exonération de TVA pour les recettes provenant de l''exploitation de la loterie nationale, des paris hippiques, des paris sur les compétitions sportives et des jeux en ligne, à l''exception des rémunérations perçues par les organisateurs et les intermédiaires participant à l''organisation de ces jeux et paris','Uniquement pour la facturation nationale en France'), + ('VATEX-FR-CGI277A','fr','Exonération en vertu de l''article 277 A du Code général des impôts (CGI)','Régime suspensif de TVA','Uniquement pour la facturation nationale en France'), + ('VATEX-FR-CGI275','fr','Exonération en vertu de l''article 275 du Code général des impôts (CGI)','Achats hors TVA de biens destinés à l''exportation, à une livraison exonérée, à une livraison effectuée sur le territoire d''un autre État membre de l''Union européenne ou à une livraison hors de France, ainsi que les services liés à ces biens','Uniquement pour la facturation nationale en France'), + ('VATEX-FR-298SEXDECIESA','fr','Exonération en vertu de l''article 298 sexdecies A du Code général des impôts (CGI)','Régime spécial applicable à l''or d''investissement','Uniquement pour la facturation nationale en France'), + ('VATEX-FR-CGI295','fr','Exonération en vertu de l''article 295 du Code général des impôts (CGI)','Exonérations de TVA pour les territoires français visés à l''article 349 et à l''article 355, paragraphe 1, du traité sur le fonctionnement de l''Union européenne où la TVA s''applique','Uniquement pour la facturation nationale en France'), + ('VATEX-FR-AE','fr','Exonération en vertu de l''article 283, paragraphe 2, du Code général des impôts (CGI)','Système d''autoliquidation national','Uniquement pour la facturation nationale en France'); + + +ALTER TABLE public.tva_rate ADD CONSTRAINT tva_rate_vatex_code_fk FOREIGN KEY (vx_code) REFERENCES public.vatex_code(vx_code); + +drop VIEW public.v_tva_rate; + +CREATE OR REPLACE VIEW public.v_tva_rate +AS SELECT tva_id, + tva_rate, + tva_code, + tva_label, + tva_comment, + tva_reverse_account, + split_part(tva_poste, ','::text, 1) AS tva_purchase, + split_part(tva_poste, ','::text, 2) AS tva_sale, + tva_both_side, + tva_payment_purchase, + tva_payment_sale, + tva_peppol_code, + vx_code +FROM tva_rate; \ No newline at end of file From 71f8a7fc82545e99a365d6816d2dc091887566ab Mon Sep 17 00:00:00 2001 From: sparkyx Date: Sat, 22 Nov 2025 08:38:44 +0100 Subject: [PATCH 12/19] E-INVOICE : VATEX filter on the country --- html/js/noalyss_script.js | 25 +++++++++++++++++++ include/ajax/ajax_search_vatex.php | 4 +-- include/lib/html_input.class.php | 24 ++++++++++++++++++ include/lib/ibutton.class.php | 6 ++--- include/template/ajax-search_vatex.php | 34 ++++++++++++++------------ 5 files changed, 71 insertions(+), 22 deletions(-) diff --git a/html/js/noalyss_script.js b/html/js/noalyss_script.js index 64fbbf99c..a50c93c45 100644 --- a/html/js/noalyss_script.js +++ b/html/js/noalyss_script.js @@ -5075,5 +5075,30 @@ VAT_Code.prototype.select_value=function(vx_code) } } +VAT_Code.prototype.filter_country=function() +{ + try { + var to_show="country"+id$("filter_country").value; + console.debug(`show ${to_show}`) + let a_row=id$("code_vatex_tb").rows; + // show all rows, then hide + for (let i=1;i< a_row.length;i++) { + + if (id$("filter_country").value == 0 || a_row[i].hasClassName(to_show)){ + a_row[i].show() + console.debug(`show row ${i}`) + + }else { + a_row[i].hide() + console.debug(`hide row ${i}`) + } + } + + }catch (e) + { + console.error(e.message); + return false; + } +} noalyss=new Noalyss(); diff --git a/include/ajax/ajax_search_vatex.php b/include/ajax/ajax_search_vatex.php index 90adebd69..ce8bdd38b 100644 --- a/include/ajax/ajax_search_vatex.php +++ b/include/ajax/ajax_search_vatex.php @@ -37,8 +37,6 @@ if ($select_code == "") { require_once NOALYSS_TEMPLATE."/ajax-search_vatex.php"; - - return; } @@ -53,7 +51,7 @@ if ($select_code != "") if ( ! empty ($row)) { $answer['vx_code']=$row['vx_code']; - $answer['vx_value']=$row['vx_code']; + $answer['vx_value']=Icon_Action::trash(uniqid(),"vat_code.select_value('xx')").$row['vx_code']; $answer['vx_description']=$row['vx_description'].span($row['vx_remark'],' class="text-muted" '); }else { $answer['vx_code']=$answer['vx_value']=$answer['vx_description']=""; diff --git a/include/lib/html_input.class.php b/include/lib/html_input.class.php index 316660b33..51c9f8415 100755 --- a/include/lib/html_input.class.php +++ b/include/lib/html_input.class.php @@ -1011,6 +1011,30 @@ class HtmlInput return $sel; } } + /** + * @brief filter the rows in a table and keep the colored row in alternance + * @param dom_id $p_table_id table + * @param string $p_col , column to search example 0,1,2 + * @param int $start_row row to always keep (header) + * @param string $p_name name of the input field + * @param string $domid domid containing the second filter, it could be an HIDDEN, TEXT, SELECT field + * @return string HTML + */ + static function filter_table_DOM($p_table_id, $p_col, $start_row, $p_name + ,$domid) + { + + $r=" + + + + + + "; + $r.=' '; + return $r; + } + /** * @brief filter the rows in a table and keep the colored row in alternance * @param dom_id $p_table_id table diff --git a/include/lib/ibutton.class.php b/include/lib/ibutton.class.php index 18fdb7a00..761fa6aae 100644 --- a/include/lib/ibutton.class.php +++ b/include/lib/ibutton.class.php @@ -105,15 +105,15 @@ class ISmallButton extends IButton $extra= ( isset($this->extra))?$this->extra:""; $this->id=($this->id=="")?$this->name:$this->id; $tab=(isset($this->tabindex))?' tabindex="'.$this->tabindex.'"':""; + $attr=$this->get_node_attribute(); + $r='extra. $tab. ' id="'.$this->id.'"'. ' value="'.$this->label.'"'. - ' onClick="'.$this->javascript.'"'.$extra.'>'; - $attr=$this->get_js_attr(); - $r.=$attr; + ' onClick="'.$this->javascript.'"'.$extra." $attr ".'>'; return $r; } diff --git a/include/template/ajax-search_vatex.php b/include/template/ajax-search_vatex.php index d38b4cbc0..7c021ac55 100644 --- a/include/template/ajax-search_vatex.php +++ b/include/template/ajax-search_vatex.php @@ -26,8 +26,21 @@ echo \HtmlInput::title_box(_("Choix VATEX"), $dgbox); $a_vatex_code = $cn->get_array("select vx_code,vx_code_name,vx_description,vx_remark , vx_country from vatex_code order by 1"); $nb_vatex_code = count($a_vatex_code); -print HtmlInput::filter_table('code_vatex_tb', '0,1,2,3', 1); + + +//@var $os_country (ISELECT) filter table by country +$os_country=new \ISelect("filter_country"); +$os_country->transform(array( + "countryeu"=>"Europe", + ""=>_("Tous"), + "countryfr"=>"France" + ) + ); +$os_country->javascript=' onchange="vat_code.filter_country()" '; ?> +
            +input()?> + - - - - -
            @@ -44,24 +57,12 @@ print HtmlInput::filter_table('code_vatex_tb', '0,1,2,3', 1);
            - - - - - - - - -
            @@ -93,3 +94,4 @@ print HtmlInput::filter_table('code_vatex_tb', '0,1,2,3', 1); + \ No newline at end of file From 3716395a8ea29ab03141e95e34622b7d521032fb Mon Sep 17 00:00:00 2001 From: sparkyx Date: Sat, 22 Nov 2025 08:40:33 +0100 Subject: [PATCH 13/19] FIX : problem with VAT SELECT: cannot update the right DOMID --- html/js/noalyss_script.js | 29 +++++++++++++++++-------- include/ajax/ajax_account.php | 5 ++++- include/class/card_property.class.php | 2 +- include/class/tva_rate_mtable.class.php | 3 +++ include/lib/itva_popup.class.php | 1 + 5 files changed, 29 insertions(+), 11 deletions(-) diff --git a/html/js/noalyss_script.js b/html/js/noalyss_script.js index a50c93c45..a5f6c5400 100644 --- a/html/js/noalyss_script.js +++ b/html/js/noalyss_script.js @@ -609,8 +609,8 @@ function popup_select_tva(obj, p_function_callback) { if (document.getElementById('tva_select')) { removeDiv('tva_select'); } - - var queryString = "gDossier=" + obj.gDossier + "&op=dsp_tva" + "&ctl=" + obj.ctl + '&popup=' + 'tva_select'; + + var queryString = "gDossier=" + obj.getAttribute("gdossier") + "&op=dsp_tva" + "&ctl=" + obj.getAttribute("ctl") + '&popup=' + 'tva_select'; if (obj.jcode) queryString += '&code=' + obj.jcode; if (obj.compute) @@ -641,7 +641,7 @@ function popup_select_tva(obj, p_function_callback) { var nTop = posY - 200; var nLeft = "15%"; - var str_style = "top:" + nTop + "px;left:" + nLeft + ";right:" + nLeft + ";width:55em;height:auto"; + var str_style = "top:" + nTop + "px;left:" + nLeft + ";right:" + nLeft + ";width:55em;height:auto;z-index:"+get_next_layer()+';'; var popup = { 'id': 'tva_select', @@ -2202,10 +2202,11 @@ function view_action(ag_id, dossier, modify) { * @param _id : id of the table * @param colnr : string containing the column number where you're searching separated by a comma * @param start_row : first row (1 if you have table header) + * @param class 2nd filter on the CSS CLASS of the row (TR), domid of the TAG containing the classname (TagName: SELECT, HIDDEN, TEXT ) * @returns nothing * @see HtmlInput::filter_table */ -function filter_table(phrase, _id, colnr, start_row) { +function filter_table(phrase, _id, colnr, start_row,classname) { id$('info_div').innerHTML = content[65]; id$('info_div').style.display = "block"; var words = id$(phrase).value.toLowerCase(); @@ -2220,8 +2221,17 @@ function filter_table(phrase, _id, colnr, start_row) { } var ele; var tot_found = 0; - - for (var r = start_row; r < table.rows.length; r++) { + console.debug(`filter is ${classname}`) + var row_class=""; + if ( classname ) row_class=id$(classname).value; + + for (var r = start_row; r < table.rows.length; r++) + { + if ( row_class != "" && ! table.rows[r].hasClassName(row_class)) { + console.debug(`no check ${r} ${classname}`) + continue; + } + console.debug(`checked ${r} ${classname}`) var found = 0; for (var col = 0; col < aCol.length; col++) { var idx = aCol[col]; @@ -5000,6 +5010,7 @@ VAT_Code.prototype.list_vatex=function () { try { + var here=this; var dgbox = "search_vatex_div"; waiting_box(); removeDiv(dgbox); @@ -5023,7 +5034,7 @@ VAT_Code.prototype.list_vatex=function () var div_style = "position:absolute;" + ";top:" + y + "px"+";z-index:"+get_next_layer(); add_div({id: dgbox, cssclass: 'inner_box', html: loading(), style: div_style, drag: false}); $(dgbox).update(req.responseText); - + here.filter_country(); } } @@ -5078,7 +5089,7 @@ VAT_Code.prototype.select_value=function(vx_code) VAT_Code.prototype.filter_country=function() { try { - var to_show="country"+id$("filter_country").value; + var to_show=id$("filter_country").value; console.debug(`show ${to_show}`) let a_row=id$("code_vatex_tb").rows; // show all rows, then hide @@ -5093,7 +5104,7 @@ VAT_Code.prototype.filter_country=function() console.debug(`hide row ${i}`) } } - + $('lk_code_vatex_tb').value=""; }catch (e) { console.error(e.message); diff --git a/include/ajax/ajax_account.php b/include/ajax/ajax_account.php index 61d1688e6..4f0d1b4ba 100644 --- a/include/ajax/ajax_account.php +++ b/include/ajax/ajax_account.php @@ -50,7 +50,10 @@ switch ($op2) * ----------------------------------------------------------------------*/ case "sf": - $ipopup=$ctl; + if ( ! isset($ctl)) { + $ctl= uniqid(); + } + $ipopup=$ctl; $attr=sprintf('this.ctl=\'%s\';',$ipopup); $ctl.='_content'; $it=new IText('acc_query'); diff --git a/include/class/card_property.class.php b/include/class/card_property.class.php index 947955070..c1b239aee 100644 --- a/include/class/card_property.class.php +++ b/include/class/card_property.class.php @@ -224,7 +224,7 @@ class Card_Property } if ($this->ad_id == ATTR_DEF_TVA) { - $result['input'] = new ITva_Popup('popup_tva'); + $result['input'] = new ITva_Popup( "av_text" . $this->ad_id); $result['input']->table = 0; $result['input']->value = $this->av_text; $result['label']=$this->ad_text; diff --git a/include/class/tva_rate_mtable.class.php b/include/class/tva_rate_mtable.class.php index 6e845b32f..d576dbfd4 100644 --- a/include/class/tva_rate_mtable.class.php +++ b/include/class/tva_rate_mtable.class.php @@ -206,6 +206,9 @@ class Tva_Rate_MTable extends Manage_Table_SQL { $text=\HtmlInput::hidden("vx_code",$value); echo $text; + if ( $value !="") { + $value= \Icon_Action::trash(uniqid(),"vat_code.select_value('xx')").$value; + } echo span($value,'id="vx_value"'); $js=sprintf("vat_code.list_vatex()"); echo \Icon_Action::icon_magnifier(uniqid(),$js ); diff --git a/include/lib/itva_popup.class.php b/include/lib/itva_popup.class.php index 0ef12fd72..5861610a2 100644 --- a/include/lib/itva_popup.class.php +++ b/include/lib/itva_popup.class.php @@ -61,6 +61,7 @@ class ITva_Popup extends HtmlInput $this->button = true; $this->in_table = false; $this->value = $p_value; + if ($p_id =="") $p_id= uniqid ("vat"); $this->id = $p_id; $this->filter = 'none'; } From 544beb2cda3bb506d95f5b685b66934530dbeb7b Mon Sep 17 00:00:00 2001 From: sparkyx Date: Sat, 22 Nov 2025 08:41:13 +0100 Subject: [PATCH 14/19] COSMETIC: z-index computed properly for managetable and search_account --- html/js/accounting_item.js | 4 ++-- html/js/managetable.js | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/html/js/accounting_item.js b/html/js/accounting_item.js index 9c261a847..d8f18af0a 100644 --- a/html/js/accounting_item.js +++ b/html/js/accounting_item.js @@ -45,7 +45,7 @@ function search_accounting(obj) sx=document.body.scrollTop+60; } - var div_style="top:"+sx+"px"; + var div_style="top:"+sx+"px"+';z-index:'+get_next_layer(); removeDiv('search_account'); add_div({id:'search_account',cssclass:'inner_box',html:loading(),style:div_style,drag:false}); @@ -234,7 +234,7 @@ function errorPoste() function display_all_card(p_dossier,p_accounting) { waiting_box(); - var div_dest=add_div({id:'info_card_accounting',cssclass:"inner_box",style:fixed_position(100,250)+";width:auto"}); + var div_dest=add_div({id:'info_card_accounting',cssclass:"inner_box",style:fixed_position(100,250)+";width:auto;z-index:"+get_next_layer()}); var action=new Ajax.Request ( 'ajax_misc.php', { method:'get', diff --git a/html/js/managetable.js b/html/js/managetable.js index 8026f1e21..9407b20a7 100644 --- a/html/js/managetable.js +++ b/html/js/managetable.js @@ -381,6 +381,7 @@ var ManageTable = function (p_table_name) var pos = calcy(50); here.mt_style["top"]=pos+"px"; + here.mt_style["z-index"]=get_next_layer(); $(obj.id).setStyle(here.mt_style); remove_waiting_box(); $(obj.id).update(x['html']); From c236776db79001dfae6f0aed96b0e6415eab9524 Mon Sep 17 00:00:00 2001 From: sparkyx Date: Sat, 22 Nov 2025 08:42:59 +0100 Subject: [PATCH 15/19] cosmetic : padding inner_box --- html/css/style-classic7.css | 12 ++++++++---- include/lib/manage_table_sql.class.php | 3 ++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/html/css/style-classic7.css b/html/css/style-classic7.css index c0429d58b..3f35c6f12 100644 --- a/html/css/style-classic7.css +++ b/html/css/style-classic7.css @@ -1266,8 +1266,8 @@ div.inner_box { border:1px solid #00008B; -moz-box-shadow: 10px 10px 5px #888; box-shadow: 15px 15px 15px #888; - font-size:90%; - font-size:0.9rem; + font-size:100%; + font-size:1rem; width: 100%; left:0px ; } @@ -1285,8 +1285,8 @@ div.inner_box { border:1px solid #00008B; -moz-box-shadow: 10px 10px 5px #888; box-shadow: 15px 15px 15px #888; - font-size:90%; - font-size:0.9rem; + font-size:100%; + font-size:1rem; width: 50%; left:22% ; } @@ -1340,6 +1340,10 @@ div.inner_box { border-radius: 7px; } } + +div.inner_box div.content,div.inner_box2 div.content { + padding:5px; +} /***********************************************************************************/ div.op_detail_title { background-color:#888; diff --git a/include/lib/manage_table_sql.class.php b/include/lib/manage_table_sql.class.php index 6d09789b1..7b8c7244c 100644 --- a/include/lib/manage_table_sql.class.php +++ b/include/lib/manage_table_sql.class.php @@ -1384,6 +1384,7 @@ function check() ob_start(); echo HtmlInput::title_box($this->getTitle(), $this->dialog_box,"close","","y","y"); + print '
            '; printf('
            ', $this->object_name, $this->table->get_pk_value(), $this->object_name, $this->object_name, @@ -1409,7 +1410,7 @@ function check() '', ''; echo "
            "; - + print "
            "; $html=ob_get_contents(); ob_end_clean(); From 1b2101c55759c1d1b213a847f3ff6a783bdc6cbe Mon Sep 17 00:00:00 2001 From: sparkyx Date: Sat, 22 Nov 2025 10:27:16 +0100 Subject: [PATCH 16/19] VAT : fix bug when VAT not usable for ledger or purchase --- html/ajax_misc.php | 2 +- html/js/noalyss_script.js | 19 ++++++---- include/class/tva_rate_mtable.class.php | 6 +-- unit-test/include/class/Acc_TVATest.php | 50 ++++++++++++++++++++++++- 4 files changed, 64 insertions(+), 13 deletions(-) diff --git a/html/ajax_misc.php b/html/ajax_misc.php index 46a8ed8e4..b926f4ccb 100644 --- a/html/ajax_misc.php +++ b/html/ajax_misc.php @@ -598,7 +598,7 @@ EOF; $Res = $cn->exec_sql("select * from v_tva_rate where - tva_purchase <> '#' and tva_sale <> '#' + tva_purchase <> '#' or tva_sale <> '#' order by tva_id asc"); } $Max = Database::num_row($Res); diff --git a/html/js/noalyss_script.js b/html/js/noalyss_script.js index a5f6c5400..8bd090620 100644 --- a/html/js/noalyss_script.js +++ b/html/js/noalyss_script.js @@ -609,14 +609,19 @@ function popup_select_tva(obj, p_function_callback) { if (document.getElementById('tva_select')) { removeDiv('tva_select'); } + var gDossier=(obj.gDossier)?obj.gDossier:obj.getAttribute("gdossier"); + var ctl=(obj.ctl)?obj.ctl:obj.getAttribute("ctl"); + + var queryString = "gDossier=" + gDossier + "&op=dsp_tva" + "&ctl=" + ctl + '&popup=' + 'tva_select'; - var queryString = "gDossier=" + obj.getAttribute("gdossier") + "&op=dsp_tva" + "&ctl=" + obj.getAttribute("ctl") + '&popup=' + 'tva_select'; - if (obj.jcode) - queryString += '&code=' + obj.jcode; - if (obj.compute) - queryString += '&compute=' + obj.compute; - if (obj.filter) - queryString += '&filter=' + obj.filter; + var jcode=(obj.jcode)?obj.jcode:obj.getAttribute("jcode"); + if (jcode) + queryString += '&code=' + jcode; + var compute=(obj.compute)?obj.compute:obj.getAttribute("compute"); + if (compute) queryString += '&compute=' + compute; + var filter=(obj.filter)?obj.filter:obj.getAttribute("filter"); + + if (filter) queryString += '&filter=' + filter; var action = new Ajax.Request( "ajax_misc.php", diff --git a/include/class/tva_rate_mtable.class.php b/include/class/tva_rate_mtable.class.php index d576dbfd4..c2be0fe7b 100644 --- a/include/class/tva_rate_mtable.class.php +++ b/include/class/tva_rate_mtable.class.php @@ -452,17 +452,17 @@ class Tva_Rate_MTable extends Manage_Table_SQL } // if vatex is set then code invoice must be different from S and Z - if ( trim($this->table->vx_code) != "" && in_array($this->table->tva_peppol_code,['S','Z'] )) + if ( trim($this->table->vx_code??"") != "" && in_array($this->table->tva_peppol_code,['S','Z'] )) { $this->set_error("vx_code",_("Le code d'exemption TVA ne peut être utilisé avec ce code Facture électronique UBL ")); } // if vatex is set then code invoice must be different from S and Z - if ( trim($this->table->vx_code) != "" && $this->table->tva_peppol_code=="") + if ( trim($this->table->vx_code??"") != "" && $this->table->tva_peppol_code=="") { $this->set_error("vx_code",_("Le code d'exemption TVA n' pas de sens sans code Facture électronique")); } // if tva_peppol_code is not S or Z then a VATEX code must be supplied - if ( ! in_array($this->table->tva_peppol_code,["Z","S"]) && trim($this->table->vx_code ) =="") + if ( ! in_array($this->table->tva_peppol_code??"",["Z","S"]) && trim($this->table->vx_code??"" ) =="") { $this->set_error("vx_code",_("Un code d'exemption de TVA doit être fourni, voyez le manuel")); diff --git a/unit-test/include/class/Acc_TVATest.php b/unit-test/include/class/Acc_TVATest.php index 878e4bfe0..5d22ce835 100644 --- a/unit-test/include/class/Acc_TVATest.php +++ b/unit-test/include/class/Acc_TVATest.php @@ -110,14 +110,60 @@ class Acc_TVATest extends TestCase $vtva_rate->tva_sale="451"; $vtva_rate->tva_both_side="0"; $vtva_rate->tva_rate=0.21; - + $vtva_rate->tva_peppol_code='S'; $tva_rate_mtable=new Tva_Rate_MTable($vtva_rate); $tva_rate_mtable->setPreviousId(0); $check = $tva_rate_mtable->check(); + $this->assertTrue($result==$check," erreur pour $tva_code "); - $this->display_error($tva_rate_mtable); + if ( $result != $check) + { + print "Error for $peppopl_code\n"; + print_r($tva_rate_mtable->aerror); + $this->display_error($tva_rate_mtable); + } } + static function dataCheckVatex() { + return array( + ['S',true,null] + ,['Z',true,null] + ,['A',false,""] + ,['A',true,"XX"] + ); + } + /** + * @brief VATEX Mandatory of tva_peppol_code not in S or Z + * @testdox VATEX Mandatory if tva_peppol_code not S or Z + * @param type $tva_code + * @param type $result + */ + #[DataProvider('dataCheckVatex')] + function testCheckVatex($peppopl_code,$result,$vatex_code) + { + $cn=\Dossier::connect(); + $vtva_rate=new V_Tva_rate_SQL($cn,1); + + $vtva_rate->tva_label="Test"; + $vtva_rate->tva_sale="451"; + $vtva_rate->tva_both_side="0"; + $vtva_rate->tva_rate=0.21; + $vtva_rate->tva_peppol_code=$peppopl_code; + $vtva_rate->vx_code=$vatex_code; + $tva_rate_mtable=new Tva_Rate_MTable($vtva_rate); + $tva_rate_mtable->setPreviousId(1); + + $check = $tva_rate_mtable->check(); + + $this->assertTrue($result==$check," erreur pour $peppopl_code "); + if ( $result != $check) + { + print "Error for $peppopl_code\n"; + print_r($tva_rate_mtable->aerror); + $this->display_error($tva_rate_mtable); + } + } + static function dataBuild() { return array( ['0A',4] From ac87e863addf20e49ddeb63b86139d10625d8cfd Mon Sep 17 00:00:00 2001 From: sparkyx Date: Sat, 22 Nov 2025 10:48:00 +0100 Subject: [PATCH 17/19] ICARD: improve, action in javascript after cleaning the input of the qcode --- include/class/acc_ledger.class.php | 1 - include/class/acc_ledger_purchase.class.php | 1 + include/class/acc_ledger_sale.class.php | 1 + include/class/acc_ledger_search.class.php | 2 +- include/lib/icard.class.php | 19 ++++++++++++++++--- include/template/param_jrn.php | 2 ++ unit-test/include/class/acc_ledgerTest.php | 4 +++- .../include/class/acc_ledger_searchTest.php | 4 +++- 8 files changed, 27 insertions(+), 7 deletions(-) diff --git a/include/class/acc_ledger.class.php b/include/class/acc_ledger.class.php index 295952b11..940e1a5cd 100644 --- a/include/class/acc_ledger.class.php +++ b/include/class/acc_ledger.class.php @@ -1113,7 +1113,6 @@ class Acc_Ledger extends jrn_def_sql $quick_code->value=(isset(${'qc_'.$i}))?${'qc_'.$i}:""; $quick_code->readonly=$p_readonly; - $label=''; if ($quick_code->value!='') { diff --git a/include/class/acc_ledger_purchase.class.php b/include/class/acc_ledger_purchase.class.php index 2c3fa0ae8..b51668546 100644 --- a/include/class/acc_ledger_purchase.class.php +++ b/include/class/acc_ledger_purchase.class.php @@ -1492,6 +1492,7 @@ class Acc_Ledger_Purchase extends Acc_Ledger $W1->name="e_march".$i; $W1->value=$march; $W1->table=0; + $W1->setAfter_clean("compute_all_ledger()"); $W1->set_dblclick("fill_ipopcard(this);"); $W1->set_attribute('ipopup','ipopcard'); diff --git a/include/class/acc_ledger_sale.class.php b/include/class/acc_ledger_sale.class.php index 5a075c252..e7f89d2c6 100644 --- a/include/class/acc_ledger_sale.class.php +++ b/include/class/acc_ledger_sale.class.php @@ -1623,6 +1623,7 @@ EOF; $W1->name = "e_march" . $i; $W1->value = $march; $W1->table = 0; + $W1->setAfter_clean("compute_all_ledger()"); $W1->set_attribute('typecard', 'cred'); $W1->set_dblclick("fill_ipopcard(this);"); $W1->set_attribute('ipopup', 'ipopcard'); diff --git a/include/class/acc_ledger_search.class.php b/include/class/acc_ledger_search.class.php index 4bb7cf2c9..ce8d04547 100644 --- a/include/class/acc_ledger_search.class.php +++ b/include/class/acc_ledger_search.class.php @@ -196,7 +196,7 @@ class Acc_Ledger_Search $f_qcode->javascript=sprintf(' onchange="fill_data_onchange(%s);" ', $f_qcode->name); $f_qcode->value=$http->request($this->div.'qcode',"string",""); - + $f_qcode->setAfter_clean(""); /* $f_txt_qcode=new IText('qcode'); $f_txt_qcode->value=(isset($_REQUEST['qcode']))?$_REQUEST['qcode']:''; */ diff --git a/include/lib/icard.class.php b/include/lib/icard.class.php index 8d195cc83..ce842b72e 100644 --- a/include/lib/icard.class.php +++ b/include/lib/icard.class.php @@ -158,6 +158,7 @@ class ICard extends HtmlInput //!< $autocomplete_file , ajax file used for autocompletion (see Ajax.Autocompleter) private $autocomplete_file; + protected $after_clean; //!< javascript to call after cleaning the INPUT TEXT function __construct($name="", $value="", $p_id="") { parent::__construct($name, $value); @@ -176,8 +177,20 @@ class ICard extends HtmlInput $this->amount_from_type=''; //!< in the follow up ,when a card is selected you take Prix Vente or Prix Achat $this->typecard='all'; $this->autocomplete_file="fid_card.php"; + $this->after_clean=""; } - /** + public function getAfter_clean() + { + return $this->after_clean; + } + + public function setAfter_clean($after_clean) + { + $this->after_clean = $after_clean; + return $this; + } + + /** * Function javascript by default it is update_value called BEFORE the querystring is send in ajax * @return type */ @@ -475,8 +488,8 @@ class ICard extends HtmlInput $this->dblclick=$e; } - $input='
            '. - Icon_Action::clean_zone(uniqid("remove"),"clean_Fid('{$this->id}');compute_all_ledger();"). + $input='
            '. + Icon_Action::clean_zone(uniqid("remove"),"clean_Fid('{$this->id}');{$this->after_clean}"). "
            "; $input.=sprintf(' diff --git a/include/template/param_jrn.php b/include/template/param_jrn.php index 2bd01c781..8f8e8b134 100644 --- a/include/template/param_jrn.php +++ b/include/template/param_jrn.php @@ -57,6 +57,8 @@ if ( $new|| $type=='FIN') : name='bank'; + // clean only the INPUT no javascript + $card->setAfter_clean(""); $card->extra=$cn->make_list('select fd_id from fiche_def where frd_id=4'); $card->set_dblclick("fill_ipopcard(this);"); $card->set_function('fill_data'); diff --git a/unit-test/include/class/acc_ledgerTest.php b/unit-test/include/class/acc_ledgerTest.php index 4960daefa..1b2c62904 100644 --- a/unit-test/include/class/acc_ledgerTest.php +++ b/unit-test/include/class/acc_ledgerTest.php @@ -901,7 +901,9 @@ class Acc_LedgerTest extends TestCase ob_end_clean(); \Noalyss\Facility::save_file(__DIR__."/file", "acc_ledger-input_new.html", $result); $size=filesize(__DIR__."/file/acc_ledger-input_new.html"); - $this->assertTrue($size == 16075 ," output input_new is $size not what it is expected"); + $expected=15985; + $delta =$size-$expected; + $this->assertTrue($delta == 0 ," output input_new is $size expected $expected not what it is expected DELTA=$delta, file to check ".__DIR__."/file/acc_ledger-input_new.html"); } diff --git a/unit-test/include/class/acc_ledger_searchTest.php b/unit-test/include/class/acc_ledger_searchTest.php index 062ec46ea..f2609c53c 100644 --- a/unit-test/include/class/acc_ledger_searchTest.php +++ b/unit-test/include/class/acc_ledger_searchTest.php @@ -89,7 +89,9 @@ class Acc_Ledger_searchTest extends TestCase $r=$ledger->display_search_form(); \Noalyss\Facility::save_file(__DIR__."/file", "acc_ledger_search-test_display_search_form.html", $r); $filesize=strlen($r); - $this->assertTrue($filesize==10971,"Size of the html string for display_search_form see " + $expected=10971; + $delta =$filesize-$expected; + $this->assertTrue($delta == 0,"Size of the html string for display_search_form see delta = $delta , size=$filesize, expected=$expected " . __DIR__."/file/acc_ledger_search-test_display_search_form.html "); } /** From 44c9fd005c17829f4d1193579033dad6b1234dc0 Mon Sep 17 00:00:00 2001 From: sparkyx Date: Sat, 22 Nov 2025 11:11:01 +0100 Subject: [PATCH 18/19] Cosmetic : set layer correctly --- html/js/card.js | 10 +++++----- html/js/noalyss_script.js | 8 ++++++-- include/template/module.php | 2 +- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/html/js/card.js b/html/js/card.js index f82a039a4..2ec564ba0 100644 --- a/html/js/card.js +++ b/html/js/card.js @@ -116,7 +116,7 @@ function search_card(obj) if ( document.getElementById('search_card') ) { removeDiv('search_card'); } - + waiting_box(); @@ -464,7 +464,7 @@ function result_card_search(req) sx=document.body.scrollTop+60; } - var div_style="top:"+sx+"px;min-height:80%;height:auto"; + var div_style="top:"+sx+"px;min-height:80%;height:auto;z-index:"+get_next_layer();; add_div({id:'search_card',cssclass:'inner_box',html:"",style:div_style,drag:false,effect:'blinddown'}); id$('search_card').innerHTML=code_html; @@ -690,7 +690,7 @@ function select_card_type(obj) var sx=0; sx=calcy(160); - var str_style="top:"+sx+"px;height:auto"; + var str_style="top:"+sx+"px;height:auto;z-index:"+get_next_layer(); waiting_box(); var popup={'id': content,'cssclass':'inner_box','style':str_style,'html':"",'drag':false}; @@ -782,7 +782,7 @@ function dis_blank_card(obj) var content='div_new_card'; var nTop=calcy(150); var nLeft=posX; - var str_style="top:"+nTop+"px;right:"+nLeft+"px;height:auto;width:45rem;"; + var str_style="top:"+nTop+"px;right:"+nLeft+"px;height:auto;width:45rem;z-index:"+get_next_layer(); var popup={'id': content,'cssclass':'inner_box','style':str_style,'html':loading(),'drag':false}; @@ -824,7 +824,7 @@ function form_blank_card(obj) var content='div_new_card'; var nTop=posY-40; var nLeft=posX-20; - var str_style="top:"+nTop+"px;left:"+nLeft+"px;width:60em;height:auto"; + var str_style="top:"+nTop+"px;left:"+nLeft+"px;width:60em;height:auto;z-index:"+get_next_layer(); var popup={'id': content,'cssclass':'inner_box','style':str_style,'html':loading(),'drag':true}; if ( document.getElementById(content)) {removeDiv(content);} diff --git a/html/js/noalyss_script.js b/html/js/noalyss_script.js index 8bd090620..afab017e5 100644 --- a/html/js/noalyss_script.js +++ b/html/js/noalyss_script.js @@ -1140,6 +1140,8 @@ function show_calc() { if (document.getElementById('calc1')) { this.document.getElementById('inp').value = ""; this.document.getElementById('inp').focus(); + document.getElementById("calc1").setStyle({ 'z-index':get_next_layer()}); + return; } var sid = 'calc1'; @@ -1152,8 +1154,10 @@ function show_calc() { shtml += ' ' + content[67] + ' '; var obj = { - id: sid, html: shtml, - drag: false, style: 'z-index:'+get_next_layer() + id: sid, + html: shtml, + drag: false, + style: 'z-index:'+get_next_layer() }; add_div(obj); this.document.getElementById('inp').focus(); diff --git a/include/template/module.php b/include/template/module.php index 6430c6921..6990a820e 100644 --- a/include/template/module.php +++ b/include/template/module.php @@ -24,7 +24,7 @@ if ( $cn->get_value("select count(*) from profile join profile_user using (p_id) $search_card=new IText('card_search'); $search_card->css_size='97%'; $search_card_js=sprintf('onclick="boxsearch_card(\'%d\')"',dossier::id()); - echo Icon_Action::card( "$('box_search_card').show()"); + echo Icon_Action::card( "$('box_search_card').show();$('box_search_card').setStyle({'z-index':get_next_layer()})"); echo '