From 801402f46aa7e085c7e6ea72b28c403c73f04718 Mon Sep 17 00:00:00 2001 From: Florian HENRY Date: Fri, 4 Mar 2022 11:58:52 +0100 Subject: [PATCH 01/24] fix: #20267 --- htdocs/comm/action/card.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/htdocs/comm/action/card.php b/htdocs/comm/action/card.php index 752868acb87..ee71cc48f07 100644 --- a/htdocs/comm/action/card.php +++ b/htdocs/comm/action/card.php @@ -1439,6 +1439,10 @@ if ($id > 0) { $("#fullday").change(function() { setdatefields(); }); + $("#actioncode").change(function() { + if ($("#actioncode").val() == \'AC_RDV\') $("#dateend").addClass("fieldrequired"); + else $("#dateend").removeClass("fieldrequired"); + }); })'; print ''."\n"; } @@ -1483,7 +1487,12 @@ if ($id > 0) { print ''.$langs->trans("EventOnFullDay").'fulldayevent ? ' checked' : '').'>'; // Date start - end - print ''.$langs->trans("DateActionStart").' - '.$langs->trans("DateActionEnd").''; + print ''; + print ''.$langs->trans("DateActionStart").''; + print ' - '; + print 'type_code == 'AC_RDV' ? ' class="fieldrequired"' : '').'>'.$langs->trans("DateActionEnd").''; + print ''; + //print ''.$langs->trans("DateActionStart").' - '.$langs->trans("DateActionEnd").''; if (GETPOST("afaire") == 1) { print $form->selectDate($datep ? $datep : $object->datep, 'ap', 1, 1, 0, "action", 1, 1, 0, 'fulldaystart', '', '', '', 1, '', '', 'tzuser'); } elseif (GETPOST("afaire") == 2) { From e06c38650796bbfb531ec98556f7a5cfd4bc2f3b Mon Sep 17 00:00:00 2001 From: kamel Date: Wed, 9 Mar 2022 11:32:07 +0100 Subject: [PATCH 02/24] Fix errors propagation and trigger position on delete proposal line --- htdocs/comm/propal/card.php | 5 ++- htdocs/comm/propal/class/propal.class.php | 52 +++++++++++++---------- 2 files changed, 33 insertions(+), 24 deletions(-) diff --git a/htdocs/comm/propal/card.php b/htdocs/comm/propal/card.php index 63f2977acdb..a77e242379f 100644 --- a/htdocs/comm/propal/card.php +++ b/htdocs/comm/propal/card.php @@ -231,8 +231,11 @@ if (empty($reshook)) { // Remove line $result = $object->deleteline($lineid); // reorder lines - if ($result) { + if ($result > 0) { $object->line_order(true); + } else { + $langs->load("errors"); + setEventMessages($object->error, $object->errors, 'errors'); } if (empty($conf->global->MAIN_DISABLE_PDF_AUTOUPDATE)) { diff --git a/htdocs/comm/propal/class/propal.class.php b/htdocs/comm/propal/class/propal.class.php index 0142d0c1b7a..16db2b12723 100644 --- a/htdocs/comm/propal/class/propal.class.php +++ b/htdocs/comm/propal/class/propal.class.php @@ -980,6 +980,8 @@ class Propal extends CommonObject $this->db->commit(); return 1; } else { + $this->error = $line->error; + $this->errors = $line->errors; $this->db->rollback(); return -1; } @@ -4166,36 +4168,40 @@ class PropaleLigne extends CommonObjectLine $error = 0; $this->db->begin(); - $sql = "DELETE FROM ".MAIN_DB_PREFIX."propaldet WHERE rowid = ".((int) $this->rowid); - dol_syslog("PropaleLigne::delete", LOG_DEBUG); - if ($this->db->query($sql)) { - // Remove extrafields - if (!$error) { - $this->id = $this->rowid; - $result = $this->deleteExtraFields(); - if ($result < 0) { - $error++; - dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR); - } + if (!$notrigger) { + // Call trigger + $result = $this->call_trigger('LINEPROPAL_DELETE', $user); + if ($result < 0) { + $error++; } + } + // End call triggers - if (!$error && !$notrigger) { - // Call trigger - $result = $this->call_trigger('LINEPROPAL_DELETE', $user); - if ($result < 0) { - $this->db->rollback(); - return -1; + if (!$error) { + $sql = "DELETE FROM " . MAIN_DB_PREFIX . "propaldet WHERE rowid = " . ((int)$this->rowid); + dol_syslog("PropaleLigne::delete", LOG_DEBUG); + if ($this->db->query($sql)) { + // Remove extrafields + if (!$error) { + $this->id = $this->rowid; + $result = $this->deleteExtraFields(); + if ($result < 0) { + $error++; + dol_syslog(get_class($this) . "::delete error -4 " . $this->error, LOG_ERR); + } } + } else { + $this->error = $this->db->error() . " sql=" . $sql; + $error++; } - // End call triggers + } - $this->db->commit(); - - return 1; - } else { - $this->error = $this->db->error()." sql=".$sql; + if ($error) { $this->db->rollback(); return -1; + } else { + $this->db->commit(); + return 1; } } From 1f00dff4dac508c367f29025ef2302815670347b Mon Sep 17 00:00:00 2001 From: kamel Date: Wed, 9 Mar 2022 11:40:26 +0100 Subject: [PATCH 03/24] Correction Stickler CI / stickler-ci --- htdocs/comm/propal/class/propal.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/comm/propal/class/propal.class.php b/htdocs/comm/propal/class/propal.class.php index 16db2b12723..65a2e60777f 100644 --- a/htdocs/comm/propal/class/propal.class.php +++ b/htdocs/comm/propal/class/propal.class.php @@ -4178,7 +4178,7 @@ class PropaleLigne extends CommonObjectLine // End call triggers if (!$error) { - $sql = "DELETE FROM " . MAIN_DB_PREFIX . "propaldet WHERE rowid = " . ((int)$this->rowid); + $sql = "DELETE FROM " . MAIN_DB_PREFIX . "propaldet WHERE rowid = " . ((int) $this->rowid); dol_syslog("PropaleLigne::delete", LOG_DEBUG); if ($this->db->query($sql)) { // Remove extrafields From a028e795f746c077db56a7cc65114331ca627f17 Mon Sep 17 00:00:00 2001 From: Florian HENRY Date: Wed, 16 Mar 2022 13:54:21 +0100 Subject: [PATCH 04/24] add missing colspan --- htdocs/comm/action/card.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/comm/action/card.php b/htdocs/comm/action/card.php index ee71cc48f07..0833929c82a 100644 --- a/htdocs/comm/action/card.php +++ b/htdocs/comm/action/card.php @@ -1491,7 +1491,7 @@ if ($id > 0) { print ''.$langs->trans("DateActionStart").''; print ' - '; print 'type_code == 'AC_RDV' ? ' class="fieldrequired"' : '').'>'.$langs->trans("DateActionEnd").''; - print ''; + print ''; //print ''.$langs->trans("DateActionStart").' - '.$langs->trans("DateActionEnd").''; if (GETPOST("afaire") == 1) { print $form->selectDate($datep ? $datep : $object->datep, 'ap', 1, 1, 0, "action", 1, 1, 0, 'fulldaystart', '', '', '', 1, '', '', 'tzuser'); From 813e1a23eb684749ed529e4d6f4a4185fd55bc48 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Thu, 17 Mar 2022 02:13:08 +0100 Subject: [PATCH 05/24] Var not defined --- htdocs/product/inventory/inventory.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/htdocs/product/inventory/inventory.php b/htdocs/product/inventory/inventory.php index aaaf4428ff5..6026e4316e4 100644 --- a/htdocs/product/inventory/inventory.php +++ b/htdocs/product/inventory/inventory.php @@ -216,7 +216,7 @@ if (empty($reshook)) { } } - // Save quantity found during inventory + // Save quantity found during inventory (when we click on Save button on inventory page) if ($action =='updateinventorylines' && $permissiontoadd) { $sql = 'SELECT id.rowid, id.datec as date_creation, id.tms as date_modification, id.fk_inventory, id.fk_warehouse,'; $sql .= ' id.fk_product, id.batch, id.qty_stock, id.qty_view, id.qty_regulated'; @@ -236,6 +236,9 @@ if (empty($reshook)) { $line = $db->fetch_object($resql); $lineid = $line->rowid; + $result = 0; + $resultupdate = 0; + if (GETPOST("id_".$lineid, 'alpha') != '') { // If a value was set ('0' or something else) $qtytoupdate = price2num(GETPOST("id_".$lineid, 'alpha'), 'MS'); $result = $inventoryline->fetch($lineid); @@ -265,7 +268,7 @@ if (empty($reshook)) { } } - // Update line with id of stock movement (and the start quantity if it has changed this last recording) + // Update user that update quantities if (! $error) { $sqlupdate = "UPDATE ".MAIN_DB_PREFIX."inventory"; $sqlupdate .= " SET fk_user_modif = ".((int) $user->id); From 4aa93b363a993938c528018b858acaf7d53559da Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Thu, 17 Mar 2022 11:24:23 +0100 Subject: [PATCH 06/24] FIX #20387 --- htdocs/core/class/discount.class.php | 41 ++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/htdocs/core/class/discount.class.php b/htdocs/core/class/discount.class.php index eaf4bc511c8..b4613d87fc8 100644 --- a/htdocs/core/class/discount.class.php +++ b/htdocs/core/class/discount.class.php @@ -54,12 +54,21 @@ class DiscountAbsolute public $fk_soc; public $discount_type; // 0 => customer discount, 1 => supplier discount - public $amount_ht; // - public $amount_tva; // - public $amount_ttc; // - public $multicurrency_amount_ht; - public $multicurrency_amount_tva; - public $multicurrency_amount_ttc; + + public $total_ht; + public $total_tva; + public $total_ttc; + public $amount_ht; // deprecated + public $amount_tva; // deprecated + public $amount_ttc; // deprecated + + public $multicurrency_total_ht; + public $multicurrency_total_tva; + public $multicurrency_total_ttc; + public $multicurrency_amount_ht; // deprecated + public $multicurrency_amount_tva; // deprecated + public $multicurrency_amount_ttc; // deprecated + // Vat rate public $tva_tx; public $vat_src_code; @@ -163,13 +172,21 @@ class DiscountAbsolute $this->fk_soc = $obj->fk_soc; $this->discount_type = $obj->discount_type; - $this->amount_ht = $obj->amount_ht; - $this->amount_tva = $obj->amount_tva; - $this->amount_ttc = $obj->amount_ttc; + $this->total_ht = $obj->amount_ht; + $this->total_tva = $obj->amount_tva; + $this->total_ttc = $obj->amount_ttc; + // For backward compatibility + $this->amount_ht = $this->total_ht; + $this->amount_tva = $this->total_tva; + $this->amount_ttc = $this->total_ttc; - $this->multicurrency_amount_ht = $this->multicurrency_subprice = $obj->multicurrency_amount_ht; - $this->multicurrency_amount_tva = $obj->multicurrency_amount_tva; - $this->multicurrency_amount_ttc = $obj->multicurrency_amount_ttc; + $this->multicurrency_total_ht = $this->multicurrency_subprice = $obj->multicurrency_amount_ht; + $this->multicurrency_total_tva = $obj->multicurrency_amount_tva; + $this->multicurrency_total_ttc = $obj->multicurrency_amount_ttc; + // For backward compatibility + $this->multicurrency_amount_ht = $this->multicurrency_total_ht; + $this->multicurrency_amount_tva = $this->multicurrency_total_tva; + $this->multicurrency_amount_ttc = $this->multicurrency_total_ttc; $this->tva_tx = $obj->tva_tx; $this->vat_src_code = $obj->vat_src_code; From 49e53abf5325752e09ca1743d69d390948c6cbe9 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Thu, 17 Mar 2022 15:01:15 +0100 Subject: [PATCH 07/24] Fix pdf on letter format --- .../modules/facture/doc/pdf_crabe.modules.php | 5 +++-- .../modules/facture/doc/pdf_sponge.modules.php | 15 ++++++++------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/htdocs/core/modules/facture/doc/pdf_crabe.modules.php b/htdocs/core/modules/facture/doc/pdf_crabe.modules.php index eac6a8df762..ee45641c57e 100644 --- a/htdocs/core/modules/facture/doc/pdf_crabe.modules.php +++ b/htdocs/core/modules/facture/doc/pdf_crabe.modules.php @@ -837,7 +837,7 @@ class pdf_crabe extends ModelePDFFactures $tab3_width = 80; $tab3_height = 4; if ($this->page_largeur < 210) { // To work with US executive format - $tab3_posx -= 20; + $tab3_posx -= 15; } $default_font_size = pdf_getPDFFontSize($outputlangs); @@ -1211,7 +1211,8 @@ class pdf_crabe extends ModelePDFFactures $col1x = 120; $col2x = 170; if ($this->page_largeur < 210) { // To work with US executive format - $col2x -= 20; + $col1x -= 15; + $col2x -= 10; } $largcol2 = ($this->page_largeur - $this->marge_droite - $col2x); diff --git a/htdocs/core/modules/facture/doc/pdf_sponge.modules.php b/htdocs/core/modules/facture/doc/pdf_sponge.modules.php index ef63ad14a94..3134bbc63f5 100644 --- a/htdocs/core/modules/facture/doc/pdf_sponge.modules.php +++ b/htdocs/core/modules/facture/doc/pdf_sponge.modules.php @@ -498,7 +498,7 @@ class pdf_sponge extends ModelePDFFactures $pdf->useTemplate($tplidx); } if (empty($conf->global->MAIN_PDF_DONOTREPEAT_HEAD)) { - $this->_pagehead($pdf, $object, 0, $outputlangs); + $this->_pagehead($pdf, $object, 0, $outputlangs, $outputlangsbis); } // $this->_pagefoot($pdf,$object,$outputlangs,1); $pdf->setTopMargin($tab_top_newpage); @@ -556,7 +556,7 @@ class pdf_sponge extends ModelePDFFactures $pdf->useTemplate($tplidx); } if (empty($conf->global->MAIN_PDF_DONOTREPEAT_HEAD)) { - $this->_pagehead($pdf, $object, 0, $outputlangs); + $this->_pagehead($pdf, $object, 0, $outputlangs, $outputlangsbis); } $height_note = $posyafter - $tab_top_newpage; $pdf->Rect($this->marge_gauche, $tab_top_newpage - 1, $tab_width, $height_note + 1); @@ -578,7 +578,7 @@ class pdf_sponge extends ModelePDFFactures $pdf->useTemplate($tplidx); } if (empty($conf->global->MAIN_PDF_DONOTREPEAT_HEAD)) { - $this->_pagehead($pdf, $object, 0, $outputlangs); + $this->_pagehead($pdf, $object, 0, $outputlangs, $outputlangsbis); } $posyafter = $tab_top_newpage; @@ -873,7 +873,7 @@ class pdf_sponge extends ModelePDFFactures $pdf->setPage($pagenb); $pdf->setPageOrientation('', 1, 0); // The only function to edit the bottom margin of current page to set it. if (empty($conf->global->MAIN_PDF_DONOTREPEAT_HEAD)) { - $this->_pagehead($pdf, $object, 0, $outputlangs); + $this->_pagehead($pdf, $object, 0, $outputlangs, $outputlangsbis); } } @@ -891,7 +891,7 @@ class pdf_sponge extends ModelePDFFactures } $pagenb++; if (empty($conf->global->MAIN_PDF_DONOTREPEAT_HEAD)) { - $this->_pagehead($pdf, $object, 0, $outputlangs); + $this->_pagehead($pdf, $object, 0, $outputlangs, $outputlangsbis); } } } @@ -977,7 +977,7 @@ class pdf_sponge extends ModelePDFFactures $tab3_width = 80; $tab3_height = 4; if ($this->page_largeur < 210) { // To work with US executive format - $tab3_posx -= 20; + $tab3_posx -= 15; } $default_font_size = pdf_getPDFFontSize($outputlangs); @@ -1292,7 +1292,8 @@ class pdf_sponge extends ModelePDFFactures $col1x = 120; $col2x = 170; if ($this->page_largeur < 210) { // To work with US executive format - $col2x -= 20; + $col1x -= 15; + $col2x -= 10; } $largcol2 = ($this->page_largeur - $this->marge_droite - $col2x); From e46ee982ec0586b0b5ec23e43aa950a8adbaf3b3 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Thu, 17 Mar 2022 18:53:50 +0100 Subject: [PATCH 08/24] FIX Option to manage packaging. FIX size/length/volume of reception Conflicts: htdocs/fourn/class/fournisseur.commande.class.php htdocs/install/mysql/migration/14.0.0-15.0.0.sql htdocs/langs/en_US/products.lang htdocs/reception/card.php htdocs/reception/class/reception.class.php --- htdocs/core/modules/modProduct.class.php | 2 +- htdocs/core/modules/modService.class.php | 2 +- .../class/fournisseur.commande.class.php | 98 ++++++++++++------- .../fourn/class/fournisseur.product.class.php | 25 ++--- .../install/mysql/migration/13.0.0-14.0.0.sql | 4 +- .../tables/llx_product_fournisseur_price.sql | 2 +- htdocs/langs/en_US/products.lang | 2 +- htdocs/langs/en_US/stocks.lang | 2 +- htdocs/langs/fr_FR/products.lang | 2 +- htdocs/product/fournisseurs.php | 3 +- htdocs/reception/card.php | 19 ++-- htdocs/reception/class/reception.class.php | 21 ++-- 12 files changed, 105 insertions(+), 77 deletions(-) diff --git a/htdocs/core/modules/modProduct.class.php b/htdocs/core/modules/modProduct.class.php index 5e51dd0344d..e5b0473597b 100644 --- a/htdocs/core/modules/modProduct.class.php +++ b/htdocs/core/modules/modProduct.class.php @@ -855,7 +855,7 @@ class modProduct extends DolibarrModules } if (!empty($conf->global->PRODUCT_USE_SUPPLIER_PACKAGING)) { $this->import_examplevalues_array[$r] = array_merge($this->import_examplevalues_array[$r], array( - 'sp.packagning'=>'1', + 'sp.packaging'=>'10', )); } diff --git a/htdocs/core/modules/modService.class.php b/htdocs/core/modules/modService.class.php index 0390a23cc83..bdc3388da5c 100644 --- a/htdocs/core/modules/modService.class.php +++ b/htdocs/core/modules/modService.class.php @@ -786,7 +786,7 @@ class modService extends DolibarrModules } if (!empty($conf->global->PRODUCT_USE_SUPPLIER_PACKAGING)) { $this->import_examplevalues_array[$r] = array_merge($this->import_examplevalues_array[$r], array( - 'sp.packagning'=>'1', + 'sp.packagning'=>'10', )); } diff --git a/htdocs/fourn/class/fournisseur.commande.class.php b/htdocs/fourn/class/fournisseur.commande.class.php index 528bda30442..5f671599c50 100644 --- a/htdocs/fourn/class/fournisseur.commande.class.php +++ b/htdocs/fourn/class/fournisseur.commande.class.php @@ -32,7 +32,7 @@ * \brief File of class to manage suppliers orders */ -include_once DOL_DOCUMENT_ROOT.'/core/class/commonorder.class.php'; +require_once DOL_DOCUMENT_ROOT.'/core/class/commonorder.class.php'; require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; if (!empty($conf->productbatch->enabled)) { require_once DOL_DOCUMENT_ROOT.'/product/class/productbatch.class.php'; @@ -431,6 +431,7 @@ class CommandeFournisseur extends CommonOrder * Lines */ $result = $this->fetch_lines(); + if ($result < 0) { return -1; } else { @@ -453,7 +454,7 @@ class CommandeFournisseur extends CommonOrder { global $conf; // phpcs:enable - //$result=$this->fetch_lines(); + $this->lines = array(); $sql = "SELECT l.rowid, l.ref as ref_supplier, l.fk_product, l.product_type, l.label, l.description, l.qty,"; @@ -464,22 +465,12 @@ class CommandeFournisseur extends CommonOrder $sql .= " l.fk_unit,"; $sql .= " l.date_start, l.date_end,"; $sql .= ' l.fk_multicurrency, l.multicurrency_code, l.multicurrency_subprice, l.multicurrency_total_ht, l.multicurrency_total_tva, l.multicurrency_total_ttc'; - if (!empty($conf->global->PRODUCT_USE_SUPPLIER_PACKAGING)) { - $sql .= ", pfp.rowid as fk_pfp, pfp.packaging, MAX(pfp.quantity) as max_qty"; - } $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as l"; $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON l.fk_product = p.rowid'; - if (!empty($conf->global->PRODUCT_USE_SUPPLIER_PACKAGING)) { - $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_fournisseur_price as pfp ON pfp.entity IN (".getEntity('product_fournisseur_price').") AND l.fk_product = pfp.fk_product and l.ref = pfp.ref_fourn AND l.qty >= pfp.quantity AND pfp.fk_soc = ".((int) $this->socid); - } - $sql .= " WHERE l.fk_commande = ".$this->id; + $sql .= " WHERE l.fk_commande = ".((int) $this->id); if ($only_product) { $sql .= ' AND p.fk_product_type = 0'; } - if (!empty($conf->global->PRODUCT_USE_SUPPLIER_PACKAGING)) { - $sql.= " GROUP BY l.rowid"; - $sql.= " HAVING (max_qty = MAX(pfp.quantity) OR max_qty IS NULL)"; - } $sql .= " ORDER BY l.rang, l.rowid"; //print $sql; @@ -527,11 +518,34 @@ class CommandeFournisseur extends CommonOrder $line->ref_supplier = $objp->ref_supplier; // The supplier ref of price when product was added. May have change since if (!empty($conf->global->PRODUCT_USE_SUPPLIER_PACKAGING)) { - $line->fk_fournprice = $objp->fk_pfp; - $line->packaging = $objp->packaging; + // TODO We should not fetch this properties into the fetch_lines. This is NOT properties of a line. + // Move this into another method and call it when required. + + // Take better packaging for $objp->qty (first supplier ref quantity <= $objp->qty) + $sqlsearchpackage = 'SELECT rowid, packaging FROM '.MAIN_DB_PREFIX."product_fournisseur_price"; + $sqlsearchpackage .= ' WHERE entity IN ('.getEntity('product_fournisseur_price').")"; + $sqlsearchpackage .= " AND fk_product = ".((int) $objp->fk_product); + $sqlsearchpackage .= " AND ref_fourn = '".$this->db->escape($objp->ref_supplier)."'"; + $sqlsearchpackage .= " AND quantity <= ".((float) $objp->qty); // required to be qualified + $sqlsearchpackage .= " AND (packaging IS NULL OR packaging = 0 OR packaging <= ".((float) $objp->qty).")"; // required to be qualified + $sqlsearchpackage .= " AND fk_soc = ".((int) $this->socid); + $sqlsearchpackage .= " ORDER BY packaging ASC"; // Take the smaller package first + $sqlsearchpackage .= " LIMIT 1"; + + $resqlsearchpackage = $this->db->query($sqlsearchpackage); + if ($resqlsearchpackage) { + $objsearchpackage = $this->db->fetch_object($resqlsearchpackage); + if ($objsearchpackage) { + $line->fk_fournprice = $objsearchpackage->rowid; + $line->packaging = $objsearchpackage->packaging; + } + } else { + $this->error = $this->db->lasterror(); + return -1; + } } - $line->date_start = $this->db->jdate($objp->date_start); + $line->date_start = $this->db->jdate($objp->date_start); $line->date_end = $this->db->jdate($objp->date_end); $line->fk_unit = $objp->fk_unit; @@ -1807,6 +1821,7 @@ class CommandeFournisseur extends CommonOrder if (!empty($conf->global->PRODUCT_USE_SUPPLIER_PACKAGING)) { $prod = new Product($this->db, $fk_product); $prod->get_buyprice($fk_prod_fourn_price, $qty, $fk_product, 'none', ($this->fk_soc ? $this->fk_soc : $this->socid)); + if ($qty < $prod->packaging) { $qty = $prod->packaging; } else { @@ -3515,25 +3530,18 @@ class CommandeFournisseurLigne extends CommonOrderLine global $conf; $sql = 'SELECT cd.rowid, cd.fk_commande, cd.fk_product, cd.product_type, cd.description, cd.qty, cd.tva_tx, cd.special_code,'; - $sql .= ' cd.localtax1_tx, cd.localtax2_tx, cd.localtax1_type, cd.localtax2_type, cd.ref,'; + $sql .= ' cd.localtax1_tx, cd.localtax2_tx, cd.localtax1_type, cd.localtax2_type, cd.ref as ref_supplier,'; $sql .= ' cd.remise, cd.remise_percent, cd.subprice,'; $sql .= ' cd.info_bits, cd.total_ht, cd.total_tva, cd.total_ttc,'; $sql .= ' cd.total_localtax1, cd.total_localtax2,'; $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc,'; $sql .= ' cd.date_start, cd.date_end, cd.fk_unit,'; - $sql .= ' cd.multicurrency_subprice, cd.multicurrency_total_ht, cd.multicurrency_total_tva, cd.multicurrency_total_ttc'; - if (!empty($conf->global->PRODUCT_USE_SUPPLIER_PACKAGING)) { - $sql .= ", pfp.rowid as fk_pfp, pfp.packaging, MAX(pfp.quantity) as max_qty"; - } - $sql .= ' FROM '.MAIN_DB_PREFIX.'commande_fournisseurdet as cd'; + $sql .= ' cd.multicurrency_subprice, cd.multicurrency_total_ht, cd.multicurrency_total_tva, cd.multicurrency_total_ttc,'; + $sql .= ' c.fk_soc as socid'; + $sql .= ' FROM '.MAIN_DB_PREFIX.'commande_fournisseur as c, '.MAIN_DB_PREFIX.'commande_fournisseurdet as cd'; $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON cd.fk_product = p.rowid'; - if (!empty($conf->global->PRODUCT_USE_SUPPLIER_PACKAGING)) { - $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_fournisseur_price as pfp ON pfp.entity IN (".getEntity('product_fournisseur_price').") AND cd.fk_product = pfp.fk_product and cd.ref = pfp.ref_fourn AND cd.qty >= pfp.quantity"; - } - $sql .= ' WHERE cd.rowid = '.((int) $rowid); - if (!empty($conf->global->PRODUCT_USE_SUPPLIER_PACKAGING)) { - $sql .= " GROUP BY cd.rowid HAVING (max_qty = MAX(pfp.quantity) OR max_qty IS NULL)"; - } + $sql .= ' WHERE cd.fk_commande = c.rowid AND cd.rowid = '.((int) $rowid); + $result = $this->db->query($sql); if ($result) { $objp = $this->db->fetch_object($result); @@ -3544,8 +3552,8 @@ class CommandeFournisseurLigne extends CommonOrderLine $this->fk_commande = $objp->fk_commande; $this->desc = $objp->description; $this->qty = $objp->qty; - $this->ref_fourn = $objp->ref; - $this->ref_supplier = $objp->ref; + $this->ref_fourn = $objp->ref_supplier; + $this->ref_supplier = $objp->ref_supplier; $this->subprice = $objp->subprice; $this->tva_tx = $objp->tva_tx; $this->localtax1_tx = $objp->localtax1_tx; @@ -3569,9 +3577,33 @@ class CommandeFournisseurLigne extends CommonOrderLine $this->product_ref = $objp->product_ref; $this->product_label = $objp->product_label; $this->product_desc = $objp->product_desc; + if (!empty($conf->global->PRODUCT_USE_SUPPLIER_PACKAGING)) { - $this->packaging = $objp->packaging; - $this->fk_fournprice = $objp->fk_pfp; + // TODO We should not fetch this properties into the fetch_lines. This is NOT properties of a line. + // Move this into another method and call it when required. + + // Take better packaging for $objp->qty (first supplier ref quantity <= $objp->qty) + $sqlsearchpackage = 'SELECT rowid, packaging FROM '.MAIN_DB_PREFIX."product_fournisseur_price"; + $sqlsearchpackage .= ' WHERE entity IN ('.getEntity('product_fournisseur_price').")"; + $sqlsearchpackage .= " AND fk_product = ".((int) $objp->fk_product); + $sqlsearchpackage .= " AND ref_fourn = '".$this->db->escape($objp->ref_supplier)."'"; + $sqlsearchpackage .= " AND quantity <= ".((float) $objp->qty); // required to be qualified + $sqlsearchpackage .= " AND (packaging IS NULL OR packaging = 0 OR packaging <= ".((float) $objp->qty).")"; // required to be qualified + $sqlsearchpackage .= " AND fk_soc = ".((int) $objp->socid); + $sqlsearchpackage .= " ORDER BY packaging ASC"; // Take the smaller package first + $sqlsearchpackage .= " LIMIT 1"; + + $resqlsearchpackage = $this->db->query($sqlsearchpackage); + if ($resqlsearchpackage) { + $objsearchpackage = $this->db->fetch_object($resqlsearchpackage); + if ($objsearchpackage) { + $this->fk_fournprice = $objsearchpackage->rowid; + $this->packaging = $objsearchpackage->packaging; + } + } else { + $this->error = $this->db->lasterror(); + return -1; + } } $this->date_start = $this->db->jdate($objp->date_start); diff --git a/htdocs/fourn/class/fournisseur.product.class.php b/htdocs/fourn/class/fournisseur.product.class.php index 6232b901382..ffb9bc06229 100644 --- a/htdocs/fourn/class/fournisseur.product.class.php +++ b/htdocs/fourn/class/fournisseur.product.class.php @@ -317,7 +317,9 @@ class ProductFournisseur extends Product $qty = price2num($qty, 'MS'); $unitBuyPrice = price2num($buyprice / $qty, 'MU'); - $packaging = price2num(((empty($this->packaging) || $this->packaging < $qty) ? $qty : $this->packaging), 'MS'); + // We can have a puchase ref that need to buy 100 min for a given price and with a packaging of 50. + //$packaging = price2num(((empty($this->packaging) || $this->packaging < $qty) ? $qty : $this->packaging), 'MS'); + $packaging = price2num((empty($this->packaging) ? $qty : $this->packaging), 'MS'); $error = 0; $now = dol_now(); @@ -405,6 +407,7 @@ class ProductFournisseur extends Product $sql .= ", packaging = ".(empty($packaging) ? 1 : $packaging); } $sql .= " WHERE rowid = ".((int) $this->product_fourn_price_id); + //print $sql;exit; // TODO Add price_base_type and price_ttc dol_syslog(get_class($this).'::update_buyprice update knowing id of line = product_fourn_price_id = '.$this->product_fourn_price_id, LOG_DEBUG); @@ -593,13 +596,7 @@ class ProductFournisseur extends Product $this->supplier_barcode = $obj->barcode; $this->supplier_fk_barcode_type = $obj->fk_barcode_type; } - - if (!empty($conf->global->PRODUCT_USE_SUPPLIER_PACKAGING)) { - $this->packaging = $obj->packaging; - if ($this->packaging < $this->fourn_qty) { - $this->packaging = $this->fourn_qty; - } - } + $this->packaging = $obj->packaging; if (empty($ignore_expression) && !empty($this->fk_supplier_price_expression)) { $priceparser = new PriceParser($this->db); @@ -647,10 +644,7 @@ class ProductFournisseur extends Product $sql .= " pfp.rowid as product_fourn_pri_id, pfp.entity, pfp.ref_fourn, pfp.desc_fourn, pfp.fk_product as product_fourn_id, pfp.fk_supplier_price_expression,"; $sql .= " pfp.price, pfp.quantity, pfp.unitprice, pfp.remise_percent, pfp.remise, pfp.tva_tx, pfp.fk_availability, pfp.charges, pfp.info_bits, pfp.delivery_time_days, pfp.supplier_reputation,"; $sql .= " pfp.multicurrency_price, pfp.multicurrency_unitprice, pfp.multicurrency_tx, pfp.fk_multicurrency, pfp.multicurrency_code, pfp.datec, pfp.tms,"; - $sql .= " pfp.barcode, pfp.fk_barcode_type"; - if (!empty($conf->global->PRODUCT_USE_SUPPLIER_PACKAGING)) { - $sql .= ", pfp.packaging"; - } + $sql .= " pfp.barcode, pfp.fk_barcode_type, pfp.packaging"; $sql .= " FROM ".MAIN_DB_PREFIX."product_fournisseur_price as pfp, ".MAIN_DB_PREFIX."product as p, ".MAIN_DB_PREFIX."societe as s"; $sql .= " WHERE pfp.entity IN (".getEntity('productsupplierprice').")"; $sql .= " AND pfp.fk_soc = s.rowid AND pfp.fk_product = p.rowid"; @@ -704,12 +698,7 @@ class ProductFournisseur extends Product $prodfourn->fourn_multicurrency_id = $record["fk_multicurrency"]; $prodfourn->fourn_multicurrency_code = $record["multicurrency_code"]; - if (!empty($conf->global->PRODUCT_USE_SUPPLIER_PACKAGING)) { - $prodfourn->packaging = $record["packaging"]; - if ($prodfourn->packaging < $prodfourn->fourn_qty) { - $prodfourn->packaging = $prodfourn->fourn_qty; - } - } + $prodfourn->packaging = $record["packaging"]; if (!empty($conf->barcode->enabled)) { $prodfourn->supplier_barcode = $record["barcode"]; diff --git a/htdocs/install/mysql/migration/13.0.0-14.0.0.sql b/htdocs/install/mysql/migration/13.0.0-14.0.0.sql index e8a3880ff40..0b6d36d6743 100644 --- a/htdocs/install/mysql/migration/13.0.0-14.0.0.sql +++ b/htdocs/install/mysql/migration/13.0.0-14.0.0.sql @@ -84,8 +84,8 @@ UPDATE llx_const set value = __ENCRYPT('eldy')__ WHERE __DECRYPT('value')__ = 'a UPDATE llx_const set value = __ENCRYPT('eldy')__ WHERE __DECRYPT('value')__ = 'cameleo'; DELETE FROM llx_user_param where param = 'MAIN_THEME' and value in ('auguria', 'amarok', 'cameleo'); -ALTER TABLE llx_product_fournisseur_price ADD COLUMN packaging varchar(64) DEFAULT NULL; -ALTER TABLE llx_product_fournisseur_price MODIFY COLUMN packaging varchar(64) DEFAULT NULL; +ALTER TABLE llx_product_fournisseur_price ADD COLUMN packaging real DEFAULT NULL; +ALTER TABLE llx_product_fournisseur_price MODIFY COLUMN packaging real DEFAULT NULL; -- For v14 diff --git a/htdocs/install/mysql/tables/llx_product_fournisseur_price.sql b/htdocs/install/mysql/tables/llx_product_fournisseur_price.sql index 2e9c88bd5ca..e5a0865ef68 100644 --- a/htdocs/install/mysql/tables/llx_product_fournisseur_price.sql +++ b/htdocs/install/mysql/tables/llx_product_fournisseur_price.sql @@ -50,7 +50,7 @@ create table llx_product_fournisseur_price import_key varchar(14), -- Import key delivery_time_days integer, supplier_reputation varchar(10), - packaging varchar(64) DEFAULT NULL, + packaging real DEFAULT NULL, fk_multicurrency integer, multicurrency_code varchar(255), multicurrency_tx double(24,8) DEFAULT 1, diff --git a/htdocs/langs/en_US/products.lang b/htdocs/langs/en_US/products.lang index fecbe7450c2..7173d465ff2 100644 --- a/htdocs/langs/en_US/products.lang +++ b/htdocs/langs/en_US/products.lang @@ -345,7 +345,7 @@ UseProductFournDesc=Add a feature to define the descriptions of products defined ProductSupplierDescription=Vendor description for the product UseProductSupplierPackaging=Use packaging on supplier prices (recalculate quantities according to packaging set on supplier price when adding/updating line in supplier documents) PackagingForThisProduct=Packaging -PackagingForThisProductDesc=On supplier order, you will automaticly order this quantity (or a multiple of this quantity). Cannot be less than minimum buying quantity +PackagingForThisProductDesc=You will automaticaly purchase a multiple of this quantity. QtyRecalculatedWithPackaging=The quantity of the line were recalculated according to supplier packaging #Attributes diff --git a/htdocs/langs/en_US/stocks.lang b/htdocs/langs/en_US/stocks.lang index 16917384460..17ccbc35f83 100644 --- a/htdocs/langs/en_US/stocks.lang +++ b/htdocs/langs/en_US/stocks.lang @@ -176,7 +176,7 @@ ProductStockWarehouseCreated=Stock limit for alert and desired optimal stock cor ProductStockWarehouseUpdated=Stock limit for alert and desired optimal stock correctly updated ProductStockWarehouseDeleted=Stock limit for alert and desired optimal stock correctly deleted AddNewProductStockWarehouse=Set new limit for alert and desired optimal stock -AddStockLocationLine=Decrease quantity then click to add another warehouse for this product +AddStockLocationLine=Decrease quantity then click to split the line InventoryDate=Inventory date Inventories=Inventories NewInventory=New inventory diff --git a/htdocs/langs/fr_FR/products.lang b/htdocs/langs/fr_FR/products.lang index cec5c583ac9..54540b14109 100644 --- a/htdocs/langs/fr_FR/products.lang +++ b/htdocs/langs/fr_FR/products.lang @@ -346,7 +346,7 @@ UseProductFournDesc=Ajouter une fonctionnalité pour définir la description pro ProductSupplierDescription=Description du fournisseur du produit UseProductSupplierPackaging=Utiliser le conditionnement/emballage sur les prix fournisseur (recalculer les quantités en fonction de l'emballage défini sur le prix fournisseur lors de l'ajout / mise à jour de la ligne dans les documents fournisseurs) PackagingForThisProduct=Emballage -PackagingForThisProductDesc=Sur une commande fournisseur, vous commanderez automatiquement cette quantité ou un multiple. Vous ne pourrez pas saisir un montant inférieur +PackagingForThisProductDesc=Sur une commande fournisseur, vous commanderez automatiquement cette quantité ou un multiple. QtyRecalculatedWithPackaging=La quantité de la ligne a été recalculée en fonction de l'emballage du fournisseur #Attributes diff --git a/htdocs/product/fournisseurs.php b/htdocs/product/fournisseurs.php index 9aaa0aa2c8c..ea11843b625 100644 --- a/htdocs/product/fournisseurs.php +++ b/htdocs/product/fournisseurs.php @@ -320,9 +320,10 @@ if (empty($reshook)) { if (empty($packaging)) { $packaging = 1; } + /* We can have a puchase ref that need to buy 100 min for a given price and with a packaging of 50. if ($packaging < $quantity) { $packaging = $quantity; - } + }*/ $object->packaging = $packaging; if (!empty($conf->multicurrency->enabled)) { diff --git a/htdocs/reception/card.php b/htdocs/reception/card.php index 48b0953e399..fda5a3910b7 100644 --- a/htdocs/reception/card.php +++ b/htdocs/reception/card.php @@ -254,10 +254,10 @@ if (empty($reshook)) { $object->origin = $origin; $object->origin_id = $origin_id; $object->fk_project = GETPOST('projectid', 'int'); - $object->weight = GETPOST('weight', 'int') == '' ? "NULL" : GETPOST('weight', 'int'); - $object->sizeH = GETPOST('sizeH', 'int') == '' ? "NULL" : GETPOST('sizeH', 'int'); - $object->sizeW = GETPOST('sizeW', 'int') == '' ? "NULL" : GETPOST('sizeW', 'int'); - $object->sizeS = GETPOST('sizeS', 'int') == '' ? "NULL" : GETPOST('sizeS', 'int'); + $object->weight = GETPOST('weight', 'int') == '' ? null : GETPOST('weight', 'int'); + $object->sizeH = GETPOST('sizeH', 'int') == '' ? null : GETPOST('sizeH', 'int'); + $object->sizeW = GETPOST('sizeW', 'int') == '' ? null : GETPOST('sizeW', 'int'); + $object->sizeS = GETPOST('sizeS', 'int') == '' ? null : GETPOST('sizeS', 'int'); $object->size_units = GETPOST('size_units', 'int'); $object->weight_units = GETPOST('weight_units', 'int'); @@ -1921,15 +1921,16 @@ if ($action == 'create') { // Warehouse source if (!empty($conf->stock->enabled)) { - print ''; - if ($lines[$i]->fk_entrepot > 0) { $entrepot = new Entrepot($db); $entrepot->fetch($lines[$i]->fk_entrepot); - print $entrepot->getNomUrl(1); - } - print ''; + print ''; + print $entrepot->getNomUrl(1); + print ''; + } else { + print ''; + } } // Batch number managment diff --git a/htdocs/reception/class/reception.class.php b/htdocs/reception/class/reception.class.php index 5fb8e3b8af0..0a025973a6d 100644 --- a/htdocs/reception/class/reception.class.php +++ b/htdocs/reception/class/reception.class.php @@ -236,7 +236,6 @@ class Reception extends CommonObject $this->user = $user; - $this->db->begin(); $sql = "INSERT INTO ".MAIN_DB_PREFIX."reception ("; @@ -273,12 +272,12 @@ class Reception extends CommonObject $sql .= ", ".$this->fk_project; $sql .= ", ".($this->shipping_method_id > 0 ? $this->shipping_method_id : "null"); $sql .= ", '".$this->db->escape($this->tracking_number)."'"; - $sql .= ", ".$this->weight; - $sql .= ", ".$this->sizeS; // TODO Should use this->trueDepth - $sql .= ", ".$this->sizeW; // TODO Should use this->trueWidth - $sql .= ", ".$this->sizeH; // TODO Should use this->trueHeight - $sql .= ", ".$this->weight_units; - $sql .= ", ".$this->size_units; + $sql .= ", ".(is_null($this->weight) ? "NULL" : ((double) $this->weight)); + $sql .= ", ".(is_null($this->sizeS) ? "NULL" : ((double) $this->sizeS)); // TODO Should use this->trueDepth + $sql .= ", ".(is_null($this->sizeW) ? "NULL" : ((double) $this->sizeW)); // TODO Should use this->trueWidth + $sql .= ", ".(is_null($this->sizeH) ? "NULL" : ((double) $this->sizeH)); // TODO Should use this->trueHeight + $sql .= ", ".(is_null($this->weight_units) ? "NULL" : ((double) $this->weight_units)); + $sql .= ", ".(is_null($this->size_units) ? "NULL" : ((double) $this->size_units)); $sql .= ", ".(!empty($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null"); $sql .= ", ".(!empty($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null"); $sql .= ", ".(!empty($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null"); @@ -727,8 +726,14 @@ class Reception extends CommonObject $line->qty = $qty; $supplierorderline = new CommandeFournisseurLigne($this->db); - $supplierorderline->fetch($id); + $result = $supplierorderline->fetch($id); + if ($result <= 0) { + $this->error = $supplierorderline->error; + $this->errors = $supplierorderline->errors; + return -1; + } + $fk_product = 0; if (!empty($conf->stock->enabled) && !empty($supplierorderline->fk_product)) { $fk_product = $supplierorderline->fk_product; From 876a013ded4501610fd42c6020b79e4ee0e734de Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Thu, 17 Mar 2022 19:09:58 +0100 Subject: [PATCH 09/24] Fix migration --- htdocs/install/mysql/migration/14.0.0-15.0.0.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/htdocs/install/mysql/migration/14.0.0-15.0.0.sql b/htdocs/install/mysql/migration/14.0.0-15.0.0.sql index 5987c4be652..ab037313dfb 100644 --- a/htdocs/install/mysql/migration/14.0.0-15.0.0.sql +++ b/htdocs/install/mysql/migration/14.0.0-15.0.0.sql @@ -35,8 +35,8 @@ -- VMYSQL4.3 ALTER TABLE llx_partnership MODIFY COLUMN date_partnership_end date NULL; -- VPGSQL8.2 ALTER TABLE llx_partnership ALTER COLUMN date_partnership_end DROP NOT NULL; -ALTER TABLE llx_product_fournisseur_price ADD COLUMN packaging varchar(64) DEFAULT NULL; -ALTER TABLE llx_product_fournisseur_price MODIFY COLUMN packaging varchar(64) DEFAULT NULL; +ALTER TABLE llx_product_fournisseur_price ADD COLUMN packaging real DEFAULT NULL; +ALTER TABLE llx_product_fournisseur_price MODIFY COLUMN packaging real DEFAULT NULL; ALTER TABLE llx_accounting_bookkeeping ADD COLUMN date_export datetime DEFAULT NULL; From 60d9db248fb7809085db61cb0f5f25fb9c60a539 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Fri, 18 Mar 2022 13:38:07 +0100 Subject: [PATCH 10/24] NEW The backup tools has an "lowmemory" for mysqldump on large database --- htdocs/admin/tools/dolibarr_export.php | 29 ++++++++--- htdocs/core/class/utils.class.php | 67 +++++++++++++++++++------- htdocs/langs/en_US/admin.lang | 2 + 3 files changed, 75 insertions(+), 23 deletions(-) diff --git a/htdocs/admin/tools/dolibarr_export.php b/htdocs/admin/tools/dolibarr_export.php index 6ed3bd00863..36136aeef24 100644 --- a/htdocs/admin/tools/dolibarr_export.php +++ b/htdocs/admin/tools/dolibarr_export.php @@ -214,10 +214,6 @@ if (in_array($type, array('mysql', 'mysqli'))) { print '
'; print '
'.$langs->trans("ExportOptions").''; - print '
'; - print ''; - print ''; - print '
'; if (!empty($conf->global->MYSQL_OLD_OPTION_DISABLE_FK)) { print '
'; @@ -239,14 +235,35 @@ if (in_array($type, array('mysql', 'mysqli'))) { print ''; print ''; print ''; - print '
'; + print '

'; - print ''; + print '
'; + print ''; + print ''; + print '
'; + + print ''; print ''; print '
'; + $execmethod = 0; + if (!empty($conf->global->MAIN_EXEC_USE_POPEN)) { + $execmethod = $conf->global->MAIN_EXEC_USE_POPEN; + } + if (empty($execmethod)) { + $execmethod = 1; + } + if ($execmethod == 1) { + // If we use the "exec" method for shell, we ask if we need to use the alternative low memory exec mode. + print ''; + print ''; + print '
'; + } + print ''; diff --git a/htdocs/core/class/utils.class.php b/htdocs/core/class/utils.class.php index f59b248a384..4a4db439822 100644 --- a/htdocs/core/class/utils.class.php +++ b/htdocs/core/class/utils.class.php @@ -189,7 +189,7 @@ class Utils * @param int $usedefault 1=Use default backup profile (Set this to 1 when used as cron) * @param string $file 'auto' or filename to build * @param int $keeplastnfiles Keep only last n files (not used yet) - * @param int $execmethod 0=Use default method (that is 1 by default), 1=Use the PHP 'exec', 2=Use the 'popen' method + * @param int $execmethod 0=Use default method (that is 1 by default), 1=Use the PHP 'exec' - need size of dump in memory, but low memory method is used if GETPOST('lowmemorydump') is set, 2=Use the 'popen' method (low memory method) * @return int 0 if OK, < 0 if KO (this function is used also by cron so only 0 is OK) */ public function dumpDatabase($compression = 'none', $type = 'auto', $usedefault = 1, $file = 'auto', $keeplastnfiles = 0, $execmethod = 0) @@ -278,8 +278,8 @@ class Utils if (!empty($dolibarr_main_db_port)) { $param .= " -P ".$dolibarr_main_db_port; } - if (!GETPOST("use_transaction", "alpha")) { - $param .= " -l --single-transaction"; + if (GETPOST("use_transaction", "alpha")) { + $param .= " --single-transaction"; } if (GETPOST("disable_fk", "alpha") || $usedefault) { $param .= " -K"; @@ -342,17 +342,42 @@ class Utils $handle = ''; + $lowmemorydump = GETPOSTISSET("lowmemorydump", "alpha") ? GETPOST("lowmemorydump") : getDolGlobalString('MAIN_LOW_MEMORY_DUMP'); + // Start call method to execute dump $fullcommandcrypted = $command." ".$paramcrypted." 2>&1"; $fullcommandclear = $command." ".$paramclear." 2>&1"; - if ($compression == 'none') { - $handle = fopen($outputfile, 'w'); - } elseif ($compression == 'gz') { - $handle = gzopen($outputfile, 'w'); - } elseif ($compression == 'bz') { - $handle = bzopen($outputfile, 'w'); - } elseif ($compression == 'zstd') { - $handle = fopen($outputfile, 'w'); + if (!$lowmemorydump) { + if ($compression == 'none') { + $handle = fopen($outputfile, 'w'); + } elseif ($compression == 'gz') { + $handle = gzopen($outputfile, 'w'); + } elseif ($compression == 'bz') { + $handle = bzopen($outputfile, 'w'); + } elseif ($compression == 'zstd') { + $handle = fopen($outputfile, 'w'); + } + } else { + if ($compression == 'none') { + $fullcommandclear .= " > ".$outputfile; + $fullcommandcrypted .= " > ".$outputfile; + $handle = 1; + } elseif ($compression == 'gz') { + $fullcommandclear .= " | gzip > ".$outputfile; + $fullcommandcrypted .= " | gzip > ".$outputfile; + $paramcrypted.=" | gzip"; + $handle = 1; + } elseif ($compression == 'bz') { + $fullcommandclear .= " | bzip2 > ".$outputfile; + $fullcommandcrypted .= " | bzip2 > ".$outputfile; + $paramcrypted.=" | bzip2"; + $handle = 1; + } elseif ($compression == 'zstd') { + $fullcommandclear .= " | zstd > ".$outputfile; + $fullcommandcrypted .= " | zstd > ".$outputfile; + $paramcrypted.=" | zstd"; + $handle = 1; + } } $ok = 0; @@ -374,7 +399,8 @@ class Utils } - // TODO Replace with executeCLI function + // TODO Replace with executeCLI function but + // we must first introduce a low memory mode if ($execmethod == 1) { $output_arr = array(); $retval = null; @@ -394,16 +420,23 @@ class Utils if ($i == 1 && preg_match('/Warning.*Using a password/i', $read)) { continue; } - fwrite($handle, $read.($execmethod == 2 ? '' : "\n")); - if (preg_match('/'.preg_quote('-- Dump completed', '/').'/i', $read)) { - $ok = 1; - } elseif (preg_match('/'.preg_quote('SET SQL_NOTES=@OLD_SQL_NOTES', '/').'/i', $read)) { - $ok = 1; + if (!$lowmemorydump) { + fwrite($handle, $read.($execmethod == 2 ? '' : "\n")); + if (preg_match('/'.preg_quote('-- Dump completed', '/').'/i', $read)) { + $ok = 1; + } elseif (preg_match('/'.preg_quote('SET SQL_NOTES=@OLD_SQL_NOTES', '/').'/i', $read)) { + $ok = 1; + } + } else { + // If we have a result here in lowmemorydump mode, something is strange } } + } elseif ($lowmemorydump) { + $ok = 1; } } } + if ($execmethod == 2) { // With this method, there is no way to get the return code, only output $handlein = popen($fullcommandclear, 'r'); $i = 0; diff --git a/htdocs/langs/en_US/admin.lang b/htdocs/langs/en_US/admin.lang index cffd3532c05..9cae7524022 100644 --- a/htdocs/langs/en_US/admin.lang +++ b/htdocs/langs/en_US/admin.lang @@ -2225,3 +2225,5 @@ PreviousHash=Previous hash LateWarningAfter="Late" warning after TemplateforBusinessCards=Template for a business card in different size InventorySetup= Inventory Setup +ExportUseLowMemoryMode=Use a low memory mode +ExportUseLowMemoryModeHelp=Use the low memory mode to execute the exec of the dump (compression is done through a pipe instead of into the PHP memory). This method does not allow to check that file is completed and error message can't be reported if it fails. From 4ef9d29b9d0514ffc6695e5ca01263d3c881449a Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Fri, 18 Mar 2022 14:09:53 +0100 Subject: [PATCH 11/24] Fix missing constraints --- htdocs/install/mysql/migration/15.0.0-16.0.0.sql | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/htdocs/install/mysql/migration/15.0.0-16.0.0.sql b/htdocs/install/mysql/migration/15.0.0-16.0.0.sql index 5e9a0250a38..2933acd00d7 100644 --- a/htdocs/install/mysql/migration/15.0.0-16.0.0.sql +++ b/htdocs/install/mysql/migration/15.0.0-16.0.0.sql @@ -91,6 +91,10 @@ INSERT INTO llx_c_forme_juridique (fk_pays, code, libelle, active) VALUES (154, INSERT INTO llx_c_forme_juridique (fk_pays, code, libelle, active) VALUES (154, '15419', '626 - Régimen Simplificado de Confianza', 1); +ALTER TABLE llx_partnership ADD UNIQUE INDEX uk_fk_type_fk_soc (fk_type, fk_soc, date_partnership_start); +ALTER TABLE llx_partnership ADD UNIQUE INDEX uk_fk_type_fk_member (fk_type, fk_member, date_partnership_start); + + -- v16 ALTER TABLE llx_projet_task_time ADD COLUMN fk_product integer NULL; From 47acdc8f34dfe7499baa368f7da952cb1204fb45 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Fri, 18 Mar 2022 14:10:42 +0100 Subject: [PATCH 12/24] Fix database corruption without that. --- htdocs/install/mysql/tables/llx_partnership.key.sql | 3 +++ 1 file changed, 3 insertions(+) diff --git a/htdocs/install/mysql/tables/llx_partnership.key.sql b/htdocs/install/mysql/tables/llx_partnership.key.sql index 7d09cdddda8..1a2cf6af5bb 100644 --- a/htdocs/install/mysql/tables/llx_partnership.key.sql +++ b/htdocs/install/mysql/tables/llx_partnership.key.sql @@ -19,3 +19,6 @@ ALTER TABLE llx_partnership ADD INDEX idx_partnership_entity (entity); ALTER TABLE llx_partnership ADD UNIQUE INDEX uk_partnership_ref (ref, entity); + +ALTER TABLE llx_partnership ADD UNIQUE INDEX uk_fk_type_fk_soc (fk_type, fk_soc, date_partnership_start); +ALTER TABLE llx_partnership ADD UNIQUE INDEX uk_fk_type_fk_member (fk_type, fk_member, date_partnership_start); From ed631b2b060742a1e894d93cc652f487cb7cde68 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Fri, 18 Mar 2022 16:14:20 +0100 Subject: [PATCH 13/24] Fix doxygen, missing error message --- htdocs/adherents/class/api_members.class.php | 2 +- .../class/api_memberstypes.class.php | 2 +- .../class/api_subscriptions.class.php | 4 +-- htdocs/api/class/api_documents.class.php | 6 ++-- .../comm/propal/class/api_proposals.class.php | 4 +-- htdocs/commande/class/api_orders.class.php | 8 ++--- .../facture/class/api_invoices.class.php | 14 ++++---- htdocs/don/class/api_donations.class.php | 2 +- .../class/api_expensereports.class.php | 2 +- .../class/api_supplier_invoices.class.php | 8 ++--- .../fourn/class/api_supplier_orders.class.php | 2 +- htdocs/product/class/api_products.class.php | 34 +++++++++---------- .../societe/class/api_thirdparties.class.php | 2 +- htdocs/ticket/class/api_tickets.class.php | 4 +-- htdocs/user/class/api_users.class.php | 2 +- 15 files changed, 48 insertions(+), 48 deletions(-) diff --git a/htdocs/adherents/class/api_members.class.php b/htdocs/adherents/class/api_members.class.php index 6fad266ea36..d30e851b9a2 100644 --- a/htdocs/adherents/class/api_members.class.php +++ b/htdocs/adherents/class/api_members.class.php @@ -358,7 +358,7 @@ class Members extends DolibarrApi if ($member->update(DolibarrApiAccess::$user) >= 0) { return $this->get($id); } else { - throw new RestException(500, $member->error); + throw new RestException(500, 'Error when updating member: '.$member->error); } } diff --git a/htdocs/adherents/class/api_memberstypes.class.php b/htdocs/adherents/class/api_memberstypes.class.php index be8f22f3c45..d95de755166 100644 --- a/htdocs/adherents/class/api_memberstypes.class.php +++ b/htdocs/adherents/class/api_memberstypes.class.php @@ -204,7 +204,7 @@ class MembersTypes extends DolibarrApi if ($membertype->update(DolibarrApiAccess::$user) >= 0) { return $this->get($id); } else { - throw new RestException(500, $membertype->error); + throw new RestException(500, 'Error when updating member type: '.$membertype->error); } } diff --git a/htdocs/adherents/class/api_subscriptions.class.php b/htdocs/adherents/class/api_subscriptions.class.php index 55b93df7663..f969017146b 100644 --- a/htdocs/adherents/class/api_subscriptions.class.php +++ b/htdocs/adherents/class/api_subscriptions.class.php @@ -159,7 +159,7 @@ class Subscriptions extends DolibarrApi $subscription->$field = $value; } if ($subscription->create(DolibarrApiAccess::$user) < 0) { - throw new RestException(500, 'Error when creating subscription', array_merge(array($subscription->error), $subscription->errors)); + throw new RestException(500, 'Error when creating contribution', array_merge(array($subscription->error), $subscription->errors)); } return $subscription->id; } @@ -193,7 +193,7 @@ class Subscriptions extends DolibarrApi if ($subscription->update(DolibarrApiAccess::$user) > 0) { return $this->get($id); } else { - throw new RestException(500, $subscription->error); + throw new RestException(500, 'Error when updating contribution: '.$subscription->error); } } diff --git a/htdocs/api/class/api_documents.class.php b/htdocs/api/class/api_documents.class.php index 28dbef6887e..da49e4cbba7 100644 --- a/htdocs/api/class/api_documents.class.php +++ b/htdocs/api/class/api_documents.class.php @@ -126,7 +126,7 @@ class Documents extends DolibarrApi * @param string $langcode Language code like 'en_US', 'fr_FR', 'es_ES', ... (If not set, use the default language). * @return array List of documents * - * @throws RestException 500 + * @throws RestException 500 System error * @throws RestException 501 * @throws RestException 400 * @throws RestException 401 @@ -249,7 +249,7 @@ class Documents extends DolibarrApi * @throws RestException 400 * @throws RestException 401 * @throws RestException 404 - * @throws RestException 500 + * @throws RestException 500 System error * * @url GET / */ @@ -546,7 +546,7 @@ class Documents extends DolibarrApi * @throws RestException 400 * @throws RestException 401 * @throws RestException 404 - * @throws RestException 500 + * @throws RestException 500 System error * * @url POST /upload */ diff --git a/htdocs/comm/propal/class/api_proposals.class.php b/htdocs/comm/propal/class/api_proposals.class.php index 41ed388e044..2cc17987224 100644 --- a/htdocs/comm/propal/class/api_proposals.class.php +++ b/htdocs/comm/propal/class/api_proposals.class.php @@ -533,7 +533,7 @@ class Proposals extends DolibarrApi * * @throws RestException 401 * @throws RestException 404 - * @throws RestException 500 + * @throws RestException 500 System error */ public function deleteContact($id, $contactid, $type) { @@ -707,7 +707,7 @@ class Proposals extends DolibarrApi * @throws RestException 304 * @throws RestException 401 * @throws RestException 404 - * @throws RestException 500 + * @throws RestException 500 System error * * @return array */ diff --git a/htdocs/commande/class/api_orders.class.php b/htdocs/commande/class/api_orders.class.php index 48865f958a2..fb912139d5f 100644 --- a/htdocs/commande/class/api_orders.class.php +++ b/htdocs/commande/class/api_orders.class.php @@ -574,7 +574,7 @@ class Orders extends DolibarrApi * * @throws RestException 401 * @throws RestException 404 - * @throws RestException 500 + * @throws RestException 500 System error */ public function deleteContact($id, $contactid, $type) { @@ -704,7 +704,7 @@ class Orders extends DolibarrApi * @throws RestException 304 * @throws RestException 401 * @throws RestException 404 - * @throws RestException 500 + * @throws RestException 500 System error * * @return array */ @@ -974,7 +974,7 @@ class Orders extends DolibarrApi * * @throws RestException 401 * @throws RestException 404 - * @throws RestException 500 + * @throws RestException 500 System error */ public function getOrderShipments($id) { @@ -1030,7 +1030,7 @@ class Orders extends DolibarrApi * * @throws RestException 401 * @throws RestException 404 - * @throws RestException 500 + * @throws RestException 500 System error */ public function createOrderShipment($id, $warehouse_id) { diff --git a/htdocs/compta/facture/class/api_invoices.class.php b/htdocs/compta/facture/class/api_invoices.class.php index 8282610d01d..61f0a9e8d15 100644 --- a/htdocs/compta/facture/class/api_invoices.class.php +++ b/htdocs/compta/facture/class/api_invoices.class.php @@ -507,7 +507,7 @@ class Invoices extends DolibarrApi * * @throws RestException 401 * @throws RestException 404 - * @throws RestException 500 + * @throws RestException 500 System error */ public function deleteContact($id, $contactid, $type) { @@ -649,7 +649,7 @@ class Invoices extends DolibarrApi $result = $this->invoice->delete(DolibarrApiAccess::$user); if ($result < 0) { - throw new RestException(500); + throw new RestException(500, 'Error when deleting invoice'); } return array( @@ -768,7 +768,7 @@ class Invoices extends DolibarrApi * @throws RestException 304 * @throws RestException 401 * @throws RestException 404 - * @throws RestException 500 + * @throws RestException 500 System error * */ public function addContact($id, $fk_socpeople, $type_contact, $source, $notrigger = 0) @@ -817,7 +817,7 @@ class Invoices extends DolibarrApi * @throws RestException 304 * @throws RestException 401 * @throws RestException 404 - * @throws RestException 500 + * @throws RestException 500 System error * */ public function settodraft($id, $idwarehouse = -1) @@ -920,7 +920,7 @@ class Invoices extends DolibarrApi * @throws RestException 304 * @throws RestException 401 * @throws RestException 404 - * @throws RestException 500 + * @throws RestException 500 System error */ public function settopaid($id, $close_code = '', $close_note = '') { @@ -970,7 +970,7 @@ class Invoices extends DolibarrApi * @throws RestException 304 * @throws RestException 401 * @throws RestException 404 - * @throws RestException 500 + * @throws RestException 500 System error */ public function settounpaid($id) { @@ -1057,7 +1057,7 @@ class Invoices extends DolibarrApi * @throws RestException 304 * @throws RestException 401 * @throws RestException 404 - * @throws RestException 500 + * @throws RestException 500 System error */ public function markAsCreditAvailable($id) { diff --git a/htdocs/don/class/api_donations.class.php b/htdocs/don/class/api_donations.class.php index 597f15b7f10..f9af0568bf0 100644 --- a/htdocs/don/class/api_donations.class.php +++ b/htdocs/don/class/api_donations.class.php @@ -293,7 +293,7 @@ class Donations extends DolibarrApi * @throws RestException 304 * @throws RestException 401 * @throws RestException 404 - * @throws RestException 500 + * @throws RestException 500 System error * * @return array */ diff --git a/htdocs/expensereport/class/api_expensereports.class.php b/htdocs/expensereport/class/api_expensereports.class.php index df942ff93e6..37319a3ec71 100644 --- a/htdocs/expensereport/class/api_expensereports.class.php +++ b/htdocs/expensereport/class/api_expensereports.class.php @@ -401,7 +401,7 @@ class ExpenseReports extends DolibarrApi * * @throws RestException 401 Not allowed * @throws RestException 404 Expense report not found - * @throws RestException 500 + * @throws RestException 500 System error */ public function put($id, $request_data = null) { diff --git a/htdocs/fourn/class/api_supplier_invoices.class.php b/htdocs/fourn/class/api_supplier_invoices.class.php index 06c836b4d77..35e783729ff 100644 --- a/htdocs/fourn/class/api_supplier_invoices.class.php +++ b/htdocs/fourn/class/api_supplier_invoices.class.php @@ -211,7 +211,7 @@ class SupplierInvoices extends DolibarrApi * @return int ID of supplier invoice * * @throws RestException 401 - * @throws RestException 500 + * @throws RestException 500 System error */ public function post($request_data = null) { @@ -283,7 +283,7 @@ class SupplierInvoices extends DolibarrApi * * @throws RestException 401 * @throws RestException 404 - * @throws RestException 500 + * @throws RestException 500 System error */ public function delete($id) { @@ -300,7 +300,7 @@ class SupplierInvoices extends DolibarrApi } if ($this->invoice->delete(DolibarrApiAccess::$user) < 0) { - throw new RestException(500); + throw new RestException(500, 'Error when deleting invoice'); } return array( @@ -326,7 +326,7 @@ class SupplierInvoices extends DolibarrApi * @throws RestException 401 * @throws RestException 404 * @throws RestException 405 - * @throws RestException 500 + * @throws RestException 500 System error */ public function validate($id, $idwarehouse = 0, $notrigger = 0) { diff --git a/htdocs/fourn/class/api_supplier_orders.class.php b/htdocs/fourn/class/api_supplier_orders.class.php index e21eb3436dc..d55a398dbd6 100644 --- a/htdocs/fourn/class/api_supplier_orders.class.php +++ b/htdocs/fourn/class/api_supplier_orders.class.php @@ -309,7 +309,7 @@ class SupplierOrders extends DolibarrApi } if ($this->order->delete(DolibarrApiAccess::$user) < 0) { - throw new RestException(500); + throw new RestException(500, 'Error when deleting order'); } return array( diff --git a/htdocs/product/class/api_products.class.php b/htdocs/product/class/api_products.class.php index ec735da3865..0f21b2aac3c 100644 --- a/htdocs/product/class/api_products.class.php +++ b/htdocs/product/class/api_products.class.php @@ -742,7 +742,7 @@ class Products extends DolibarrApi * @param int $fk_barcode_type Barcode type * @return int * - * @throws RestException 500 + * @throws RestException 500 System error * @throws RestException 401 * * @url POST {id}/purchase_prices @@ -1158,7 +1158,7 @@ class Products extends DolibarrApi * @param string $ref_ext External reference of Attribute * @return array * - * @throws RestException 500 + * @throws RestException 500 System error * @throws RestException 401 * * @url GET attributes/ref_ext/{ref_ext} @@ -1206,7 +1206,7 @@ class Products extends DolibarrApi * @param string $ref_ext Reference of Attribute * @return int * - * @throws RestException 500 + * @throws RestException 500 System error * @throws RestException 401 * * @url POST attributes @@ -1283,7 +1283,7 @@ class Products extends DolibarrApi * @param int $id ID of Attribute * @return int Result of deletion * - * @throws RestException 500 + * @throws RestException 500 System error * @throws RestException 401 * * @url DELETE attributes/{id} @@ -1311,7 +1311,7 @@ class Products extends DolibarrApi * @param int $id ID of Attribute value * @return array * - * @throws RestException 500 + * @throws RestException 500 System error * @throws RestException 401 * * @url GET attributes/values/{id} @@ -1352,7 +1352,7 @@ class Products extends DolibarrApi * @param string $ref Ref of Attribute value * @return array * - * @throws RestException 500 + * @throws RestException 500 System error * @throws RestException 401 * * @url GET attributes/{id}/values/ref/{ref} @@ -1439,7 +1439,7 @@ class Products extends DolibarrApi * @return array * * @throws RestException 401 - * @throws RestException 500 + * @throws RestException 500 System error * * @url GET attributes/{id}/values */ @@ -1511,7 +1511,7 @@ class Products extends DolibarrApi * @param string $value Value of Attribute value * @return int * - * @throws RestException 500 + * @throws RestException 500 System error * @throws RestException 401 * * @url POST attributes/{id}/values @@ -1545,7 +1545,7 @@ class Products extends DolibarrApi * @return array * * @throws RestException 401 - * @throws RestException 500 + * @throws RestException 500 System error * * @url PUT attributes/values/{id} */ @@ -1590,7 +1590,7 @@ class Products extends DolibarrApi * @param int $id ID of Attribute value * @return int * - * @throws RestException 500 + * @throws RestException 500 System error * @throws RestException 401 * * @url DELETE attributes/values/{id} @@ -1617,7 +1617,7 @@ class Products extends DolibarrApi * @param int $includestock Default value 0. If parameter is set to 1 the response will contain stock data of each variant * @return array * - * @throws RestException 500 + * @throws RestException 500 System error * @throws RestException 401 * * @url GET {id}/variants @@ -1653,7 +1653,7 @@ class Products extends DolibarrApi * @param string $ref Ref of Product * @return array * - * @throws RestException 500 + * @throws RestException 500 System error * @throws RestException 401 * * @url GET ref/{ref}/variants @@ -1695,7 +1695,7 @@ class Products extends DolibarrApi * @param string $ref_ext External reference of variant * @return int * - * @throws RestException 500 + * @throws RestException 500 System error * @throws RestException 401 * @throws RestException 404 * @@ -1752,7 +1752,7 @@ class Products extends DolibarrApi * @param array $features List of attributes pairs id_attribute->id_value. Example: array(id_color=>id_Blue, id_size=>id_small, id_option=>id_val_a, ...) * @return int * - * @throws RestException 500 + * @throws RestException 500 System error * @throws RestException 401 * @throws RestException 404 * @@ -1807,7 +1807,7 @@ class Products extends DolibarrApi * @param array $request_data Datas * @return int * - * @throws RestException 500 + * @throws RestException 500 System error * @throws RestException 401 * * @url PUT variants/{id} @@ -1841,7 +1841,7 @@ class Products extends DolibarrApi * @param int $id ID of Variant * @return int Result of deletion * - * @throws RestException 500 + * @throws RestException 500 System error * @throws RestException 401 * * @url DELETE variants/{id} @@ -1869,7 +1869,7 @@ class Products extends DolibarrApi * @param int $selected_warehouse_id ID of warehouse * @return int * - * @throws RestException 500 + * @throws RestException 500 System error * @throws RestException 401 * @throws RestException 404 * diff --git a/htdocs/societe/class/api_thirdparties.class.php b/htdocs/societe/class/api_thirdparties.class.php index 47e893846e6..962bbf021b8 100644 --- a/htdocs/societe/class/api_thirdparties.class.php +++ b/htdocs/societe/class/api_thirdparties.class.php @@ -1409,7 +1409,7 @@ class Thirdparties extends DolibarrApi if ($result > 0) { return array("success" => $result); } else { - throw new RestException(500); + throw new RestException(500, 'Error generating the document '.$this->error); } } diff --git a/htdocs/ticket/class/api_tickets.class.php b/htdocs/ticket/class/api_tickets.class.php index 75c2db21224..3151f75877b 100644 --- a/htdocs/ticket/class/api_tickets.class.php +++ b/htdocs/ticket/class/api_tickets.class.php @@ -377,7 +377,7 @@ class Tickets extends DolibarrApi } $this->ticket->message = $ticketMessageText; if (!$this->ticket->createTicketMessage(DolibarrApiAccess::$user)) { - throw new RestException(500); + throw new RestException(500, 'Error when creating ticket'); } return $this->ticket->id; } @@ -438,7 +438,7 @@ class Tickets extends DolibarrApi } if (!$this->ticket->delete($id)) { - throw new RestException(500); + throw new RestException(500, 'Error when deleting ticket'); } return array( diff --git a/htdocs/user/class/api_users.class.php b/htdocs/user/class/api_users.class.php index 68aa2b650f7..66d842c3564 100644 --- a/htdocs/user/class/api_users.class.php +++ b/htdocs/user/class/api_users.class.php @@ -466,7 +466,7 @@ class Users extends DolibarrApi * * @throws RestException 401 Not allowed * @throws RestException 404 User not found - * @throws RestException 500 Error + * @throws RestException 500 System error * * @url GET {id}/setGroup/{group} */ From ef21def5cec3bf370dda7eddb67e622ee8a44936 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Fri, 18 Mar 2022 14:09:53 +0100 Subject: [PATCH 14/24] NEW Add option MAIN_API_DEBUG to save API logs into a file --- htdocs/api/index.php | 19 +++++++++++++++++++ .../install/mysql/migration/15.0.0-16.0.0.sql | 4 ++++ 2 files changed, 23 insertions(+) diff --git a/htdocs/api/index.php b/htdocs/api/index.php index d5bc7e273e2..c66573e8022 100644 --- a/htdocs/api/index.php +++ b/htdocs/api/index.php @@ -158,6 +158,25 @@ if (!empty($reg[1]) && $reg[1] == 'explorer' && ($reg[2] == '/swagger.json' || $ $api = new DolibarrApi($db, '', $refreshcache); //var_dump($api->r->apiVersionMap); +// If MAIN_API_DEBUG is set to 1, we save logs into file "dolibarr_api.log" +if (!empty($conf->global->MAIN_API_DEBUG)) { + $r = $api->r; + $r->onCall(function () use ($r) { + // Don't log Luracast Restler Explorer recources calls + //if (!preg_match('/^explorer/', $r->url)) { + // 'method' => $api->r->requestMethod, + // 'url' => $api->r->url, + // 'route' => $api->r->apiMethodInfo->className.'::'.$api->r->apiMethodInfo->methodName, + // 'version' => $api->r->getRequestedApiVersion(), + // 'data' => $api->r->getRequestData(), + //dol_syslog("Debug API input ".var_export($r, true), LOG_DEBUG, 0, '_api'); + dol_syslog("Debug API url ".var_export($r->url, true), LOG_DEBUG, 0, '_api'); + dol_syslog("Debug API input ".var_export($r->getRequestData(), true), LOG_DEBUG, 0, '_api'); + //} + }); +} + + // Enable the Restler API Explorer. // See https://github.com/Luracast/Restler-API-Explorer for more info. $api->r->addAPIClass('Luracast\\Restler\\Explorer'); diff --git a/htdocs/install/mysql/migration/15.0.0-16.0.0.sql b/htdocs/install/mysql/migration/15.0.0-16.0.0.sql index 5e9a0250a38..2933acd00d7 100644 --- a/htdocs/install/mysql/migration/15.0.0-16.0.0.sql +++ b/htdocs/install/mysql/migration/15.0.0-16.0.0.sql @@ -91,6 +91,10 @@ INSERT INTO llx_c_forme_juridique (fk_pays, code, libelle, active) VALUES (154, INSERT INTO llx_c_forme_juridique (fk_pays, code, libelle, active) VALUES (154, '15419', '626 - Régimen Simplificado de Confianza', 1); +ALTER TABLE llx_partnership ADD UNIQUE INDEX uk_fk_type_fk_soc (fk_type, fk_soc, date_partnership_start); +ALTER TABLE llx_partnership ADD UNIQUE INDEX uk_fk_type_fk_member (fk_type, fk_member, date_partnership_start); + + -- v16 ALTER TABLE llx_projet_task_time ADD COLUMN fk_product integer NULL; From 4c293aa49b47f4ddb1169e57cce2299adf2c1410 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sat, 19 Mar 2022 13:06:30 +0100 Subject: [PATCH 15/24] Update doc --- htdocs/core/db/mysqli.class.php | 2 +- htdocs/modulebuilder/template/myobject_list.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/htdocs/core/db/mysqli.class.php b/htdocs/core/db/mysqli.class.php index fa24e4a70ac..520fba66ce1 100644 --- a/htdocs/core/db/mysqli.class.php +++ b/htdocs/core/db/mysqli.class.php @@ -266,7 +266,7 @@ class DoliDBMysqli extends DoliDB * @param int $usesavepoint 0=Default mode, 1=Run a savepoint before and a rollback to savepoint if error (this allow to have some request with errors inside global transactions). * Note that with Mysql, this parameter is not used as Myssql can already commit a transaction even if one request is in error, without using savepoints. * @param string $type Type of SQL order ('ddl' for insert, update, select, delete or 'dml' for create, alter...) - * @param int $result_mode Result mode + * @param int $result_mode Result mode (Using 1=MYSQLI_USE_RESULT instead of 0=MYSQLI_STORE_RESULT will not buffer the result and save memory) * @return bool|mysqli_result Resultset of answer */ public function query($query, $usesavepoint = 0, $type = 'auto', $result_mode = 0) diff --git a/htdocs/modulebuilder/template/myobject_list.php b/htdocs/modulebuilder/template/myobject_list.php index 589f4052724..67186fcf102 100644 --- a/htdocs/modulebuilder/template/myobject_list.php +++ b/htdocs/modulebuilder/template/myobject_list.php @@ -372,7 +372,7 @@ if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST)) { $nbtotalofrecords++; }*/ /* The fast and low memory method to get and count full list converts the sql into a sql count */ - $sqlforcount = preg_replace('/^SELECT[a-z0-9\._\s\(\),]+FROM/i', 'SELECT COUNT(*) as nbtotalofrecords FROM', $sql); + $sqlforcount = preg_replace('/^SELECT[a-zA-Z0-9\._\s\(\),]+FROM/', 'SELECT COUNT(*) as nbtotalofrecords FROM', $sql); $resql = $db->query($sqlforcount); $objforcount = $db->fetch_object($resql); $nbtotalofrecords = $objforcount->nbtotalofrecords; From 65ed94f9d0650e385414c0c393ae536a8c2f1ff1 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sat, 19 Mar 2022 13:14:34 +0100 Subject: [PATCH 16/24] Fix sql is executed twice --- htdocs/contrat/list.php | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/htdocs/contrat/list.php b/htdocs/contrat/list.php index e6446e3585b..036331eecd0 100644 --- a/htdocs/contrat/list.php +++ b/htdocs/contrat/list.php @@ -352,12 +352,6 @@ if ($search_dfyear > 0 && $search_op2df) { } $sql .= $db->order($sortfield, $sortorder); -$totalnboflines = 0; -$result = $db->query($sql); -if ($result) { - $totalnboflines = $db->num_rows($result); -} - $nbtotalofrecords = ''; if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST)) { $result = $db->query($sql); @@ -508,7 +502,7 @@ print ''; print ''; print ''; -print_barre_liste($langs->trans("ListOfContracts"), $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $totalnboflines, 'contract', 0, $newcardbutton, '', $limit, 0, 0, 1); +print_barre_liste($langs->trans("ListOfContracts"), $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'contract', 0, $newcardbutton, '', $limit, 0, 0, 1); $topicmail = "SendContractRef"; $modelmail = "contract"; From 6a7309305b49e856f25782906c19a6640051cdb3 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sat, 19 Mar 2022 14:41:10 +0100 Subject: [PATCH 17/24] Fix out of memory with a lot of contracts --- htdocs/contrat/list.php | 42 +++++++++++++------ .../modulebuilder/template/myobject_list.php | 10 +++-- 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/htdocs/contrat/list.php b/htdocs/contrat/list.php index e6446e3585b..0c8eefdef44 100644 --- a/htdocs/contrat/list.php +++ b/htdocs/contrat/list.php @@ -350,25 +350,43 @@ if ($search_dfyear > 0 && $search_op2df) { $sql .= " HAVING MIN(".$db->ifsql("cd.statut=4", "cd.date_fin_validite", "null").") <= '".$db->idate(dol_get_last_day($search_dfyear, $search_dfmonth, false))."' AND MIN(".$db->ifsql("cd.statut=4", "cd.date_fin_validite", "null").") >= '".$db->idate(dol_get_first_day($search_dfyear, $search_dfmonth, false))."'"; } } -$sql .= $db->order($sortfield, $sortorder); -$totalnboflines = 0; -$result = $db->query($sql); -if ($result) { - $totalnboflines = $db->num_rows($result); -} - -$nbtotalofrecords = ''; +$nbtotalofrecords = 0; if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST)) { - $result = $db->query($sql); - $nbtotalofrecords = $db->num_rows($result); + //$result = $db->query($sql); + //$nbtotalofrecords = $db->num_rows($result); + + if ($search_dfyear > 0 && $search_op2df) { + $resql = $db->query($sql, 0, 'auto', 1); + while ($db->fetch_object($resql)) { + if (empty($nbtotalofrecords)) { + $nbtotalofrecords = 1; // We can't make +1 because init value is '' + } else { + $nbtotalofrecords++; + } + } + } else { + $sqlforcount = preg_replace('/^SELECT[a-zA-Z0-9\._\s\(\),=<>\:\-\']+\sFROM/', 'SELECT COUNT(*) as nbtotalofrecords FROM', $sql); + $sqlforcount = preg_replace('/LEFT JOIN '.MAIN_DB_PREFIX.'contratdet as cd ON c.rowid = cd.fk_contrat/', '', $sqlforcount); + $sqlforcount = preg_replace('/LEFT JOIN '.MAIN_DB_PREFIX.'categorie_product as cp ON cp.fk_product=cd.fk_product/', '', $sqlforcount); + $sqlforcount = preg_replace('/AND cp.fk_categorie = '.((int) $search_product_category).'/', '', $sqlforcount); + $sqlforcount = preg_replace('/GROUP BY.*$/', '', $sqlforcount); + + $resql = $db->query($sqlforcount); + $objforcount = $db->fetch_object($resql); + $nbtotalofrecords = $objforcount->nbtotalofrecords; + } + if (($page * $limit) > $nbtotalofrecords) { // if total resultset is smaller then paging size (filtering), goto and load page 0 $page = 0; $offset = 0; } } -$sql .= $db->plimit($limit + 1, $offset); +$sql .= $db->order($sortfield, $sortorder); +if ($limit) { + $sql .= $db->plimit($limit + 1, $offset); +} $resql = $db->query($sql); if (!$resql) { @@ -508,7 +526,7 @@ print ''; print ''; print ''; -print_barre_liste($langs->trans("ListOfContracts"), $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $totalnboflines, 'contract', 0, $newcardbutton, '', $limit, 0, 0, 1); +print_barre_liste($langs->trans("ListOfContracts"), $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'contract', 0, $newcardbutton, '', $limit, 0, 0, 1); $topicmail = "SendContractRef"; $modelmail = "contract"; diff --git a/htdocs/modulebuilder/template/myobject_list.php b/htdocs/modulebuilder/template/myobject_list.php index 67186fcf102..902969ee430 100644 --- a/htdocs/modulebuilder/template/myobject_list.php +++ b/htdocs/modulebuilder/template/myobject_list.php @@ -369,10 +369,14 @@ if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST)) { /* The slow method does not consume memory on mysql (not tested on pgsql) */ /*$resql = $db->query($sql, 0, 'auto', 1); while ($db->fetch_object($resql)) { - $nbtotalofrecords++; - }*/ + if (empty($nbtotalofrecords)) { + $nbtotalofrecords = 1; // We can't make +1 because init value is '' + } else { + $nbtotalofrecords++; + } + }*/ /* The fast and low memory method to get and count full list converts the sql into a sql count */ - $sqlforcount = preg_replace('/^SELECT[a-zA-Z0-9\._\s\(\),]+FROM/', 'SELECT COUNT(*) as nbtotalofrecords FROM', $sql); + $sqlforcount = preg_replace('/^SELECT[a-zA-Z0-9\._\s\(\),=<>\:\-\']+\sFROM/', 'SELECT COUNT(*) as nbtotalofrecords FROM', $sql); $resql = $db->query($sqlforcount); $objforcount = $db->fetch_object($resql); $nbtotalofrecords = $objforcount->nbtotalofrecords; From e48d29e4f23fae90e5685bd69ab1355490b267c3 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sat, 19 Mar 2022 14:48:22 +0100 Subject: [PATCH 18/24] Fix bad position of order --- htdocs/contrat/list.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/htdocs/contrat/list.php b/htdocs/contrat/list.php index 036331eecd0..8b00ec2a857 100644 --- a/htdocs/contrat/list.php +++ b/htdocs/contrat/list.php @@ -350,19 +350,22 @@ if ($search_dfyear > 0 && $search_op2df) { $sql .= " HAVING MIN(".$db->ifsql("cd.statut=4", "cd.date_fin_validite", "null").") <= '".$db->idate(dol_get_last_day($search_dfyear, $search_dfmonth, false))."' AND MIN(".$db->ifsql("cd.statut=4", "cd.date_fin_validite", "null").") >= '".$db->idate(dol_get_first_day($search_dfyear, $search_dfmonth, false))."'"; } } -$sql .= $db->order($sortfield, $sortorder); $nbtotalofrecords = ''; if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST)) { $result = $db->query($sql); $nbtotalofrecords = $db->num_rows($result); + if (($page * $limit) > $nbtotalofrecords) { // if total resultset is smaller then paging size (filtering), goto and load page 0 $page = 0; $offset = 0; } } -$sql .= $db->plimit($limit + 1, $offset); +$sql .= $db->order($sortfield, $sortorder); +if ($limit) { + $sql .= $db->plimit($limit + 1, $offset); +} $resql = $db->query($sql); if (!$resql) { From 1bb1475b54b383d75660d17f9c71a65675366e00 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sat, 19 Mar 2022 15:03:32 +0100 Subject: [PATCH 19/24] Fix phpdoc --- htdocs/core/menus/standard/eldy.lib.php | 218 ++++++++++++------------ 1 file changed, 109 insertions(+), 109 deletions(-) diff --git a/htdocs/core/menus/standard/eldy.lib.php b/htdocs/core/menus/standard/eldy.lib.php index efe8d87ff86..6edc4cb2c3c 100644 --- a/htdocs/core/menus/standard/eldy.lib.php +++ b/htdocs/core/menus/standard/eldy.lib.php @@ -156,8 +156,8 @@ function print_eldy_menu($db, $atarget, $type_user, &$tabMenu, &$menu, $noout = 'name' => 'Products', 'link' => '/product/index.php?mainmenu=products&leftmenu=', 'title' => (!empty($conf->product->enabled) && !empty($conf->service->enabled)) - ? (array("TMenuProducts", " | ", "TMenuServices")) - : (!empty($conf->product->enabled) ? "TMenuProducts" : "TMenuServices"), + ? (array("TMenuProducts", " | ", "TMenuServices")) + : (!empty($conf->product->enabled) ? "TMenuProducts" : "TMenuServices"), 'level' => 0, 'enabled' => $showmode = isVisibleToUserType($type_user, $tmpentry, $listofmodulesforexternal), 'target' => $atarget, @@ -232,7 +232,7 @@ function print_eldy_menu($db, $atarget, $type_user, &$tabMenu, &$menu, $noout = || !empty($conf->supplier_order->enabled) || !empty($conf->contrat->enabled) || !empty($conf->ficheinter->enabled) - ) ? 1 : 0, + ) ? 1 : 0, 'perms'=>(!empty($user->rights->propal->lire) || !empty($user->rights->commande->lire) || !empty($user->rights->supplier_proposal->lire) @@ -241,17 +241,17 @@ function print_eldy_menu($db, $atarget, $type_user, &$tabMenu, &$menu, $noout = || !empty($user->rights->supplier_order->lire) || !empty($user->rights->contrat->lire) || !empty($user->rights->ficheinter->lire) - ), + ), 'module'=>'propal|commande|supplier_proposal|supplier_order|contrat|ficheinter' ); $onlysupplierorder = !empty($user->rights->fournisseur->commande->lire) && - empty($user->rights->propal->lire) && - empty($user->rights->commande->lire) && - empty($user->rights->supplier_order->lire) && - empty($user->rights->supplier_proposal->lire) && - empty($user->rights->contrat->lire) && - empty($user->rights->ficheinter->lire); + empty($user->rights->propal->lire) && + empty($user->rights->commande->lire) && + empty($user->rights->supplier_order->lire) && + empty($user->rights->supplier_proposal->lire) && + empty($user->rights->contrat->lire) && + empty($user->rights->ficheinter->lire); $menu_arr[] = array( 'name' => 'Commercial', @@ -524,7 +524,7 @@ function print_eldy_menu($db, $atarget, $type_user, &$tabMenu, &$menu, $noout = $idsel, $classname, $newTabMenu[$i]['prefix'] - ); + ); } // Sort on position @@ -544,9 +544,9 @@ function print_eldy_menu($db, $atarget, $type_user, &$tabMenu, &$menu, $noout = if (!empty($mysoc->logo_squarred_mini) && is_readable($conf->mycompany->dir_output.'/logos/thumbs/'.$mysoc->logo_squarred_mini)) { $urllogo = DOL_URL_ROOT.'/viewimage.php?cache=1&modulepart=mycompany&file='.urlencode('logos/thumbs/'.$mysoc->logo_squarred_mini); /*} elseif (! empty($mysoc->logo_mini) && is_readable($conf->mycompany->dir_output.'/logos/thumbs/'.$mysoc->logo_mini)) - { - $urllogo=DOL_URL_ROOT.'/viewimage.php?cache=1&modulepart=mycompany&file='.urlencode('logos/thumbs/'.$mysoc->logo_mini); - }*/ + { + $urllogo=DOL_URL_ROOT.'/viewimage.php?cache=1&modulepart=mycompany&file='.urlencode('logos/thumbs/'.$mysoc->logo_mini); + }*/ } else { $urllogo = DOL_URL_ROOT.'/theme/dolibarr_512x512_white.png'; $logoContainerAdditionalClass = ''; @@ -798,7 +798,7 @@ function print_left_eldy_menu($db, $menu_array_before, $menu_array_after, &$tabM /* * Menu HRM - */ + */ if ($mainmenu == 'hrm') { get_left_menu_hrm($mainmenu, $newmenu, $usemenuhider, $leftmenu, $type_user); } @@ -881,13 +881,13 @@ function print_left_eldy_menu($db, $menu_array_before, $menu_array_after, &$tabM // TODO Use the position property in menu_array to reorder the $menu_array //var_dump($menu_array); /*$new_menu_array = array(); - $level=0; $cusor=0; $position=0; - $nbentry = count($menu_array); - while (findNextEntryForLevel($menu_array, $cursor, $position, $level)) - { + $level=0; $cusor=0; $position=0; + $nbentry = count($menu_array); + while (findNextEntryForLevel($menu_array, $cursor, $position, $level)) + { - $cursor++; - }*/ + $cursor++; + }*/ // Show menu $invert = empty($conf->global->MAIN_MENU_INVERT) ? "" : "invert"; @@ -1046,11 +1046,11 @@ function print_left_eldy_menu($db, $menu_array_before, $menu_array_after, &$tabM /** * Get left Menu HOME * - * @param string $mainmenu - * @param Menu &$newmenu Object Menu to return back list of menu entries - * @param string $usemenuhider - * @param string $leftmenu - * @param int $type_user + * @param string $mainmenu Main menu + * @param Menu $newmenu Object Menu to return back list of menu entries + * @param string $usemenuhider Use menu hider + * @param string $leftmenu Left menu + * @param int $type_user Type of user * @return void */ function get_left_menu_home($mainmenu, &$newmenu, $usemenuhider = 1, $leftmenu = 'none', $type_user = 0) @@ -1165,11 +1165,11 @@ function get_left_menu_home($mainmenu, &$newmenu, $usemenuhider = 1, $leftmenu = /** * Get left Menu THIRDPARTIES * - * @param string $mainmenu - * @param Menu &$newmenu Object Menu to return back list of menu entries - * @param string $usemenuhider - * @param string $leftmenu - * @param int $type_user + * @param string $mainmenu Main menu + * @param Menu $newmenu Object Menu to return back list of menu entries + * @param string $usemenuhider Use menu hider + * @param string $leftmenu Left menu + * @param int $type_user Type of targeted user for menu * @return void */ function get_left_menu_thridparties($mainmenu, &$newmenu, $usemenuhider = 1, $leftmenu = 'none', $type_user = 0) @@ -1197,12 +1197,12 @@ function get_left_menu_thridparties($mainmenu, &$newmenu, $usemenuhider = 1, $le $langs->load("commercial"); $newmenu->add("/societe/list.php?type=p&leftmenu=prospects", $langs->trans("ListProspectsShort"), 2, $user->rights->societe->lire, '', $mainmenu, 'prospects'); /* no more required, there is a filter that can do more - if ($usemenuhider || empty($leftmenu) || $leftmenu=="prospects") $newmenu->add("/societe/list.php?type=p&sortfield=s.datec&sortorder=desc&begin=&search_stcomm=-1", $langs->trans("LastProspectDoNotContact"), 2, $user->rights->societe->lire); - if ($usemenuhider || empty($leftmenu) || $leftmenu=="prospects") $newmenu->add("/societe/list.php?type=p&sortfield=s.datec&sortorder=desc&begin=&search_stcomm=0", $langs->trans("LastProspectNeverContacted"), 2, $user->rights->societe->lire); - if ($usemenuhider || empty($leftmenu) || $leftmenu=="prospects") $newmenu->add("/societe/list.php?type=p&sortfield=s.datec&sortorder=desc&begin=&search_stcomm=1", $langs->trans("LastProspectToContact"), 2, $user->rights->societe->lire); - if ($usemenuhider || empty($leftmenu) || $leftmenu=="prospects") $newmenu->add("/societe/list.php?type=p&sortfield=s.datec&sortorder=desc&begin=&search_stcomm=2", $langs->trans("LastProspectContactInProcess"), 2, $user->rights->societe->lire); - if ($usemenuhider || empty($leftmenu) || $leftmenu=="prospects") $newmenu->add("/societe/list.php?type=p&sortfield=s.datec&sortorder=desc&begin=&search_stcomm=3", $langs->trans("LastProspectContactDone"), 2, $user->rights->societe->lire); - */ + if ($usemenuhider || empty($leftmenu) || $leftmenu=="prospects") $newmenu->add("/societe/list.php?type=p&sortfield=s.datec&sortorder=desc&begin=&search_stcomm=-1", $langs->trans("LastProspectDoNotContact"), 2, $user->rights->societe->lire); + if ($usemenuhider || empty($leftmenu) || $leftmenu=="prospects") $newmenu->add("/societe/list.php?type=p&sortfield=s.datec&sortorder=desc&begin=&search_stcomm=0", $langs->trans("LastProspectNeverContacted"), 2, $user->rights->societe->lire); + if ($usemenuhider || empty($leftmenu) || $leftmenu=="prospects") $newmenu->add("/societe/list.php?type=p&sortfield=s.datec&sortorder=desc&begin=&search_stcomm=1", $langs->trans("LastProspectToContact"), 2, $user->rights->societe->lire); + if ($usemenuhider || empty($leftmenu) || $leftmenu=="prospects") $newmenu->add("/societe/list.php?type=p&sortfield=s.datec&sortorder=desc&begin=&search_stcomm=2", $langs->trans("LastProspectContactInProcess"), 2, $user->rights->societe->lire); + if ($usemenuhider || empty($leftmenu) || $leftmenu=="prospects") $newmenu->add("/societe/list.php?type=p&sortfield=s.datec&sortorder=desc&begin=&search_stcomm=3", $langs->trans("LastProspectContactDone"), 2, $user->rights->societe->lire); + */ $newmenu->add("/societe/card.php?leftmenu=prospects&action=create&type=p", $langs->trans("MenuNewProspect"), 3, $user->rights->societe->creer); } @@ -1270,11 +1270,11 @@ function get_left_menu_thridparties($mainmenu, &$newmenu, $usemenuhider = 1, $le /** * Get left Menu COMMERCIAL (propal, commande, supplier_proposal, supplier_order, contrat, ficheinter) * - * @param string $mainmenu - * @param Menu &$newmenu Object Menu to return back list of menu entries - * @param string $usemenuhider - * @param string $leftmenu - * @param int $type_user + * @param string $mainmenu Main menu + * @param Menu $newmenu Object Menu to return back list of menu entries + * @param string $usemenuhider Use menu hider + * @param string $leftmenu Left menu + * @param int $type_user Type of targeted user for menu * @return void */ function get_left_menu_commercial($mainmenu, &$newmenu, $usemenuhider = 1, $leftmenu = 'none', $type_user = 0) @@ -1386,11 +1386,11 @@ function get_left_menu_commercial($mainmenu, &$newmenu, $usemenuhider = 1, $left /** * Get left COMPTA-FINANCIAL * - * @param string $mainmenu - * @param Menu &$newmenu Object Menu to return back list of menu entries - * @param string $usemenuhider - * @param string $leftmenu - * @param int $type_user + * @param string $mainmenu Main menu + * @param Menu $newmenu Object Menu to return back list of menu entries + * @param string $usemenuhider Use menu hider + * @param string $leftmenu Left menu + * @param int $type_user Type of targeted user for menu * @return void */ function get_left_menu_billing($mainmenu, &$newmenu, $usemenuhider = 1, $leftmenu = 'none', $type_user = 0) @@ -1567,17 +1567,17 @@ function get_left_menu_billing($mainmenu, &$newmenu, $usemenuhider = 1, $leftmen /** * Get left COMPTA-FINANCIAL (accountancy) * - * @param string $mainmenu - * @param Menu &$newmenu Object Menu to return back list of menu entries - * @param string $usemenuhider - * @param string $leftmenu - * @param int $type_user - * @param DB $db + * @param string $mainmenu Main menu + * @param Menu $newmenu Object Menu to return back list of menu entries + * @param string $usemenuhider Use menu hider + * @param string $leftmenu Left menu + * @param int $type_user Type of targeted user for menu * @return void */ -function get_left_menu_accountancy($mainmenu, &$newmenu, $usemenuhider = 1, $leftmenu = 'none', $type_user = 0, $db) +function get_left_menu_accountancy($mainmenu, &$newmenu, $usemenuhider = 1, $leftmenu = 'none', $type_user = 0) { global $user, $conf, $langs; + global $db; if ($mainmenu == 'accountancy') { $langs->load("companies"); @@ -1679,7 +1679,7 @@ function get_left_menu_accountancy($mainmenu, &$newmenu, $usemenuhider = 1, $lef if ($objp->nature == 3 && ((!empty($conf->fournisseur->enabled) && empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD)) || !empty($conf->supplier_invoice->enabled)) && empty($conf->global->ACCOUNTING_DISABLE_BINDING_ON_PURCHASES)) { - $nature = "purchases"; + $nature = "purchases"; } if ($objp->nature == 4 && !empty($conf->banque->enabled)) { $nature = "bank"; @@ -1697,7 +1697,7 @@ function get_left_menu_accountancy($mainmenu, &$newmenu, $usemenuhider = 1, $lef $nature = "hasnew"; } - // To enable when page exists + // To enable when page exists if (empty($conf->global->ACCOUNTANCY_SHOW_DEVELOP_JOURNAL)) { if ($nature == 'hasnew' || $nature == 'inventory') { $nature = ''; @@ -1709,7 +1709,7 @@ function get_left_menu_accountancy($mainmenu, &$newmenu, $usemenuhider = 1, $lef $journallabel = $langs->transnoentities($objp->label); // Labels in this table are set by loading llx_accounting_abc.sql. Label can be 'ACCOUNTING_SELL_JOURNAL', 'InventoryJournal', ... $newmenu->add('/accountancy/journal/'.$nature.'journal.php?mainmenu=accountancy&leftmenu=accountancy_journal&id_journal='.$objp->rowid, $journallabel, 2, $user->rights->accounting->comptarapport->lire); } - $i++; + $i++; } } else { // Should not happend. Entries are added @@ -1815,17 +1815,17 @@ function get_left_menu_accountancy($mainmenu, &$newmenu, $usemenuhider = 1, $lef $newmenu->add("/compta/resultat/index.php?leftmenu=report", $langs->trans("MenuReportInOut"), 1, $user->rights->compta->resultat->lire); $newmenu->add("/compta/resultat/clientfourn.php?leftmenu=report", $langs->trans("ByCompanies"), 2, $user->rights->compta->resultat->lire); /* On verra ca avec module compabilite expert - $newmenu->add("/compta/resultat/compteres.php?leftmenu=report","Compte de resultat",2,$user->rights->compta->resultat->lire); - $newmenu->add("/compta/resultat/bilan.php?leftmenu=report","Bilan",2,$user->rights->compta->resultat->lire); - */ + $newmenu->add("/compta/resultat/compteres.php?leftmenu=report","Compte de resultat",2,$user->rights->compta->resultat->lire); + $newmenu->add("/compta/resultat/bilan.php?leftmenu=report","Bilan",2,$user->rights->compta->resultat->lire); + */ /* - $newmenu->add("/compta/stats/cumul.php?leftmenu=report","Cumule",2,$user->rights->compta->resultat->lire); - if (! empty($conf->propal->enabled)) { - $newmenu->add("/compta/stats/prev.php?leftmenu=report","Previsionnel",2,$user->rights->compta->resultat->lire); - $newmenu->add("/compta/stats/comp.php?leftmenu=report","Transforme",2,$user->rights->compta->resultat->lire); - } - */ + $newmenu->add("/compta/stats/cumul.php?leftmenu=report","Cumule",2,$user->rights->compta->resultat->lire); + if (! empty($conf->propal->enabled)) { + $newmenu->add("/compta/stats/prev.php?leftmenu=report","Previsionnel",2,$user->rights->compta->resultat->lire); + $newmenu->add("/compta/stats/comp.php?leftmenu=report","Transforme",2,$user->rights->compta->resultat->lire); + } + */ $modecompta = 'CREANCES-DETTES'; $newmenu->add("/compta/stats/index.php?leftmenu=report&modecompta=".$modecompta, $langs->trans("ReportTurnover"), 1, $user->rights->compta->resultat->lire); @@ -1846,11 +1846,11 @@ function get_left_menu_accountancy($mainmenu, &$newmenu, $usemenuhider = 1, $lef $newmenu->add("/compta/stats/supplier_turnover_by_prodserv.php?leftmenu=accountancy_report&modecompta=".$modecompta, $langs->trans("ByProductsAndServices"), 2, $user->rights->compta->resultat->lire); /* - $modecompta = 'RECETTES-DEPENSES'; - $newmenu->add("/compta/stats/index.php?leftmenu=accountancy_report&modecompta=".$modecompta, $langs->trans("ReportPurchaseTurnoverCollected"), 1, $user->rights->compta->resultat->lire); - $newmenu->add("/compta/stats/casoc.php?leftmenu=accountancy_report&modecompta=".$modecompta, $langs->trans("ByCompanies"), 2, $user->rights->compta->resultat->lire); - $newmenu->add("/compta/stats/cabyuser.php?leftmenu=accountancy_report&modecompta=".$modecompta, $langs->trans("ByUsers"), 2, $user->rights->compta->resultat->lire); - */ + $modecompta = 'RECETTES-DEPENSES'; + $newmenu->add("/compta/stats/index.php?leftmenu=accountancy_report&modecompta=".$modecompta, $langs->trans("ReportPurchaseTurnoverCollected"), 1, $user->rights->compta->resultat->lire); + $newmenu->add("/compta/stats/casoc.php?leftmenu=accountancy_report&modecompta=".$modecompta, $langs->trans("ByCompanies"), 2, $user->rights->compta->resultat->lire); + $newmenu->add("/compta/stats/cabyuser.php?leftmenu=accountancy_report&modecompta=".$modecompta, $langs->trans("ByUsers"), 2, $user->rights->compta->resultat->lire); + */ // Journals $newmenu->add("/compta/journal/sellsjournal.php?leftmenu=report", $langs->trans("SellsJournal"), 1, $user->rights->compta->resultat->lire, '', '', '', 50); @@ -1886,11 +1886,11 @@ function get_left_menu_accountancy($mainmenu, &$newmenu, $usemenuhider = 1, $lef /** * Get left Menu BANK * - * @param string $mainmenu - * @param Menu &$newmenu Object Menu to return back list of menu entries - * @param string $usemenuhider - * @param string $leftmenu - * @param int $type_user + * @param string $mainmenu Main menu + * @param Menu $newmenu Object Menu to return back list of menu entries + * @param string $usemenuhider Use menu hider + * @param string $leftmenu Left menu + * @param int $type_user Type of targeted user for menu * @return void */ function get_left_menu_bank($mainmenu, &$newmenu, $usemenuhider = 1, $leftmenu = 'none', $type_user = 0) @@ -1969,11 +1969,11 @@ function get_left_menu_bank($mainmenu, &$newmenu, $usemenuhider = 1, $leftmenu = /** * Get left Menu PRODUCTS-SERVICES * - * @param string $mainmenu - * @param Menu &$newmenu Object Menu to return back list of menu entries - * @param string $usemenuhider - * @param string $leftmenu - * @param int $type_user + * @param string $mainmenu Main menu + * @param Menu $newmenu Object Menu to return back list of menu entries + * @param string $usemenuhider Use menu hider + * @param string $leftmenu Left menu + * @param int $type_user Type of targeted user for menu * @return void */ function get_left_menu_products($mainmenu, &$newmenu, $usemenuhider = 1, $leftmenu = 'none', $type_user = 0) @@ -2100,17 +2100,17 @@ function get_left_menu_products($mainmenu, &$newmenu, $usemenuhider = 1, $leftme /** * Get left Menu PRODUCTS-SERVICES MRP - GPAO * - * @param string $mainmenu - * @param Menu &$newmenu Object Menu to return back list of menu entries - * @param string $usemenuhider - * @param string $leftmenu - * @param int $type_user + * @param string $mainmenu Main menu + * @param Menu $newmenu Object Menu to return back list of menu entries + * @param string $usemenuhider Use menu hider + * @param string $leftmenu Left menu + * @param int $type_user Type of targeted user for menu * @return void */ function get_left_menu_mrp($mainmenu, &$newmenu, $usemenuhider = 1, $leftmenu = 'none', $type_user = 0) { global $user, $conf, $langs; - + if ($mainmenu == 'mrp') { // BOM if (!empty($conf->bom->enabled) || !empty($conf->mrp->enabled)) { @@ -2134,17 +2134,17 @@ function get_left_menu_mrp($mainmenu, &$newmenu, $usemenuhider = 1, $leftmenu = /** * Get left Menu PROJECTS * - * @param string $mainmenu - * @param Menu &$newmenu Object Menu to return back list of menu entries - * @param string $usemenuhider - * @param string $leftmenu - * @param int $type_user + * @param string $mainmenu Main menu + * @param Menu $newmenu Object Menu to return back list of menu entries + * @param string $usemenuhider Use menu hider + * @param string $leftmenu Left menu + * @param int $type_user Type of targeted user for menu * @return void */ function get_left_menu_projects($mainmenu, &$newmenu, $usemenuhider = 1, $leftmenu = 'none', $type_user = 0) { global $user, $conf, $langs; - + if ($mainmenu == 'project') { if (!empty($conf->projet->enabled)) { $langs->load("projects"); @@ -2208,11 +2208,11 @@ function get_left_menu_projects($mainmenu, &$newmenu, $usemenuhider = 1, $leftme /** * Get left Menu HRM * - * @param string $mainmenu - * @param Menu &$newmenu Object Menu to return back list of menu entries - * @param string $usemenuhider - * @param string $leftmenu - * @param int $type_user + * @param string $mainmenu Main menu + * @param Menu $newmenu Object Menu to return back list of menu entries + * @param string $usemenuhider Use menu hider + * @param string $leftmenu Left menu + * @param int $type_user Type of targeted user for menu * @return void */ function get_left_menu_hrm($mainmenu, &$newmenu, $usemenuhider = 1, $leftmenu = 'none', $type_user = 0) @@ -2315,17 +2315,17 @@ function get_left_menu_hrm($mainmenu, &$newmenu, $usemenuhider = 1, $leftmenu = /** * Get left Menu TOOLS * - * @param string $mainmenu - * @param Menu &$newmenu Object Menu to return back list of menu entries - * @param string $usemenuhider - * @param string $leftmenu - * @param int $type_user + * @param string $mainmenu Main menu + * @param Menu $newmenu Object Menu to return back list of menu entries + * @param string $usemenuhider Use menu hider + * @param string $leftmenu Left menu + * @param int $type_user Type of targeted user for menu * @return void */ function get_left_menu_tools($mainmenu, &$newmenu, $usemenuhider = 1, $leftmenu = 'none', $type_user = 0) { global $user, $conf, $langs; - + if ($mainmenu == 'tools') { if (empty($user->socid)) { // limit to internal users $langs->load("mails"); @@ -2356,11 +2356,11 @@ function get_left_menu_tools($mainmenu, &$newmenu, $usemenuhider = 1, $leftmenu /** * Get left Menu MEMBERS * - * @param string $mainmenu - * @param Menu &$newmenu Object Menu to return back list of menu entries - * @param string $usemenuhider - * @param string $leftmenu - * @param int $type_user + * @param string $mainmenu Main menu + * @param Menu $newmenu Object Menu to return back list of menu entries + * @param string $usemenuhider Use menu hider + * @param string $leftmenu Left menu + * @param int $type_user Type of targeted user for menu * @return void */ function get_left_menu_members($mainmenu, &$newmenu, $usemenuhider = 1, $leftmenu = 'none', $type_user = 0) From 9878b04a9817c0d8be38f8976b785a20f0d9b62d Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sat, 19 Mar 2022 18:20:24 +0100 Subject: [PATCH 20/24] Fix position of text in PDF --- .../facture/doc/pdf_sponge.modules.php | 167 +++++++++--------- 1 file changed, 88 insertions(+), 79 deletions(-) diff --git a/htdocs/core/modules/facture/doc/pdf_sponge.modules.php b/htdocs/core/modules/facture/doc/pdf_sponge.modules.php index 5d5b3cf762a..a5e9c0b841b 100644 --- a/htdocs/core/modules/facture/doc/pdf_sponge.modules.php +++ b/htdocs/core/modules/facture/doc/pdf_sponge.modules.php @@ -870,9 +870,9 @@ class pdf_sponge extends ModelePDFFactures // Retrieve type from database for backward compatibility with old records if ((!isset($localtax1_type) || $localtax1_type == '' || !isset($localtax2_type) || $localtax2_type == '') // if tax type not defined && (!empty($localtax1_rate) || !empty($localtax2_rate))) { // and there is local tax - $localtaxtmp_array = getLocalTaxesFromRate($vatrate, 0, $object->thirdparty, $mysoc); - $localtax1_type = isset($localtaxtmp_array[0]) ? $localtaxtmp_array[0] : ''; - $localtax2_type = isset($localtaxtmp_array[2]) ? $localtaxtmp_array[2] : ''; + $localtaxtmp_array = getLocalTaxesFromRate($vatrate, 0, $object->thirdparty, $mysoc); + $localtax1_type = isset($localtaxtmp_array[0]) ? $localtaxtmp_array[0] : ''; + $localtax2_type = isset($localtaxtmp_array[2]) ? $localtaxtmp_array[2] : ''; } // retrieve global local tax @@ -989,9 +989,9 @@ class pdf_sponge extends ModelePDFFactures @chmod($file, octdec($conf->global->MAIN_UMASK)); } - $this->result = array('fullpath'=>$file); + $this->result = array('fullpath'=>$file); - return 1; // No error + return 1; // No error } else { $this->error = $langs->transnoentities("ErrorCanNotCreateDir", $dir); return 0; @@ -1177,20 +1177,24 @@ class pdf_sponge extends ModelePDFFactures $posy = $pdf->GetY() + 4; } - $posxval = 52; + $posxval = 52; // Position of values of properties shown on left side + $posxend = 110; // End of x for text on left side + if ($this->page_largeur < 210) { // To work with US executive format + $posxend -= 10; + } // Show payments conditions if ($object->type != 2 && ($object->cond_reglement_code || $object->cond_reglement)) { $pdf->SetFont('', 'B', $default_font_size - 2); $pdf->SetXY($this->marge_gauche, $posy); $titre = $outputlangs->transnoentities("PaymentConditions").':'; - $pdf->MultiCell(43, 4, $titre, 0, 'L'); + $pdf->MultiCell($posxval - $this->marge_gauche, 4, $titre, 0, 'L'); $pdf->SetFont('', '', $default_font_size - 2); $pdf->SetXY($posxval, $posy); $lib_condition_paiement = $outputlangs->transnoentities("PaymentCondition".$object->cond_reglement_code) != ('PaymentCondition'.$object->cond_reglement_code) ? $outputlangs->transnoentities("PaymentCondition".$object->cond_reglement_code) : $outputlangs->convToOutputCharset($object->cond_reglement_doc ? $object->cond_reglement_doc : $object->cond_reglement_label); $lib_condition_paiement = str_replace('\n', "\n", $lib_condition_paiement); - $pdf->MultiCell(67, 4, $lib_condition_paiement, 0, 'L'); + $pdf->MultiCell($posxend - $posxval, 4, $lib_condition_paiement, 0, 'L'); $posy = $pdf->GetY() + 3; // We need spaces for 2 lines payment conditions } @@ -1198,42 +1202,42 @@ class pdf_sponge extends ModelePDFFactures if ($object->type != 2) { // Check a payment mode is defined if (empty($object->mode_reglement_code) - && empty($conf->global->FACTURE_CHQ_NUMBER) - && empty($conf->global->FACTURE_RIB_NUMBER)) { - $this->error = $outputlangs->transnoentities("ErrorNoPaiementModeConfigured"); + && empty($conf->global->FACTURE_CHQ_NUMBER) + && empty($conf->global->FACTURE_RIB_NUMBER)) { + $this->error = $outputlangs->transnoentities("ErrorNoPaiementModeConfigured"); } elseif (($object->mode_reglement_code == 'CHQ' && empty($conf->global->FACTURE_CHQ_NUMBER) && empty($object->fk_account) && empty($object->fk_bank)) - || ($object->mode_reglement_code == 'VIR' && empty($conf->global->FACTURE_RIB_NUMBER) && empty($object->fk_account) && empty($object->fk_bank))) { - // Avoid having any valid PDF with setup that is not complete - $outputlangs->load("errors"); + || ($object->mode_reglement_code == 'VIR' && empty($conf->global->FACTURE_RIB_NUMBER) && empty($object->fk_account) && empty($object->fk_bank))) { + // Avoid having any valid PDF with setup that is not complete + $outputlangs->load("errors"); - $pdf->SetXY($this->marge_gauche, $posy); - $pdf->SetTextColor(200, 0, 0); - $pdf->SetFont('', 'B', $default_font_size - 2); - $this->error = $outputlangs->transnoentities("ErrorPaymentModeDefinedToWithoutSetup", $object->mode_reglement_code); - $pdf->MultiCell(80, 3, $this->error, 0, 'L', 0); - $pdf->SetTextColor(0, 0, 0); + $pdf->SetXY($this->marge_gauche, $posy); + $pdf->SetTextColor(200, 0, 0); + $pdf->SetFont('', 'B', $default_font_size - 2); + $this->error = $outputlangs->transnoentities("ErrorPaymentModeDefinedToWithoutSetup", $object->mode_reglement_code); + $pdf->MultiCell($posxend - $this->marge_gauche, 3, $this->error, 0, 'L', 0); + $pdf->SetTextColor(0, 0, 0); - $posy = $pdf->GetY() + 1; + $posy = $pdf->GetY() + 1; } - // Show payment mode + // Show payment mode if (!empty($object->mode_reglement_code) - && $object->mode_reglement_code != 'CHQ' - && $object->mode_reglement_code != 'VIR') { - $pdf->SetFont('', 'B', $default_font_size - 2); - $pdf->SetXY($this->marge_gauche, $posy); - $titre = $outputlangs->transnoentities("PaymentMode").':'; - $pdf->MultiCell(80, 5, $titre, 0, 'L'); + && $object->mode_reglement_code != 'CHQ' + && $object->mode_reglement_code != 'VIR') { + $pdf->SetFont('', 'B', $default_font_size - 2); + $pdf->SetXY($this->marge_gauche, $posy); + $titre = $outputlangs->transnoentities("PaymentMode").':'; + $pdf->MultiCell($posxend - $this->marge_gauche, 5, $titre, 0, 'L'); - $pdf->SetFont('', '', $default_font_size - 2); - $pdf->SetXY($posxval, $posy); - $lib_mode_reg = $outputlangs->transnoentities("PaymentType".$object->mode_reglement_code) != ('PaymentType'.$object->mode_reglement_code) ? $outputlangs->transnoentities("PaymentType".$object->mode_reglement_code) : $outputlangs->convToOutputCharset($object->mode_reglement); - $pdf->MultiCell(80, 5, $lib_mode_reg, 0, 'L'); + $pdf->SetFont('', '', $default_font_size - 2); + $pdf->SetXY($posxval, $posy); + $lib_mode_reg = $outputlangs->transnoentities("PaymentType".$object->mode_reglement_code) != ('PaymentType'.$object->mode_reglement_code) ? $outputlangs->transnoentities("PaymentType".$object->mode_reglement_code) : $outputlangs->convToOutputCharset($object->mode_reglement); + $pdf->MultiCell($posxend - $posxval, 5, $lib_mode_reg, 0, 'L'); - $posy = $pdf->GetY(); + $posy = $pdf->GetY(); } - // Show online payment link + // Show online payment link if (empty($object->mode_reglement_code) || $object->mode_reglement_code == 'CB' || $object->mode_reglement_code == 'VAD') { $useonlinepayment = 0; if (!empty($conf->global->PDF_SHOW_LINK_TO_ONLINE_PAYMENT)) { @@ -1258,13 +1262,13 @@ class pdf_sponge extends ModelePDFFactures $linktopay = $langs->trans("ToOfferALinkForOnlinePayment", $servicename).' '.$outputlangs->transnoentities("ClickHere").''; $pdf->SetXY($this->marge_gauche, $posy); - $pdf->writeHTMLCell(80, 5, '', '', dol_htmlentitiesbr($linktopay), 0, 1); + $pdf->writeHTMLCell($posxend - $this->marge_gauche, 5, '', '', dol_htmlentitiesbr($linktopay), 0, 1); $posy = $pdf->GetY() + 1; } } - // Show payment mode CHQ + // Show payment mode CHQ if (empty($object->mode_reglement_code) || $object->mode_reglement_code == 'CHQ') { // If payment mode unregulated or payment mode forced to CHQ if (!empty($conf->global->FACTURE_CHQ_NUMBER)) { @@ -1276,33 +1280,33 @@ class pdf_sponge extends ModelePDFFactures $pdf->SetXY($this->marge_gauche, $posy); $pdf->SetFont('', 'B', $default_font_size - $diffsizetitle); - $pdf->MultiCell(100, 3, $outputlangs->transnoentities('PaymentByChequeOrderedTo', $account->proprio), 0, 'L', 0); + $pdf->MultiCell($posxend - $this->marge_gauche, 3, $outputlangs->transnoentities('PaymentByChequeOrderedTo', $account->proprio), 0, 'L', 0); $posy = $pdf->GetY() + 1; if (empty($conf->global->MAIN_PDF_HIDE_CHQ_ADDRESS)) { $pdf->SetXY($this->marge_gauche, $posy); $pdf->SetFont('', '', $default_font_size - $diffsizetitle); - $pdf->MultiCell(100, 3, $outputlangs->convToOutputCharset($account->owner_address), 0, 'L', 0); + $pdf->MultiCell($posxend - $this->marge_gauche, 3, $outputlangs->convToOutputCharset($account->owner_address), 0, 'L', 0); $posy = $pdf->GetY() + 2; } } if ($conf->global->FACTURE_CHQ_NUMBER == -1) { $pdf->SetXY($this->marge_gauche, $posy); $pdf->SetFont('', 'B', $default_font_size - $diffsizetitle); - $pdf->MultiCell(100, 3, $outputlangs->transnoentities('PaymentByChequeOrderedTo', $this->emetteur->name), 0, 'L', 0); + $pdf->MultiCell($posxend - $this->marge_gauche, 3, $outputlangs->transnoentities('PaymentByChequeOrderedTo', $this->emetteur->name), 0, 'L', 0); $posy = $pdf->GetY() + 1; if (empty($conf->global->MAIN_PDF_HIDE_CHQ_ADDRESS)) { $pdf->SetXY($this->marge_gauche, $posy); $pdf->SetFont('', '', $default_font_size - $diffsizetitle); - $pdf->MultiCell(100, 3, $outputlangs->convToOutputCharset($this->emetteur->getFullAddress()), 0, 'L', 0); + $pdf->MultiCell($posxend - $this->marge_gauche, 3, $outputlangs->convToOutputCharset($this->emetteur->getFullAddress()), 0, 'L', 0); $posy = $pdf->GetY() + 2; } } } } - // If payment mode not forced or forced to VIR, show payment with BAN + // If payment mode not forced or forced to VIR, show payment with BAN if (empty($object->mode_reglement_code) || $object->mode_reglement_code == 'VIR') { if ($object->fk_account > 0 || $object->fk_bank > 0 || !empty($conf->global->FACTURE_RIB_NUMBER)) { $bankid = ($object->fk_account <= 0 ? $conf->global->FACTURE_RIB_NUMBER : $object->fk_account); @@ -1350,7 +1354,11 @@ class pdf_sponge extends ModelePDFFactures $tab2_top = $posy; $tab2_hl = 4; - $pdf->SetFont('', '', $default_font_size - 1); + if (is_object($outputlangsbis)) { // When we show 2 languages we need more room for text, so we use a smaller font. + $pdf->SetFont('', '', $default_font_size - 2); + } else { + $pdf->SetFont('', '', $default_font_size - 1); + } // Total table $col1x = 120; @@ -1482,9 +1490,10 @@ class pdf_sponge extends ModelePDFFactures $tab2_top = $posy; $index = 0; + + $tab2_top += 3; } - $tab2_top += 3; // Get Total HT $total_ht = (!empty($conf->multicurrency->enabled) && $object->mylticurrency_tx != 1 ? $object->multicurrency_total_ht : $object->total_ht); @@ -1805,19 +1814,19 @@ class pdf_sponge extends ModelePDFFactures } /* - if ($object->close_code == Facture::CLOSECODE_DISCOUNTVAT) - { - $index++; - $pdf->SetFillColor(255, 255, 255); + if ($object->close_code == Facture::CLOSECODE_DISCOUNTVAT) + { + $index++; + $pdf->SetFillColor(255, 255, 255); - $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index); - $pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities("EscompteOfferedShort").(is_object($outputlangsbis) ? ' / '.$outputlangsbis->transnoentities("EscompteOfferedShort") : ''), $useborder, 'L', 1); - $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index); - $pdf->MultiCell($largcol2, $tab2_hl, price($object->total_ttc - $deja_regle - $creditnoteamount - $depositsamount, 0, $outputlangs), $useborder, 'R', 1); + $pdf->SetXY($col1x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($col2x - $col1x, $tab2_hl, $outputlangs->transnoentities("EscompteOfferedShort").(is_object($outputlangsbis) ? ' / '.$outputlangsbis->transnoentities("EscompteOfferedShort") : ''), $useborder, 'L', 1); + $pdf->SetXY($col2x, $tab2_top + $tab2_hl * $index); + $pdf->MultiCell($largcol2, $tab2_hl, price($object->total_ttc - $deja_regle - $creditnoteamount - $depositsamount, 0, $outputlangs), $useborder, 'R', 1); - $resteapayer = 0; - } - */ + $resteapayer = 0; + } + */ $index++; $pdf->SetTextColor(0, 0, 60); @@ -1937,7 +1946,7 @@ class pdf_sponge extends ModelePDFFactures // Show Draft Watermark if ($object->statut == $object::STATUS_DRAFT && (!empty($conf->global->FACTURE_DRAFT_WATERMARK))) { - pdf_watermark($pdf, $outputlangs, $this->page_hauteur, $this->page_largeur, 'mm', $conf->global->FACTURE_DRAFT_WATERMARK); + pdf_watermark($pdf, $outputlangs, $this->page_hauteur, $this->page_largeur, 'mm', $conf->global->FACTURE_DRAFT_WATERMARK); } $pdf->SetTextColor(0, 0, 60); @@ -2024,15 +2033,15 @@ class pdf_sponge extends ModelePDFFactures $pdf->SetFont('', 'B', $default_font_size); /* - $posy += 5; - $pdf->SetXY($posx, $posy); - $pdf->SetTextColor(0, 0, 60); - $textref = $outputlangs->transnoentities("Ref")." : ".$outputlangs->convToOutputCharset($object->ref); - if ($object->statut == $object::STATUS_DRAFT) { - $pdf->SetTextColor(128, 0, 0); - $textref .= ' - '.$outputlangs->transnoentities("NotValidated"); - } - $pdf->MultiCell($w, 4, $textref, '', 'R');*/ + $posy += 5; + $pdf->SetXY($posx, $posy); + $pdf->SetTextColor(0, 0, 60); + $textref = $outputlangs->transnoentities("Ref")." : ".$outputlangs->convToOutputCharset($object->ref); + if ($object->statut == $object::STATUS_DRAFT) { + $pdf->SetTextColor(128, 0, 0); + $textref .= ' - '.$outputlangs->transnoentities("NotValidated"); + } + $pdf->MultiCell($w, 4, $textref, '', 'R');*/ $posy += 3; $pdf->SetFont('', '', $default_font_size - 2); @@ -2296,21 +2305,21 @@ class pdf_sponge extends ModelePDFFactures /* * For exemple - $this->cols['theColKey'] = array( - 'rank' => $rank, // int : use for ordering columns - 'width' => 20, // the column width in mm - 'title' => array( - 'textkey' => 'yourLangKey', // if there is no label, yourLangKey will be translated to replace label - 'label' => ' ', // the final label : used fore final generated text - 'align' => 'L', // text alignement : R,C,L - 'padding' => array(0.5,0.5,0.5,0.5), // Like css 0 => top , 1 => right, 2 => bottom, 3 => left - ), - 'content' => array( - 'align' => 'L', // text alignement : R,C,L - 'padding' => array(0.5,0.5,0.5,0.5), // Like css 0 => top , 1 => right, 2 => bottom, 3 => left - ), - ); - */ + $this->cols['theColKey'] = array( + 'rank' => $rank, // int : use for ordering columns + 'width' => 20, // the column width in mm + 'title' => array( + 'textkey' => 'yourLangKey', // if there is no label, yourLangKey will be translated to replace label + 'label' => ' ', // the final label : used fore final generated text + 'align' => 'L', // text alignement : R,C,L + 'padding' => array(0.5,0.5,0.5,0.5), // Like css 0 => top , 1 => right, 2 => bottom, 3 => left + ), + 'content' => array( + 'align' => 'L', // text alignement : R,C,L + 'padding' => array(0.5,0.5,0.5,0.5), // Like css 0 => top , 1 => right, 2 => bottom, 3 => left + ), + ); + */ $rank = 0; // do not use negative rank $this->cols['desc'] = array( From cb93ab472dfc734e5de28bf9b8aab5639d137098 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sat, 19 Mar 2022 19:14:12 +0100 Subject: [PATCH 21/24] FIX ZATCA Encoding --- .../iso-normes/QR code for invoices.txt | 8 +- htdocs/core/class/commoninvoice.class.php | 8 +- test/phpunit/BarcodeTest.php | 203 ++++++++++++++++++ 3 files changed, 214 insertions(+), 5 deletions(-) create mode 100644 test/phpunit/BarcodeTest.php diff --git a/dev/resources/iso-normes/QR code for invoices.txt b/dev/resources/iso-normes/QR code for invoices.txt index a55c9569297..f03351f453f 100644 --- a/dev/resources/iso-normes/QR code for invoices.txt +++ b/dev/resources/iso-normes/QR code for invoices.txt @@ -8,6 +8,10 @@ https://en.wikipedia.org/wiki/EPC_QR_code#Generators -* For ZATCA QR Code format (Saudi Arabia) ------------------------------------------ +* For ZATCA QR Code format (Saudi Arabia). Used when INVOICE_ADD_ZATCA_QR_CODE is set +------------------------------------------------------------------------------------- https://www.pwc.com/m1/en/services/tax/me-tax-legal-news/2021/saudi-arabia-guide-to-develop-compliant-qr-code-for-simplified-einvoices.html + +https://www.tecklenborgh.com/post/ksa-zatca-publishes-guide-on-how-to-develop-a-fatoora-compliant-qr-code + +Method to encode/decode ZATCA string is available in test/phpunit/BarcodeTest.php diff --git a/htdocs/core/class/commoninvoice.class.php b/htdocs/core/class/commoninvoice.class.php index 29fd230a61b..eedc1b2fa10 100644 --- a/htdocs/core/class/commoninvoice.class.php +++ b/htdocs/core/class/commoninvoice.class.php @@ -827,8 +827,10 @@ abstract class CommonInvoice extends CommonObject $tmplang->load("main"); $datestring = dol_print_date($this->date, 'dayhourrfc'); - $pricewithtaxstring = price($this->total_ttc, 0, $tmplang, 0, -1, 2); - $pricetaxstring = price($this->total_tva, 0, $tmplang, 0, -1, 2); + //$pricewithtaxstring = price($this->total_ttc, 0, $tmplang, 0, -1, 2); + //$pricetaxstring = price($this->total_tva, 0, $tmplang, 0, -1, 2); + $pricewithtaxstring = price2num($this->total_ttc, 2, 1); + $pricetaxstring = price2num($this->total_tva, 2, 1); /* $name = implode(unpack("H*", $this->thirdparty->name)); @@ -857,7 +859,7 @@ abstract class CommonInvoice extends CommonObject // Using TLV format $s = pack('C1', 1).pack('C1', strlen($this->thirdparty->name)).$this->thirdparty->name; $s .= pack('C1', 2).pack('C1', strlen($this->thirdparty->tva_intra)).$this->thirdparty->tva_intra; - $s .= pack('C1', 3).pack('C1', strlen($datestring)).$this->date; + $s .= pack('C1', 3).pack('C1', strlen($datestring)).$datestring; $s .= pack('C1', 4).pack('C1', strlen($pricewithtaxstring)).$pricewithtaxstring; $s .= pack('C1', 5).pack('C1', strlen($pricetaxstring)).$pricetaxstring; $s .= ''; // Hash of xml invoice diff --git a/test/phpunit/BarcodeTest.php b/test/phpunit/BarcodeTest.php new file mode 100644 index 00000000000..132ba8c1126 --- /dev/null +++ b/test/phpunit/BarcodeTest.php @@ -0,0 +1,203 @@ + + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * or see https://www.gnu.org/ + */ + +/** + * \file test/phpunit/BarcodeTest.php + * \ingroup test + * \brief PHPUnit test + * \remarks To run this script as CLI: phpunit filename.php + */ + +global $conf,$user,$langs,$db; +//define('TEST_DB_FORCE_TYPE','mysql'); // This is to force using mysql driver +//require_once 'PHPUnit/Autoload.php'; +require_once dirname(__FILE__).'/../../htdocs/master.inc.php'; +require_once dirname(__FILE__).'/../../htdocs/compta/facture/class/facture.class.php'; + + +if (empty($user->id)) { + print "Load permissions for admin user nb 1\n"; + $user->fetch(1); + $user->getrights(); +} +$conf->global->MAIN_DISABLE_ALL_MAILS=1; + +$langs->load("main"); + + +/** + * Class for PHPUnit tests + * + * @backupGlobals disabled + * @backupStaticAttributes enabled + * @remarks backupGlobals must be disabled to have db,conf,user and lang not erased. + */ +class BarcodeTest extends PHPUnit\Framework\TestCase +{ + protected $savconf; + protected $savuser; + protected $savlangs; + protected $savdb; + + /** + * Constructor + * We save global variables into local variables + * + * @return BarcodeTest + */ + public function __construct() + { + parent::__construct(); + + //$this->sharedFixture + global $conf,$user,$langs,$db; + $this->savconf=$conf; + $this->savuser=$user; + $this->savlangs=$langs; + $this->savdb=$db; + + print __METHOD__." db->type=".$db->type." user->id=".$user->id; + //print " - db ".$db->db; + print "\n"; + } + + /** + * setUpBeforeClass + * + * @return void + */ + public static function setUpBeforeClass() + { + global $conf,$user,$langs,$db; + $db->begin(); // This is to have all actions inside a transaction even if test launched without suite. + + print __METHOD__."\n"; + } + + /** + * tearDownAfterClass + * + * @return void + */ + public static function tearDownAfterClass() + { + global $conf,$user,$langs,$db; + $db->rollback(); + + print __METHOD__."\n"; + } + + /** + * Init phpunit tests + * + * @return void + */ + protected function setUp() + { + global $conf,$user,$langs,$db; + $conf=$this->savconf; + $user=$this->savuser; + $langs=$this->savlangs; + $db=$this->savdb; + + print __METHOD__."\n"; + } + + /** + * End phpunit tests + * + * @return void + */ + protected function tearDown() + { + print __METHOD__."\n"; + } + + + /** + * testBarcodeZATCAEncode + * + * @return int + */ + public function testBarcodeZATCAEncode() + { + global $conf,$user,$langs,$db; + $conf=$this->savconf; + $user=$this->savuser; + $langs=$this->savlangs; + $db=$this->savdb; + + $company = new Societe($db); + $company->name = 'Specimen company'; + $company->tva_intra = '123456789'; + + $tmpinvoice = new Facture($db); + + $tmpinvoice->thirdparty = $company; + $tmpinvoice->total_ht = 100; + $tmpinvoice->total_tva = 20; + $tmpinvoice->total_ttc = $tmpinvoice->total_ht + $tmpinvoice->total_tva; + $tmpinvoice->date = dol_mktime(12, 34, 56, 1, 1, 2020, 'gmt'); + + $string_zatca = $tmpinvoice->buildZATCAQRString(); + + $this->assertEquals($string_zatca, "ARBTcGVjaW1lbiBjb21wYW55AgkxMjM0NTY3ODkDFDIwMjAtMDEtMDFUMDk6MzQ6NTZaBAMxMjAFAjIw"); + + return 1; + } + + + + /** + * testBarcodeZATCADecode + * + * @return int + */ + public function testBarcodeZATCADecode() + { + global $conf,$user,$langs,$db; + $conf=$this->savconf; + $user=$this->savuser; + $langs=$this->savlangs; + $db=$this->savdb; + + //$string_zatca_base64 = "AQZSYWZlZXECDTEyMzQ1Njc4OVQxMjUDFDIwMjEtMDctMTJUMTQ6MjU6MDlaBAM3ODYFAjI1"; + $string_zatca_base64 = "ARBTcGVjaW1lbiBjb21wYW55AgkxMjM0NTY3ODkDFDIwMjAtMDEtMDFUMDk6MzQ6NTZaBAMxMjAFAjIw"; + + $decoded = base64_decode($string_zatca_base64); + + //print_r($decoded) + //raw data + //\u0001\u0006Rafeeq\u0002\t123456789\u0003\u00142021-07-12T14:25:09Z\u0004\u0003786\u0005\u000225 + + $result_data = preg_replace('/[\x00-\x1F\x80-\xFF]/', ',', $decoded); + + $arrayOfData = explode(',,', $result_data); + + + print __METHOD__." result=".var_export($arrayOfData, true)."\n"; + $this->assertEquals("", $arrayOfData[0]); + $this->assertEquals("Specimen company", $arrayOfData[1]); + $this->assertEquals("123456789", $arrayOfData[2]); + $this->assertEquals("2020-01-01T09:34:56Z", $arrayOfData[3]); + $this->assertEquals("120", $arrayOfData[4]); + $this->assertEquals("20", $arrayOfData[5]); + + return 1; + } +} From cd37d4b403080089399601540d9aa3d67c2432c5 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Mon, 21 Mar 2022 00:14:42 +0100 Subject: [PATCH 22/24] Code comment --- htdocs/core/class/doleditor.class.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/htdocs/core/class/doleditor.class.php b/htdocs/core/class/doleditor.class.php index 4a2e65ba1f3..23c04a98a81 100644 --- a/htdocs/core/class/doleditor.class.php +++ b/htdocs/core/class/doleditor.class.php @@ -193,10 +193,10 @@ class DolEditor removePlugins : \''.$pluginstodisable.'\', readOnly : '.($this->readonly ? 'true' : 'false').', htmlEncodeOutput :'.$htmlencode_force.', - allowedContent :'.($disallowAnyContent ? 'false' : 'true').', - extraAllowedContent : \'a[target];div{float,display}\', /* Add the style float and display into div to default other allowed tags */ - disallowedContent : '.($disallowAnyContent ? '\'\'' : '\'\'').', - fullPage : '.($fullpage ? 'true' : 'false').', + allowedContent :'.($disallowAnyContent ? 'false' : 'true').', /* Advanced Content Filter (ACF) is own when allowedContent is false */ + extraAllowedContent : \'a[target];div{float,display}\', /* Add the style float and display into div to default other allowed tags */ + disallowedContent : '.($disallowAnyContent ? '\'\'' : '\'\'').', /* Tags that are not allowed */ + fullPage : '.($fullpage ? 'true' : 'false').', /* if true, the html, header and body tags are kept */ toolbar: \''.$this->toolbarname.'\', toolbarStartupExpanded: '.($this->toolbarstartexpanded ? 'true' : 'false').', width: '.($this->width ? '\''.$this->width.'\'' : '\'\'').', From 40fbaf15fef5c4aeb06075b20abeb0ec82117ead Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Mon, 21 Mar 2022 17:12:26 +0100 Subject: [PATCH 23/24] Add welsh language --- htdocs/langs/en_US/languages.lang | 1 + 1 file changed, 1 insertion(+) diff --git a/htdocs/langs/en_US/languages.lang b/htdocs/langs/en_US/languages.lang index 7c2a0f8b6ec..820339e6875 100644 --- a/htdocs/langs/en_US/languages.lang +++ b/htdocs/langs/en_US/languages.lang @@ -16,6 +16,7 @@ Language_bg_BG=Bulgarian Language_bs_BA=Bosnian Language_ca_ES=Catalan Language_cs_CZ=Czech +Language_cy_GB=Welsh Language_da_DA=Danish Language_da_DK=Danish Language_de_DE=German From 2e27c40de4a233c89dd71c9cbc4607676145f662 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Mon, 21 Mar 2022 22:02:34 +0100 Subject: [PATCH 24/24] Fix customreport with link to parents --- htdocs/contrat/class/contrat.class.php | 4 +- htdocs/core/customreports.php | 58 +++++++++++++++++++++----- htdocs/langs/en_US/contracts.lang | 4 +- 3 files changed, 52 insertions(+), 14 deletions(-) diff --git a/htdocs/contrat/class/contrat.class.php b/htdocs/contrat/class/contrat.class.php index 4e4437c1b2c..c9341d23ee3 100644 --- a/htdocs/contrat/class/contrat.class.php +++ b/htdocs/contrat/class/contrat.class.php @@ -2750,8 +2750,8 @@ class ContratLigne extends CommonObjectLine //'model_pdf' =>array('type'=>'varchar(255)', 'label'=>'Model pdf', 'enabled'=>1, 'visible'=>0, 'position'=>115), //'import_key' =>array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>-2, 'position'=>120), //'extraparams' =>array('type'=>'varchar(255)', 'label'=>'Extraparams', 'enabled'=>1, 'visible'=>-1, 'position'=>125), - 'fk_user_ouverture' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserOpen', 'enabled'=>1, 'visible'=>-2, 'notnull'=>-1, 'position'=>135), - 'fk_user_cloture' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserCloture', 'enabled'=>1, 'visible'=>-2, 'notnull'=>-1, 'position'=>135), + 'fk_user_ouverture' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserStartingService', 'enabled'=>1, 'visible'=>-2, 'notnull'=>-1, 'position'=>135), + 'fk_user_cloture' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserClosingService', 'enabled'=>1, 'visible'=>-2, 'notnull'=>-1, 'position'=>135), 'statut' =>array('type'=>'smallint(6)', 'label'=>'Statut', 'enabled'=>1, 'visible'=>-1, 'position'=>500, 'arrayofkeyval'=>array(0=>'Draft', 4=>'Open', 5=>'Closed')) ); // END MODULEBUILDER PROPERTIES diff --git a/htdocs/core/customreports.php b/htdocs/core/customreports.php index 7331b08e13e..ec0ce330dc4 100644 --- a/htdocs/core/customreports.php +++ b/htdocs/core/customreports.php @@ -556,9 +556,26 @@ if (!empty($search_measures) && !empty($search_xaxis)) { $sql .= ' AND parenttable.entity IN ('.getEntity($tmparray[1]).')'; } } + + // Add INNER JOIN for all parent tables + //var_dump($arrayofxaxis); var_dump($search_xaxis); + $listoftablesalreadyadded = array($object->table_element => $object->table_element); + foreach ($search_xaxis as $key => $val) { + if (!empty($arrayofxaxis[$val])) { + $tmpval = explode('.', $val); + //var_dump($arrayofxaxis[$val]['table']); + if (! in_array($arrayofxaxis[$val]['table'], $listoftablesalreadyadded)) { // We do not add join for main table already added + $sql .= ' INNER JOIN '.MAIN_DB_PREFIX.$arrayofxaxis[$val]['table'].' as '.$tmpval[0]; + $listoftablesalreadyadded[$arrayofxaxis[$val]['table']] = $arrayofxaxis[$val]['table']; + } + } else { + dol_print_error($db, 'Found an key into search_xaxis not found into arrayofxaxis'); + } + } + $sql .= ' WHERE 1 = 1'; if ($object->ismultientitymanaged == 1) { - $sql .= ' AND entity IN ('.getEntity($object->element).')'; + $sql .= ' AND t.entity IN ('.getEntity($object->element).')'; } // Add the where here $sqlfilters = $search_component_params_hidden; @@ -914,7 +931,7 @@ function fillArrayOfMeasures($object, $tablealias, $labelofobject, &$arrayofmesu * Fill arrayofmesures for an object * * @param mixed $object Any object - * @param string $tablealias Alias of table + * @param string $tablealias Alias of table ('t' for example) * @param string $labelofobject Label of object * @param array $arrayofxaxis Array of xaxis already filled * @param int $level Level @@ -964,14 +981,31 @@ function fillArrayOfXAxis($object, $tablealias, $labelofobject, &$arrayofxaxis, continue; } if (in_array($val['type'], array('timestamp', 'date', 'datetime'))) { - $arrayofxaxis[$tablealias.'.'.$key.'-year'] = array('label' => img_picto('', $object->picto, 'class="pictofixedwidth"').' '.$labelofobject.': '.$langs->trans($val['label']).' ('.$YYYY.')', 'position' => ($val['position']+($count * 100000)).'.1'); - $arrayofxaxis[$tablealias.'.'.$key.'-month'] = array('label' => img_picto('', $object->picto, 'class="pictofixedwidth"').' '.$labelofobject.': '.$langs->trans($val['label']).' ('.$YYYY.'-'.$MM.')', 'position' => ($val['position']+($count * 100000)).'.2'); - $arrayofxaxis[$tablealias.'.'.$key.'-day'] = array('label' => img_picto('', $object->picto, 'class="pictofixedwidth"').' '.$labelofobject.': '.$langs->trans($val['label']).' ('.$YYYY.'-'.$MM.'-'.$DD.')', 'position' => ($val['position']+($count * 100000)).'.3'); + $arrayofxaxis[$tablealias.'.'.$key.'-year'] = array( + 'label' => img_picto('', $object->picto, 'class="pictofixedwidth"').' '.$labelofobject.': '.$langs->trans($val['label']).' ('.$YYYY.')', + 'position' => ($val['position']+($count * 100000)).'.1', + 'table' => $object->table_element + ); + $arrayofxaxis[$tablealias.'.'.$key.'-month'] = array( + 'label' => img_picto('', $object->picto, 'class="pictofixedwidth"').' '.$labelofobject.': '.$langs->trans($val['label']).' ('.$YYYY.'-'.$MM.')', + 'position' => ($val['position']+($count * 100000)).'.2', + 'table' => $object->table_element + ); + $arrayofxaxis[$tablealias.'.'.$key.'-day'] = array( + 'label' => img_picto('', $object->picto, 'class="pictofixedwidth"').' '.$labelofobject.': '.$langs->trans($val['label']).' ('.$YYYY.'-'.$MM.'-'.$DD.')', + 'position' => ($val['position']+($count * 100000)).'.3', + 'table' => $object->table_element + ); } else { - $arrayofxaxis[$tablealias.'.'.$key] = array('label' => img_picto('', $object->picto, 'class="pictofixedwidth"').' '.$labelofobject.': '.$langs->trans($val['label']), 'position' => ($val['position']+($count * 100000))); + $arrayofxaxis[$tablealias.'.'.$key] = array( + 'label' => img_picto('', $object->picto, 'class="pictofixedwidth"').' '.$labelofobject.': '.$langs->trans($val['label']), + 'position' => ($val['position']+($count * 100000)), + 'table' => $object->table_element + ); } } } + // Add extrafields to X-Axis if ($object->isextrafieldmanaged) { foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $val) { @@ -981,9 +1015,14 @@ function fillArrayOfXAxis($object, $tablealias, $labelofobject, &$arrayofxaxis, if (!empty($extrafields->attributes[$object->table_element]['totalizable'][$key])) { continue; } - $arrayofxaxis[$tablealias.'e.'.$key] = array('label' => img_picto('', $object->picto, 'class="pictofixedwidth"').' '.$labelofobject.': '.$langs->trans($extrafields->attributes[$object->table_element]['label'][$key]), 'position' => 1000 + (int) $extrafields->attributes[$object->table_element]['pos'][$key] + ($count * 100000)); + $arrayofxaxis[$tablealias.'e.'.$key] = array( + 'label' => img_picto('', $object->picto, 'class="pictofixedwidth"').' '.$labelofobject.': '.$langs->trans($extrafields->attributes[$object->table_element]['label'][$key]), + 'position' => 1000 + (int) $extrafields->attributes[$object->table_element]['pos'][$key] + ($count * 100000), + 'table' => $object->table_element + ); } } + // Add fields for parent objects foreach ($object->fields as $key => $val) { if (preg_match('/^[^:]+:[^:]+:/', $val['type'])) { @@ -993,10 +1032,7 @@ function fillArrayOfXAxis($object, $tablealias, $labelofobject, &$arrayofxaxis, dol_include_once($tmptype[2]); if (class_exists($newobject)) { $tmpobject = new $newobject($db); - /*var_dump($val['label']); - var_dump($tmptype); - var_dump($arrayofmesures); - var_dump('t-'.$key);*/ + //var_dump($key); var_dump($tmpobject->element); var_dump($val['label']); var_dump($tmptype); var_dump('t-'.$key); $count++; $arrayofxaxis = fillArrayOfXAxis($tmpobject, $tablealias.'__'.$key, $langs->trans($val['label']), $arrayofxaxis, $level + 1, $count); } else { diff --git a/htdocs/langs/en_US/contracts.lang b/htdocs/langs/en_US/contracts.lang index 746c7fdfeb6..8d209623c1b 100644 --- a/htdocs/langs/en_US/contracts.lang +++ b/htdocs/langs/en_US/contracts.lang @@ -102,4 +102,6 @@ TypeContact_contrat_external_CUSTOMER=Follow-up customer contact TypeContact_contrat_external_SALESREPSIGN=Signing contract customer contact HideClosedServiceByDefault=Hide closed services by default ShowClosedServices=Show Closed Services -HideClosedServices=Hide Closed Services \ No newline at end of file +HideClosedServices=Hide Closed Services +UserStartingService=User starting service +UserClosingService=User closing service