From 05e09ccc17bd713d5ae2af13c31f79d8c7b00d22 Mon Sep 17 00:00:00 2001 From: altairis-noe Date: Fri, 16 Jul 2021 17:46:21 +0200 Subject: [PATCH 01/42] Filter reassortlot with warehouse tags --- htdocs/product/reassortlot.php | 53 ++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 19 deletions(-) diff --git a/htdocs/product/reassortlot.php b/htdocs/product/reassortlot.php index ea9901a3794..bc93503a3c7 100644 --- a/htdocs/product/reassortlot.php +++ b/htdocs/product/reassortlot.php @@ -6,6 +6,7 @@ * Copyright (C) 2015 Raphaël Doursenaud * Copyright (C) 2016 Ferran Marcet * Copyright (C) 2019 Juanjo Menent + * Copyright (C) 2021 Noé Cendrier * * 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 @@ -72,6 +73,7 @@ $offset = $limit * $page; // Load sale and categ filters $search_sale = GETPOST("search_sale"); $search_categ = GETPOST("search_categ"); +$search_warehouse_categ = GETPOST("search_warehouse_categ"); // Get object canvas (By default, this is not defined, so standard usage of dolibarr) $canvas = GETPOST("canvas"); @@ -101,6 +103,7 @@ if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x' $tobuy = ""; $search_sale = ""; $search_categ = ""; + $search_warehouse_categ = ""; $type = ""; $catid = ''; $toolowstock = ''; @@ -132,16 +135,17 @@ $sql .= ' SUM(pb.qty) as stock_physique, COUNT(pb.rowid) as nbinbatchtable'; $sql .= ' FROM '.MAIN_DB_PREFIX.'product as p'; $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product_stock as ps on p.rowid = ps.fk_product'; // Detail for each warehouse $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'entrepot as e on ps.fk_entrepot = e.rowid'; // Link on unique key +$sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'categorie_warehouse as cw on e.rowid = cw.fk_warehouse'; $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product_batch as pb on pb.fk_product_stock = ps.rowid'; // Detail for each lot on each warehouse $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product_lot as pl on pl.fk_product = p.rowid AND pl.batch = pb.batch'; // Link on unique key // We'll need this table joined to the select in order to filter by categ if ($search_categ) { - $sql .= ", ".MAIN_DB_PREFIX."categorie_product as cp"; + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."categorie_product as cp ON p.rowid = cp.fk_product"; } $sql .= " WHERE p.entity IN (".getEntity('product').")"; -if ($search_categ) { - $sql .= " AND p.rowid = cp.fk_product"; // Join for the needed table to filter by categ -} +// if ($search_categ) { +// $sql .= " AND "; // Join for the needed table to filter by categ +// } if ($sall) { $sql .= natural_search(array('p.ref', 'p.label', 'p.description', 'p.note'), $sall); } @@ -178,9 +182,13 @@ if ($fourn_id > 0) { $sql .= " AND p.rowid = pf.fk_product AND pf.fk_soc = ".((int) $fourn_id); } // Insert categ filter -if ($search_categ) { +if ($search_categ > 0) { $sql .= " AND cp.fk_categorie = ".((int) $search_categ); } +if ($search_warehouse_categ > 0) { + // $sql .= Categorie::getFilterSelectQuery(Categorie::TYPE_WAREHOUSE, "e.rowid", $search_warehouse_categ); + $sql .= " AND cw.fk_categorie = " . (int) $search_warehouse_categ; +} if ($search_warehouse) { $sql .= natural_search("e.ref", $search_warehouse); } @@ -280,6 +288,9 @@ if ($resql) { if ($search_categ) { $param .= "&search_categ=".urlencode($search_categ); } + if ($search_warehouse_categ) { + $param .= "&search_warehouse_categ=".urlencode($search_warehouse_categ); + } /*if ($eatby) $param.="&eatby=".$eatby; if ($sellby) $param.="&sellby=".$sellby;*/ @@ -303,28 +314,32 @@ if ($resql) { print "
"; } - // Filter on categories + // Filter on product categories $moreforfilter = ''; if (!empty($conf->categorie->enabled)) { - $moreforfilter .= '
'; - $moreforfilter .= img_picto($langs->trans('Categories'), 'category', 'class="pictofixedwidth"'); - $moreforfilter .= $htmlother->select_categories(Categorie::TYPE_PRODUCT, $search_categ, 'search_categ'); - $moreforfilter .= '
'; + $moreforfilter .= ''; + $moreforfilter .= ''.img_picto($langs->trans('Categories'), 'category', 'class="pictofixedwidth"'); + $moreforfilter .= $htmlother->select_categories(Categorie::TYPE_PRODUCT, $search_categ, 'search_categ').''; + + // Filter on warehouse categories + $moreforfilter .= ''.img_picto($langs->trans('Categories'), 'category', 'class="pictofixedwidth"'); + $moreforfilter .= $htmlother->select_categories(Categorie::TYPE_WAREHOUSE, $search_warehouse_categ, 'search_warehouse_categ').''; + $moreforfilter .= ''; } //$moreforfilter.=$langs->trans("StockTooLow").' '; - if (!empty($moreforfilter)) { - print '
'; - print $moreforfilter; - $parameters = array(); - $reshook = $hookmanager->executeHooks('printFieldPreListTitle', $parameters); // Note that $action and $object may have been modified by hook - print $hookmanager->resPrint; - print '
'; - } - print '
'; print ''; + if (!empty($moreforfilter)) { + // print '
'; + print $moreforfilter; + $parameters = array(); + $reshook = $hookmanager->executeHooks('printFieldPreListTitle', $parameters); // Note that $action and $object may have been modified by hook + print $hookmanager->resPrint; + // print '
'; + } + // Fields title search print ''; From d8551da83f77eab4998f9b732d937dcdb953c427 Mon Sep 17 00:00:00 2001 From: altairis-noe Date: Mon, 19 Jul 2021 09:36:58 +0200 Subject: [PATCH 02/42] correction after review --- htdocs/product/reassortlot.php | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/htdocs/product/reassortlot.php b/htdocs/product/reassortlot.php index bc93503a3c7..751c47fd2d2 100644 --- a/htdocs/product/reassortlot.php +++ b/htdocs/product/reassortlot.php @@ -135,17 +135,16 @@ $sql .= ' SUM(pb.qty) as stock_physique, COUNT(pb.rowid) as nbinbatchtable'; $sql .= ' FROM '.MAIN_DB_PREFIX.'product as p'; $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product_stock as ps on p.rowid = ps.fk_product'; // Detail for each warehouse $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'entrepot as e on ps.fk_entrepot = e.rowid'; // Link on unique key -$sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'categorie_warehouse as cw on e.rowid = cw.fk_warehouse'; $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product_batch as pb on pb.fk_product_stock = ps.rowid'; // Detail for each lot on each warehouse $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product_lot as pl on pl.fk_product = p.rowid AND pl.batch = pb.batch'; // Link on unique key // We'll need this table joined to the select in order to filter by categ -if ($search_categ) { +if ($search_categ > 0) { $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."categorie_product as cp ON p.rowid = cp.fk_product"; } +if ($search_warehouse_categ > 0) { + $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'categorie_warehouse as cw on e.rowid = cw.fk_warehouse'; +} $sql .= " WHERE p.entity IN (".getEntity('product').")"; -// if ($search_categ) { -// $sql .= " AND "; // Join for the needed table to filter by categ -// } if ($sall) { $sql .= natural_search(array('p.ref', 'p.label', 'p.description', 'p.note'), $sall); } @@ -186,7 +185,6 @@ if ($search_categ > 0) { $sql .= " AND cp.fk_categorie = ".((int) $search_categ); } if ($search_warehouse_categ > 0) { - // $sql .= Categorie::getFilterSelectQuery(Categorie::TYPE_WAREHOUSE, "e.rowid", $search_warehouse_categ); $sql .= " AND cw.fk_categorie = " . (int) $search_warehouse_categ; } if ($search_warehouse) { @@ -285,10 +283,10 @@ if ($resql) { if ($search_sale) { $param .= "&search_sale=".urlencode($search_sale); } - if ($search_categ) { + if ($search_categ > 0) { $param .= "&search_categ=".urlencode($search_categ); } - if ($search_warehouse_categ) { + if ($search_warehouse_categ > 0) { $param .= "&search_warehouse_categ=".urlencode($search_warehouse_categ); } /*if ($eatby) $param.="&eatby=".$eatby; @@ -332,12 +330,10 @@ if ($resql) { print '
'; print '
'; if (!empty($moreforfilter)) { - // print '
'; print $moreforfilter; $parameters = array(); $reshook = $hookmanager->executeHooks('printFieldPreListTitle', $parameters); // Note that $action and $object may have been modified by hook print $hookmanager->resPrint; - // print '
'; } From fdbef33d9c24e3127a694d6f43c84b1013a44c15 Mon Sep 17 00:00:00 2001 From: altairis-noe Date: Mon, 19 Jul 2021 11:22:41 +0200 Subject: [PATCH 03/42] indet linter --- htdocs/product/reassortlot.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/product/reassortlot.php b/htdocs/product/reassortlot.php index 751c47fd2d2..7558597057f 100644 --- a/htdocs/product/reassortlot.php +++ b/htdocs/product/reassortlot.php @@ -319,7 +319,7 @@ if ($resql) { $moreforfilter .= ''; - // Filter on warehouse categories + // Filter on warehouse categories $moreforfilter .= ''; $moreforfilter .= ''; From f1c14316937459ac26077a69b5effde90070bbbf Mon Sep 17 00:00:00 2001 From: kkhelifa Date: Thu, 17 Nov 2022 17:09:13 +0100 Subject: [PATCH 04/42] FIX: Set extrafield value of multiselect type --- htdocs/core/class/commonobject.class.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/htdocs/core/class/commonobject.class.php b/htdocs/core/class/commonobject.class.php index 27b00377411..95bf1085637 100644 --- a/htdocs/core/class/commonobject.class.php +++ b/htdocs/core/class/commonobject.class.php @@ -5988,6 +5988,12 @@ abstract class CommonObject dol_syslog('Error bad setup of extrafield', LOG_WARNING); } break; + case 'checkbox': + case 'chkbxlst': + if (is_array($this->array_options[$key])) { + $new_array_options[$key] = implode(',', $this->array_options[$key]); + } + break; } } @@ -6333,6 +6339,12 @@ abstract class CommonObject } break; */ + case 'checkbox': + case 'chkbxlst': + if (is_array($this->array_options[$key])) { + $new_array_options[$key] = implode(',', $this->array_options[$key]); + } + break; } $this->db->begin(); From 91f39fb4f412b10b964c7be3c38407bddf3bb8f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?No=C3=A9=20Courtier?= Date: Mon, 20 Mar 2023 11:36:28 +0100 Subject: [PATCH 05/42] FIX: Correct Evaluation for extrafields --- htdocs/core/class/commonobject.class.php | 6 +++--- htdocs/core/class/extrafields.class.php | 14 +++++++------- htdocs/core/tpl/extrafields_view.tpl.php | 6 +++--- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/htdocs/core/class/commonobject.class.php b/htdocs/core/class/commonobject.class.php index e7d2c61bd4c..9bda1575d37 100644 --- a/htdocs/core/class/commonobject.class.php +++ b/htdocs/core/class/commonobject.class.php @@ -8110,7 +8110,7 @@ abstract class CommonObject // Test on 'enabled' ('enabled' is different than 'list' = 'visibility') $enabled = 1; if ($enabled && isset($extrafields->attributes[$this->table_element]['enabled'][$key])) { - $enabled = dol_eval($extrafields->attributes[$this->table_element]['enabled'][$key], 1, 1, '1'); + $enabled = dol_eval($extrafields->attributes[$this->table_element]['enabled'][$key], 1, 1, '2'); } if (empty($enabled)) { continue; @@ -8118,12 +8118,12 @@ abstract class CommonObject $visibility = 1; if ($visibility && isset($extrafields->attributes[$this->table_element]['list'][$key])) { - $visibility = dol_eval($extrafields->attributes[$this->table_element]['list'][$key], 1, 1, '1'); + $visibility = dol_eval($extrafields->attributes[$this->table_element]['list'][$key], 1, 1, '2'); } $perms = 1; if ($perms && isset($extrafields->attributes[$this->table_element]['perms'][$key])) { - $perms = dol_eval($extrafields->attributes[$this->table_element]['perms'][$key], 1, 1, '1'); + $perms = dol_eval($extrafields->attributes[$this->table_element]['perms'][$key], 1, 1, '2'); } if (($mode == 'create') && abs($visibility) != 1 && abs($visibility) != 3) { diff --git a/htdocs/core/class/extrafields.class.php b/htdocs/core/class/extrafields.class.php index 5d91febc65d..a1150fb97fc 100644 --- a/htdocs/core/class/extrafields.class.php +++ b/htdocs/core/class/extrafields.class.php @@ -958,9 +958,9 @@ class ExtraFields $unique = $this->attributes[$extrafieldsobjectkey]['unique'][$key]; $required = $this->attributes[$extrafieldsobjectkey]['required'][$key]; $param = $this->attributes[$extrafieldsobjectkey]['param'][$key]; - $perms = dol_eval($this->attributes[$extrafieldsobjectkey]['perms'][$key], 1, 1, '1'); + $perms = dol_eval($this->attributes[$extrafieldsobjectkey]['perms'][$key], 1, 1, '2'); $langfile = $this->attributes[$extrafieldsobjectkey]['langfile'][$key]; - $list = dol_eval($this->attributes[$extrafieldsobjectkey]['list'][$key], 1, 1, '1'); + $list = dol_eval($this->attributes[$extrafieldsobjectkey]['list'][$key], 1, 1, '2'); $totalizable = $this->attributes[$extrafieldsobjectkey]['totalizable'][$key]; $help = $this->attributes[$extrafieldsobjectkey]['help'][$key]; $hidden = (empty($list) ? 1 : 0); // If empty, we are sure it is hidden, otherwise we show. If it depends on mode (view/create/edit form or list, this must be filtered by caller) @@ -1591,9 +1591,9 @@ class ExtraFields $unique = $this->attributes[$extrafieldsobjectkey]['unique'][$key]; $required = $this->attributes[$extrafieldsobjectkey]['required'][$key]; $param = $this->attributes[$extrafieldsobjectkey]['param'][$key]; - $perms = dol_eval($this->attributes[$extrafieldsobjectkey]['perms'][$key], 1, 1, '1'); + $perms = dol_eval($this->attributes[$extrafieldsobjectkey]['perms'][$key], 1, 1, '2'); $langfile = $this->attributes[$extrafieldsobjectkey]['langfile'][$key]; - $list = dol_eval($this->attributes[$extrafieldsobjectkey]['list'][$key], 1, 1, '1'); + $list = dol_eval($this->attributes[$extrafieldsobjectkey]['list'][$key], 1, 1, '2'); $help = $this->attributes[$extrafieldsobjectkey]['help'][$key]; $hidden = (empty($list) ? 1 : 0); // If $list empty, we are sure it is hidden, otherwise we show. If it depends on mode (view/create/edit form or list, this must be filtered by caller) @@ -2080,17 +2080,17 @@ class ExtraFields $enabled = 1; if (isset($this->attributes[$object->table_element]['enabled'][$key])) { // 'enabled' is often a condition on module enabled or not - $enabled = dol_eval($this->attributes[$object->table_element]['enabled'][$key], 1, 1, '1'); + $enabled = dol_eval($this->attributes[$object->table_element]['enabled'][$key], 1, 1, '2'); } $visibility = 1; if (isset($this->attributes[$object->table_element]['list'][$key])) { // 'list' is option for visibility - $visibility = intval(dol_eval($this->attributes[$object->table_element]['list'][$key], 1, 1, '1')); + $visibility = intval(dol_eval($this->attributes[$object->table_element]['list'][$key], 1, 1, '2')); } $perms = 1; if (isset($this->attributes[$object->table_element]['perms'][$key])) { - $perms = dol_eval($this->attributes[$object->table_element]['perms'][$key], 1, 1, '1'); + $perms = dol_eval($this->attributes[$object->table_element]['perms'][$key], 1, 1, '2'); } if (empty($enabled) || ( diff --git a/htdocs/core/tpl/extrafields_view.tpl.php b/htdocs/core/tpl/extrafields_view.tpl.php index 7d99a021f69..c75317471fb 100644 --- a/htdocs/core/tpl/extrafields_view.tpl.php +++ b/htdocs/core/tpl/extrafields_view.tpl.php @@ -72,15 +72,15 @@ if (empty($reshook) && isset($extrafields->attributes[$object->table_element]['l $enabled = 1; if ($enabled && isset($extrafields->attributes[$object->table_element]['enabled'][$tmpkeyextra])) { - $enabled = dol_eval($extrafields->attributes[$object->table_element]['enabled'][$tmpkeyextra], 1); + $enabled = dol_eval($extrafields->attributes[$object->table_element]['enabled'][$tmpkeyextra], 1, 1, '2'); } if ($enabled && isset($extrafields->attributes[$object->table_element]['list'][$tmpkeyextra])) { - $enabled = dol_eval($extrafields->attributes[$object->table_element]['list'][$tmpkeyextra], 1); + $enabled = dol_eval($extrafields->attributes[$object->table_element]['list'][$tmpkeyextra], 1, 1, '2'); } $perms = 1; if ($perms && isset($extrafields->attributes[$object->table_element]['perms'][$tmpkeyextra])) { - $perms = dol_eval($extrafields->attributes[$object->table_element]['perms'][$tmpkeyextra], 1); + $perms = dol_eval($extrafields->attributes[$object->table_element]['perms'][$tmpkeyextra], 1, 1, '2'); } //print $tmpkeyextra.'-'.$enabled.'-'.$perms.'
'."\n"; From 620cbf398ab1790f489ff2fadfa5d68ff1b9fcdb Mon Sep 17 00:00:00 2001 From: kkhelifa Date: Thu, 30 Mar 2023 11:10:22 +0200 Subject: [PATCH 06/42] Correction --- htdocs/core/class/commonobject.class.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/htdocs/core/class/commonobject.class.php b/htdocs/core/class/commonobject.class.php index 95bf1085637..6380730816f 100644 --- a/htdocs/core/class/commonobject.class.php +++ b/htdocs/core/class/commonobject.class.php @@ -5992,6 +5992,8 @@ abstract class CommonObject case 'chkbxlst': if (is_array($this->array_options[$key])) { $new_array_options[$key] = implode(',', $this->array_options[$key]); + } else { + $new_array_options[$key] = $this->array_options[$key]; } break; } @@ -6343,6 +6345,8 @@ abstract class CommonObject case 'chkbxlst': if (is_array($this->array_options[$key])) { $new_array_options[$key] = implode(',', $this->array_options[$key]); + } else { + $new_array_options[$key] = $this->array_options[$key]; } break; } From 99d121232fb2d21ccb20e01581cc0dad81f7fe49 Mon Sep 17 00:00:00 2001 From: Regis Houssin Date: Sat, 8 Apr 2023 15:08:37 +0200 Subject: [PATCH 07/42] FIX missing mrp module dependency --- htdocs/core/modules/modMrp.class.php | 2 +- htdocs/core/modules/modWorkstation.class.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/htdocs/core/modules/modMrp.class.php b/htdocs/core/modules/modMrp.class.php index 58e7791845a..593a5611de0 100644 --- a/htdocs/core/modules/modMrp.class.php +++ b/htdocs/core/modules/modMrp.class.php @@ -119,7 +119,7 @@ class modMrp extends DolibarrModules $this->hidden = false; // List of module class names as string that must be enabled if this module is enabled. Example: array('always1'=>'modModuleToEnable1','always2'=>'modModuleToEnable2', 'FR1'=>'modModuleToEnableFR'...) $this->depends = array('modBom'); - $this->requiredby = array(); // List of module class names as string to disable if this one is disabled. Example: array('modModuleToDisable1', ...) + $this->requiredby = array('modWorkstation'); // List of module class names as string to disable if this one is disabled. Example: array('modModuleToDisable1', ...) $this->conflictwith = array(); // List of module class names as string this module is in conflict with. Example: array('modModuleToDisable1', ...) $this->langfiles = array("mrp"); $this->phpmin = array(7, 0); // Minimum version of PHP required by module diff --git a/htdocs/core/modules/modWorkstation.class.php b/htdocs/core/modules/modWorkstation.class.php index a8be0f23226..6bba7b1807a 100644 --- a/htdocs/core/modules/modWorkstation.class.php +++ b/htdocs/core/modules/modWorkstation.class.php @@ -120,7 +120,7 @@ class modWorkstation extends DolibarrModules // A condition to hide module $this->hidden = false; // List of module class names as string that must be enabled if this module is enabled. Example: array('always1'=>'modModuleToEnable1','always2'=>'modModuleToEnable2', 'FR1'=>'modModuleToEnableFR'...) - $this->depends = array(); + $this->depends = array('modMrp'); $this->requiredby = array(); // List of module class names as string to disable if this one is disabled. Example: array('modModuleToDisable1', ...) $this->conflictwith = array(); // List of module class names as string this module is in conflict with. Example: array('modModuleToDisable1', ...) $this->langfiles = array("mrp"); From fa8b6ecd552bf6bd4e42c2a324169ef1c85e655f Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sat, 8 Apr 2023 16:53:18 +0200 Subject: [PATCH 08/42] css --- htdocs/projet/class/project.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/projet/class/project.class.php b/htdocs/projet/class/project.class.php index 701d43d24e8..582e062f68a 100644 --- a/htdocs/projet/class/project.class.php +++ b/htdocs/projet/class/project.class.php @@ -287,7 +287,7 @@ class Project extends CommonObject 'date_close' =>array('type'=>'datetime', 'label'=>'DateClosing', 'enabled'=>1, 'visible'=>0, 'position'=>105), 'fk_user_close' =>array('type'=>'integer', 'label'=>'UserClosing', 'enabled'=>1, 'visible'=>0, 'position'=>110), 'opp_amount' =>array('type'=>'double(24,8)', 'label'=>'OpportunityAmountShort', 'enabled'=>1, 'visible'=>'getDolGlobalString("PROJECT_USE_OPPORTUNITIES")', 'position'=>115), - 'budget_amount' =>array('type'=>'double(24,8)', 'label'=>'Budget', 'enabled'=>1, 'visible'=>1, 'position'=>119), + 'budget_amount' =>array('type'=>'double(24,8)', 'label'=>'Budget', 'enabled'=>1, 'visible'=>-1, 'position'=>119), 'usage_bill_time' =>array('type'=>'integer', 'label'=>'UsageBillTimeShort', 'enabled'=>1, 'visible'=>-1, 'position'=>130), 'usage_opportunity' =>array('type'=>'integer', 'label'=>'UsageOpportunity', 'enabled'=>1, 'visible'=>-1, 'position'=>135), 'usage_task' =>array('type'=>'integer', 'label'=>'UsageTasks', 'enabled'=>1, 'visible'=>-1, 'position'=>140), From 6e93503898950038d4bf44223cc123e4bf23d5f2 Mon Sep 17 00:00:00 2001 From: Juanjo Menent Date: Sun, 9 Apr 2023 14:18:31 +0200 Subject: [PATCH 09/42] Fix #24355 Update price with discount --- htdocs/contrat/card.php | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/htdocs/contrat/card.php b/htdocs/contrat/card.php index 4e52941063d..7839d8ac241 100644 --- a/htdocs/contrat/card.php +++ b/htdocs/contrat/card.php @@ -744,12 +744,22 @@ if (empty($reshook)) { $fk_unit = GETPOST('unit', 'alpha'); + // update price_ht with discount + // TODO Use object->updateline instead objedtline->update + + $price_ht = price2num(GETPOST('elprice'), 'MU'); + $remise_percent = price2num(GETPOST('elremise_percent'), 2); + if ($remise_percent > 0) { + $remise = round(($price_ht * $remise_percent / 100), 2); + $price_ht = ($price_ht - $remise); + } + $objectline->fk_product = GETPOST('idprod', 'int'); $objectline->description = GETPOST('product_desc', 'restricthtml'); - $objectline->price_ht = price2num(GETPOST('elprice'), 'MU'); + $objectline->price_ht = $price_ht; $objectline->subprice = price2num(GETPOST('elprice'), 'MU'); $objectline->qty = price2num(GETPOST('elqty'), 'MS'); - $objectline->remise_percent = price2num(GETPOST('elremise_percent'), 2); + $objectline->remise_percent = $remise_percent; $objectline->tva_tx = ($txtva ? $txtva : 0); // Field may be disabled, so we use vat rate 0 $objectline->vat_src_code = $vat_src_code; $objectline->localtax1_tx = is_numeric($localtax1_tx) ? $localtax1_tx : 0; From 976c2d76f260d73cd5508fe837e487675228d156 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sun, 9 Apr 2023 22:10:56 +0200 Subject: [PATCH 10/42] Try fix MS OAuth2 Imap (from feedback at dolibarr.org/forum/t/emailcollectors-with-microsoft-365-oauth2) --- htdocs/core/lib/oauth.lib.php | 2 +- htdocs/includes/OAuth/OAuth2/Service/Microsoft.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/htdocs/core/lib/oauth.lib.php b/htdocs/core/lib/oauth.lib.php index 8a81d9a80e5..aaac40a525f 100644 --- a/htdocs/core/lib/oauth.lib.php +++ b/htdocs/core/lib/oauth.lib.php @@ -32,7 +32,7 @@ if (isModEnabled('stripe')) { $supportedoauth2array['OAUTH_STRIPE_LIVE_NAME'] = array('callbackfile' => 'stripelive', 'picto' => 'stripe', 'urlforapp' => '', 'name'=>'StripeLive', 'urlforcredentials'=>'', 'availablescopes'=>'read_write', 'returnurl'=>'/core/modules/oauth/stripelive_oauthcallback.php'); } $supportedoauth2array['OAUTH_GITHUB_NAME'] = array('callbackfile' => 'github', 'picto' => 'github', 'urlforapp' => 'OAUTH_GITHUB_DESC', 'name'=>'GitHub', 'urlforcredentials'=>'https://github.com/settings/developers', 'availablescopes'=>'user,public_repo', 'returnurl'=>'/core/modules/oauth/github_oauthcallback.php'); -$supportedoauth2array['OAUTH_MICROSOFT_NAME'] = array('callbackfile' => 'microsoft', 'picto' => 'microsoft', 'urlforapp' => 'OAUTH_MICROSOFT_DESC', 'name'=>'Microsoft', 'urlforcredentials'=>'https://portal.azure.com/', 'availablescopes'=>'openid,offline_access,profile,email,User.Read,https://outlook.office365.com/IMAP.AccessAsUser.All,https://outlook.office365.com/SMTP.Send', 'returnurl'=>'/core/modules/oauth/microsoft_oauthcallback.php'); +$supportedoauth2array['OAUTH_MICROSOFT_NAME'] = array('callbackfile' => 'microsoft', 'picto' => 'microsoft', 'urlforapp' => 'OAUTH_MICROSOFT_DESC', 'name'=>'Microsoft', 'urlforcredentials'=>'https://portal.azure.com/', 'availablescopes'=>'openid,offline_access,profile,email,User.Read,https://outlook.office.com/IMAP.AccessAsUser.All,https://outlook.office.com/SMTP.Send', 'returnurl'=>'/core/modules/oauth/microsoft_oauthcallback.php'); if (getDolGlobalInt('MAIN_FEATURES_LEVEL') >= 2) { $supportedoauth2array['OAUTH_OTHER_NAME'] = array('callbackfile' => 'generic', 'picto' => 'generic', 'urlforapp' => 'OAUTH_OTHER_DESC', 'name'=>'Other', 'urlforcredentials'=>'', 'availablescopes'=>'Standard', 'returnurl'=>'/core/modules/oauth/generic_oauthcallback.php'); // See https://learn.microsoft.com/fr-fr/azure/active-directory/develop/quickstart-register-app#register-an-application diff --git a/htdocs/includes/OAuth/OAuth2/Service/Microsoft.php b/htdocs/includes/OAuth/OAuth2/Service/Microsoft.php index 6c3b18b3c0f..90c20405e6b 100644 --- a/htdocs/includes/OAuth/OAuth2/Service/Microsoft.php +++ b/htdocs/includes/OAuth/OAuth2/Service/Microsoft.php @@ -38,8 +38,8 @@ class Microsoft extends AbstractService const SCOPE_APPLICATIONS = 'applications'; const SCOPE_APPLICATIONS_CREATE = 'applications_create'; const SCOPE_IMAP = 'imap'; - const SOCPE_IMAP_ACCESSASUSERALL = 'https://outlook.office365.com/IMAP.AccessAsUser.All'; - const SOCPE_SMTPSEND = 'https://outlook.office365.com/SMTP.Send'; + const SOCPE_IMAP_ACCESSASUSERALL = 'https://outlook.office.com/IMAP.AccessAsUser.All'; + const SOCPE_SMTPSEND = 'https://outlook.office.com/SMTP.Send'; const SOCPE_USERREAD = 'User.Read'; const SOCPE_MAILREAD = 'Mail.Read'; const SOCPE_MAILSEND = 'Mail.Send'; From df27824249bb8e9d026f487707f9adb02eaf8e49 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Mon, 10 Apr 2023 10:58:49 +0200 Subject: [PATCH 11/42] css --- htdocs/expensereport/card.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/htdocs/expensereport/card.php b/htdocs/expensereport/card.php index b7a6cb662d6..1f7326ed40a 100644 --- a/htdocs/expensereport/card.php +++ b/htdocs/expensereport/card.php @@ -2025,6 +2025,7 @@ if ($action == 'create') { print ''; print ''; print ''; + print ''; print '
'; print '
'.img_picto($langs->trans('Categories'), 'category', 'class="pictofixedwidth"'); $moreforfilter .= $htmlother->select_categories(Categorie::TYPE_PRODUCT, $search_categ, 'search_categ').''.img_picto($langs->trans('Categories'), 'category', 'class="pictofixedwidth"'); $moreforfilter .= $htmlother->select_categories(Categorie::TYPE_WAREHOUSE, $search_warehouse_categ, 'search_warehouse_categ').'
'; @@ -2043,7 +2044,7 @@ if ($action == 'create') { if (!empty($conf->global->MAIN_USE_EXPENSE_IK)) { print ''; } - print ''; + print ''; print ''; print ''; print ''; @@ -2082,7 +2083,7 @@ if ($action == 'create') { // Project if (isModEnabled('project')) { - print ''; } - print ''; + print ''; print ''; print ''; print ''; @@ -2554,7 +2555,7 @@ if ($action == 'create') { } print ''; print ''; From 3f3e8a84ff552c921c2f427b937437efa45a8d90 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Mon, 10 Apr 2023 11:23:18 +0200 Subject: [PATCH 12/42] FIX Report of date of task suggested only if there is tasks --- htdocs/projet/card.php | 16 ++++++++++++---- htdocs/projet/class/project.class.php | 6 +++--- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/htdocs/projet/card.php b/htdocs/projet/card.php index 7fddc12b7c9..3b5a0175d03 100644 --- a/htdocs/projet/card.php +++ b/htdocs/projet/card.php @@ -1153,11 +1153,14 @@ if ($action == 'create' && $user->rights->projet->creer) { print $form->selectDate($object->date_start ? $object->date_start : -1, 'projectstart', 0, 0, 0, '', 1, 0); print ' '.$langs->trans("to").' '; print $form->selectDate($object->date_end ? $object->date_end : -1, 'projectend', 0, 0, 0, '', 1, 0); - print '     getLinesArray(null, 0); + if (!empty($object->usage_task) && !empty($object->lines)) { + print ' '; } - print '/>'; print ''; if (isModEnabled('eventorganization')) { @@ -1379,6 +1382,11 @@ if ($action == 'create' && $user->rights->projet->creer) { jQuery("#usage_task").prop("checked", true); } }); + + jQuery("#projectstart").change(function() { + console.log("We modify the start date"); + jQuery("#divreportdate").show(); + }); }); '; diff --git a/htdocs/projet/class/project.class.php b/htdocs/projet/class/project.class.php index 582e062f68a..1e4c6862c58 100644 --- a/htdocs/projet/class/project.class.php +++ b/htdocs/projet/class/project.class.php @@ -2305,11 +2305,11 @@ class Project extends CommonObject /** - * Create an array of tasks of current project + * Create an array of tasks of current project * - * @param User $user Object user we want project allowed to + * @param User $user Object user we want project allowed to * @param int $loadRoleMode 1= will test Roles on task; 0 used in delete project action - * @return int >0 if OK, <0 if KO + * @return int >0 if OK, <0 if KO */ public function getLinesArray($user, $loadRoleMode = 1) { From eb187052e8badbcc73694cab3d00f7a0d9dd9821 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Mon, 10 Apr 2023 11:44:19 +0200 Subject: [PATCH 13/42] Fix column alignment/balance --- htdocs/core/class/html.formprojet.class.php | 2 +- htdocs/projet/tasks/time.php | 67 +++++++++++++++++---- 2 files changed, 55 insertions(+), 14 deletions(-) diff --git a/htdocs/core/class/html.formprojet.class.php b/htdocs/core/class/html.formprojet.class.php index e05931f437d..dfbc2aa65a1 100644 --- a/htdocs/core/class/html.formprojet.class.php +++ b/htdocs/core/class/html.formprojet.class.php @@ -378,7 +378,7 @@ class FormProjets include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php'; $comboenhancement = ajax_combobox($htmlname, '', 0, $forcefocus); $out .= $comboenhancement; - $morecss .= ' minwidth200 maxwidth500'; + $morecss .= ' minwidth150'; } if (empty($option_only)) { diff --git a/htdocs/projet/tasks/time.php b/htdocs/projet/tasks/time.php index b98e9298732..3f1413ea3f2 100644 --- a/htdocs/projet/tasks/time.php +++ b/htdocs/projet/tasks/time.php @@ -1750,7 +1750,7 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser if ($projectstatic->public) { $contactsofproject = array(); } - print $form->select_dolusers((GETPOST('userid', 'int') ? GETPOST('userid', 'int') : $userid), 'userid', 0, '', 0, '', $contactsofproject, 0, 0, 0, '', 0, $langs->trans("ResourceNotAssignedToProject"), 'maxwidth250'); + print $form->select_dolusers((GETPOST('userid', 'int') ? GETPOST('userid', 'int') : $userid), 'userid', 0, '', 0, '', $contactsofproject, 0, 0, 0, '', 0, $langs->trans("ResourceNotAssignedToProject"), 'maxwidth200'); } else { if ($nboftasks) { print img_error($langs->trans('FirstAddRessourceToAllocateTime')).' '.$langs->trans('FirstAddRessourceToAllocateTime'); @@ -1783,8 +1783,9 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser print ''; if ($conf->service->enabled && $projectstatic->thirdparty->id > 0 && $projectstatic->usage_bill_time) { - print ''; } } @@ -1796,7 +1797,7 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser print ''; @@ -2004,7 +2005,7 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser // Thirdparty if (!empty($arrayfields['p.fk_soc']['checked'])) { - print ''; + if (!$i) { + $totalarray['nbfield']++; + } } // Value spent @@ -2270,9 +2274,9 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser print ''; // Date @@ -2328,6 +2333,18 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser print ''; } + // Thirdparty + if (!empty($arrayfields['p.fk_soc']['checked'])) { + print ''; + } + + // Thirdparty alias + if (!empty($arrayfields['s.name_alias']['checked'])) { + print ''; + } + // Project ref if (!empty($allprojectforuser)) { if ((empty($id) && empty($ref)) || !empty($projectidforalltimes)) { // Not a dedicated task @@ -2359,7 +2376,7 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser // User if (!empty($arrayfields['author']['checked'])) { - print ''; } + // Product + if (!empty($arrayfields['t.fk_product']['checked'])) { + print ''; + } + // Value spent if (!empty($arrayfields['value']['checked'])) { print ''; + print ''; // Date if (!empty($arrayfields['t.task_date']['checked'])) { @@ -2466,6 +2489,18 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser print ''; } + // Thirdparty + if (!empty($arrayfields['p.fk_soc']['checked'])) { + print ''; + } + + // Thirdparty alias + if (!empty($arrayfields['s.name_alias']['checked'])) { + print ''; + } + // Project ref if (!empty($allprojectforuser)) { if ((empty($id) && empty($ref)) || !empty($projectidforalltimes)) { // Not a dedicated task @@ -2490,14 +2525,14 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser if (!empty($arrayfields['t.task_label']['checked'])) { if ((empty($id) && empty($ref)) || !empty($projectidforalltimes)) { // Not a dedicated task print ''; } } // User if (!empty($arrayfields['author']['checked'])) { - print ''; } + // Product + if (!empty($arrayfields['t.fk_product']['checked'])) { + print ''; + } + // Value spent if (!empty($arrayfields['value']['checked'])) { print ''; From 6143a1336b7c97bab8d49411a5d9591a608ef72b Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Mon, 10 Apr 2023 12:31:31 +0200 Subject: [PATCH 15/42] Look and feel v17 --- htdocs/admin/stock.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/htdocs/admin/stock.php b/htdocs/admin/stock.php index 8aaf76d310b..ac516b00d8e 100644 --- a/htdocs/admin/stock.php +++ b/htdocs/admin/stock.php @@ -217,7 +217,7 @@ if (isModEnabled('facture')) { print $form->selectarray("STOCK_CALCULATE_ON_BILL", $arrval, $conf->global->STOCK_CALCULATE_ON_BILL); } } else { - print $langs->trans("ModuleMustBeEnabledFirst", $langs->transnoentitiesnoconv("Module30Name")); + print ''.$langs->trans("ModuleMustBeEnabledFirst", $langs->transnoentitiesnoconv("Module30Name")).''; } print "\n\n"; $found++; @@ -234,7 +234,7 @@ if (isModEnabled('commande')) { print $form->selectarray("STOCK_CALCULATE_ON_VALIDATE_ORDER", $arrval, $conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER); } } else { - print $langs->trans("ModuleMustBeEnabledFirst", $langs->transnoentitiesnoconv("Module25Name")); + print ''.$langs->trans("ModuleMustBeEnabledFirst", $langs->transnoentitiesnoconv("Module25Name")).''; } print "\n\n"; $found++; @@ -253,7 +253,7 @@ if (isModEnabled("expedition")) { print $form->selectarray("STOCK_CALCULATE_ON_SHIPMENT", $arrval, $conf->global->STOCK_CALCULATE_ON_SHIPMENT); } } else { - print $langs->trans("ModuleMustBeEnabledFirst", $langs->transnoentitiesnoconv("Module80Name")); + print ''.$langs->trans("ModuleMustBeEnabledFirst", $langs->transnoentitiesnoconv("Module80Name")).''; } print "\n\n"; $found++; @@ -270,7 +270,7 @@ if (isModEnabled("expedition")) { print $form->selectarray("STOCK_CALCULATE_ON_SHIPMENT_CLOSE", $arrval, $conf->global->STOCK_CALCULATE_ON_SHIPMENT_CLOSE); } } else { - print $langs->trans("ModuleMustBeEnabledFirst", $langs->transnoentitiesnoconv("Module80Name")); + print ''.$langs->trans("ModuleMustBeEnabledFirst", $langs->transnoentitiesnoconv("Module80Name")).''; } print "\n\n"; $found++; @@ -301,7 +301,7 @@ if ((isModEnabled("fournisseur") && empty($conf->global->MAIN_USE_NEW_SUPPLIERMO print $form->selectarray("STOCK_CALCULATE_ON_SUPPLIER_BILL", $arrval, $conf->global->STOCK_CALCULATE_ON_SUPPLIER_BILL); } } else { - print $langs->trans("ModuleMustBeEnabledFirst", $langs->transnoentitiesnoconv("Module40Name")); + print ''.$langs->trans("ModuleMustBeEnabledFirst", $langs->transnoentitiesnoconv("Module40Name")).''; } print "\n\n"; $found++; @@ -319,7 +319,7 @@ if ((isModEnabled("fournisseur") && empty($conf->global->MAIN_USE_NEW_SUPPLIERMO print $form->selectarray("STOCK_CALCULATE_ON_SUPPLIER_VALIDATE_ORDER", $arrval, $conf->global->STOCK_CALCULATE_ON_SUPPLIER_VALIDATE_ORDER); } } else { - print $langs->trans("ModuleMustBeEnabledFirst", $langs->transnoentitiesnoconv("Module40Name")); + print ''.$langs->trans("ModuleMustBeEnabledFirst", $langs->transnoentitiesnoconv("Module40Name")).''; } print "\n\n"; $found++; @@ -364,7 +364,7 @@ if (isModEnabled("reception")) { print $form->selectarray("STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER", $arrval, $conf->global->STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER); } } else { - print $langs->trans("ModuleMustBeEnabledFirst", $langs->transnoentitiesnoconv("Module40Name")); + print ''.$langs->trans("ModuleMustBeEnabledFirst", $langs->transnoentitiesnoconv("Module40Name")).''; } print "\n\n"; $found++; From 9283b7af7497525723f31ed35d36f5a69412a72c Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Mon, 10 Apr 2023 13:03:09 +0200 Subject: [PATCH 16/42] Fix better setup tooltip for shipment/reception module --- htdocs/admin/stock.php | 17 +++++++++++++---- htdocs/core/class/conf.class.php | 9 +++++---- htdocs/core/modules/modExpedition.class.php | 6 ++++-- htdocs/core/modules/modReception.class.php | 6 ++++-- htdocs/langs/en_US/productbatch.lang | 2 +- 5 files changed, 27 insertions(+), 13 deletions(-) diff --git a/htdocs/admin/stock.php b/htdocs/admin/stock.php index 7a7e49a3129..2cd4985fdb1 100644 --- a/htdocs/admin/stock.php +++ b/htdocs/admin/stock.php @@ -181,16 +181,25 @@ $formproduct = new FormProduct($db); $disabled = ''; if (isModEnabled('productbatch')) { + // If module lot/serial enabled, we force the inc/dec mode to STOCK_CALCULATE_ON_SHIPMENT_CLOSE and STOCK_CALCULATE_ON_RECEPTION_CLOSE $langs->load("productbatch"); $disabled = ' disabled'; - print info_admin($langs->trans("WhenProductBatchModuleOnOptionAreForced")); + + // STOCK_CALCULATE_ON_SHIPMENT_CLOSE + $descmode = $langs->trans('DeStockOnShipmentOnClosing'); + if (!isModEnabled('reception')) { + // STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER + $incmode = $langs->trans('ReStockOnDispatchOrder'); + } else { + // STOCK_CALCULATE_ON_RECEPTION_CLOSE + $incmode = $langs->trans('StockOnReceptionOnClosing'); + } + print info_admin($langs->trans("WhenProductBatchModuleOnOptionAreForced", $descmode, $incmode)); } -//if (!empty($conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER) || !empty($conf->global->STOCK_CALCULATE_ON_SHIPMENT)) -//{ + print info_admin($langs->trans("IfYouUsePointOfSaleCheckModule")); print '
'; -//} print '
'; diff --git a/htdocs/core/class/conf.class.php b/htdocs/core/class/conf.class.php index 589d28453fc..756b2771e39 100644 --- a/htdocs/core/class/conf.class.php +++ b/htdocs/core/class/conf.class.php @@ -635,18 +635,19 @@ class Conf unset($this->global->PROJECT_USE_SEARCH_TO_SELECT); } - if (!empty($this->productbatch->enabled)) { + if (isModEnabled('productbatch')) { + // If module lot/serial enabled, we force the inc/dec mode to STOCK_CALCULATE_ON_SHIPMENT_CLOSE and STOCK_CALCULATE_ON_RECEPTION_CLOSE $this->global->STOCK_CALCULATE_ON_BILL = 0; $this->global->STOCK_CALCULATE_ON_VALIDATE_ORDER = 0; - if (empty($this->global->STOCK_CALCULATE_ON_SHIPMENT_CLOSE)) $this->global->STOCK_CALCULATE_ON_SHIPMENT = 1; if (empty($this->global->STOCK_CALCULATE_ON_SHIPMENT)) $this->global->STOCK_CALCULATE_ON_SHIPMENT_CLOSE = 1; + if (empty($this->global->STOCK_CALCULATE_ON_SHIPMENT_CLOSE)) $this->global->STOCK_CALCULATE_ON_SHIPMENT = 1; $this->global->STOCK_CALCULATE_ON_SUPPLIER_BILL = 0; $this->global->STOCK_CALCULATE_ON_SUPPLIER_VALIDATE_ORDER = 0; - if (empty($this->reception->enabled)) { + if (!isModEnabled('reception')) { $this->global->STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER = 1; } else { - if (empty($this->global->STOCK_CALCULATE_ON_RECEPTION_CLOSE)) $this->global->STOCK_CALCULATE_ON_RECEPTION = 1; if (empty($this->global->STOCK_CALCULATE_ON_RECEPTION)) $this->global->STOCK_CALCULATE_ON_RECEPTION_CLOSE = 1; + if (empty($this->global->STOCK_CALCULATE_ON_RECEPTION_CLOSE)) $this->global->STOCK_CALCULATE_ON_RECEPTION = 1; } } diff --git a/htdocs/core/modules/modExpedition.class.php b/htdocs/core/modules/modExpedition.class.php index 2ff117404ee..590e5b6b488 100644 --- a/htdocs/core/modules/modExpedition.class.php +++ b/htdocs/core/modules/modExpedition.class.php @@ -360,9 +360,11 @@ class modExpedition extends DolibarrModules $sql = array( "DELETE FROM ".MAIN_DB_PREFIX."document_model WHERE nom = '".$this->db->escape($this->const[0][2])."' AND type = 'shipping' AND entity = ".((int) $conf->entity), - "INSERT INTO ".MAIN_DB_PREFIX."document_model (nom, type, entity) VALUES('".$this->db->escape($this->const[0][2])."','shipping',".((int) $conf->entity).")", + "INSERT INTO ".MAIN_DB_PREFIX."document_model (nom, type, entity) VALUES('".$this->db->escape($this->const[0][2])."', 'shipping', ".((int) $conf->entity).")", "DELETE FROM ".MAIN_DB_PREFIX."document_model WHERE nom = '".$this->db->escape($this->const[3][2])."' AND type = 'delivery' AND entity = ".((int) $conf->entity), - "INSERT INTO ".MAIN_DB_PREFIX."document_model (nom, type, entity) VALUES('".$this->db->escape($this->const[3][2])."','delivery',".((int) $conf->entity).")", + "INSERT INTO ".MAIN_DB_PREFIX."document_model (nom, type, entity) VALUES('".$this->db->escape($this->const[3][2])."', 'delivery', ".((int) $conf->entity).")", + //"DELETE FROM ".MAIN_DB_PREFIX."const WHERE name IN ('STOCK_CALCULATE_ON_BILL', 'STOCK_CALCULATE_ON_VALIDATE_ORDER', 'STOCK_CALCULATE_ON_SHIPMENT', 'STOCK_CALCULATE_ON_SHIPMENT_CLOSE') AND entity = ".((int) $conf->entity), + //"INSERT INTO ".MAIN_DB_PREFIX."const (name, value, entity) VALUES ('STOCK_CALCULATE_ON_SHIPMENT_CLOSE', 1, ".((int) $conf->entity).")" ); return $this->_init($sql, $options); diff --git a/htdocs/core/modules/modReception.class.php b/htdocs/core/modules/modReception.class.php index 6e7cba2b053..d6341e6279d 100644 --- a/htdocs/core/modules/modReception.class.php +++ b/htdocs/core/modules/modReception.class.php @@ -282,8 +282,10 @@ class modReception extends DolibarrModules $sql = array(); $sql = array( - "DELETE FROM ".MAIN_DB_PREFIX."document_model WHERE nom = '".$this->db->escape($this->const[0][2])."' AND type = 'reception' AND entity = ".((int) $conf->entity), - "INSERT INTO ".MAIN_DB_PREFIX."document_model (nom, type, entity) VALUES('".$this->db->escape($this->const[0][2])."','reception',".((int) $conf->entity).")", + "DELETE FROM ".MAIN_DB_PREFIX."document_model WHERE nom = '".$this->db->escape($this->const[0][2])."' AND type = 'reception' AND entity = ".((int) $conf->entity), + "INSERT INTO ".MAIN_DB_PREFIX."document_model (nom, type, entity) VALUES('".$this->db->escape($this->const[0][2])."','reception',".((int) $conf->entity).")", + //"DELETE FROM ".MAIN_DB_PREFIX."const WHERE name IN ('STOCK_CALCULATE_ON_SUPPLIER_BILL', 'STOCK_CALCULATE_ON_SUPPLIER_VALIDATE_ORDER', 'STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER', 'STOCK_CALCULATE_ON_RECEPTION', 'STOCK_CALCULATE_ON_RECEPTION_CLOSE') AND entity = ".((int) $conf->entity), + //"INSERT INTO ".MAIN_DB_PREFIX."const (name, value, entity) VALUES ('STOCK_CALCULATE_ON_RECEPTION_CLOSE', 1, ".((int) $conf->entity).")" ); return $this->_init($sql, $options); diff --git a/htdocs/langs/en_US/productbatch.lang b/htdocs/langs/en_US/productbatch.lang index a1039e05e62..c51d94b8acd 100644 --- a/htdocs/langs/en_US/productbatch.lang +++ b/htdocs/langs/en_US/productbatch.lang @@ -19,7 +19,7 @@ printSellby=Sell-by: %s printQty=Qty: %d printPlannedWarehouse=Warehouse: %s AddDispatchBatchLine=Add a line for Shelf Life dispatching -WhenProductBatchModuleOnOptionAreForced=When module Lot/Serial is on, automatic stock decrease is forced to 'Decrease real stocks on shipping validation' and automatic increase mode is forced to 'Increase real stocks on manual dispatching into warehouses' and can't be edited. Other options can be defined as you want. +WhenProductBatchModuleOnOptionAreForced=When module Lot/Serial is on, automatic stock decrease is forced to '%s' and automatic increase mode is forced to '%s'. Some choices may be not available. Other options can be defined as you want. ProductDoesNotUseBatchSerial=This product does not use lot/serial number ProductLotSetup=Setup of module lot/serial ShowCurrentStockOfLot=Show current stock for couple product/lot From 569320424b1e3570f24509f3030475e412bebb8b Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Mon, 10 Apr 2023 13:59:19 +0200 Subject: [PATCH 17/42] Fix sql error --- htdocs/expedition/class/expedition.class.php | 8 ++++---- htdocs/expedition/shipment.php | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/htdocs/expedition/class/expedition.class.php b/htdocs/expedition/class/expedition.class.php index 0f7bedc19d3..8a5a84dbb78 100644 --- a/htdocs/expedition/class/expedition.class.php +++ b/htdocs/expedition/class/expedition.class.php @@ -2209,8 +2209,8 @@ class Expedition extends CommonObject $sql = "SELECT cd.fk_product, cd.subprice,"; $sql .= " ed.rowid, ed.qty, ed.fk_entrepot,"; $sql .= " e.ref,"; - $sql .= " edb.rowid as edbrowid, edb.eatby, edb.sellby, edb.batch, edb.qty as edbqty, edb.fk_origin_stock"; - $sql .= " ,cd.rowid as cdid, ed.rowid as edid"; + $sql .= " edb.rowid as edbrowid, edb.eatby, edb.sellby, edb.batch, edb.qty as edbqty, edb.fk_origin_stock,"; + $sql .= " cd.rowid as cdid, ed.rowid as edid"; $sql .= " FROM " . MAIN_DB_PREFIX . "commandedet as cd,"; $sql .= " " . MAIN_DB_PREFIX . "expeditiondet as ed"; $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "expeditiondet_batch as edb on edb.fk_expeditiondet = ed.rowid"; @@ -2264,8 +2264,8 @@ class Expedition extends CommonObject // If some stock lines are now 0, we can remove entry into llx_product_stock, but only if there is no child lines into llx_product_batch (detail of batch, because we can imagine // having a lot1/qty=X and lot2/qty=-X, so 0 but we must not loose repartition of different lot. - $sql = "DELETE FROM ".MAIN_DB_PREFIX."product_stock WHERE reel = 0 AND rowid NOT IN (SELECT fk_product_stock FROM ".MAIN_DB_PREFIX."product_batch as pb)"; - $resql = $this->db->query($sql); + $sqldelete = "DELETE FROM ".MAIN_DB_PREFIX."product_stock WHERE reel = 0 AND rowid NOT IN (SELECT fk_product_stock FROM ".MAIN_DB_PREFIX."product_batch as pb)"; + $resqldelete = $this->db->query($sqldelete); // We do not test error, it can fails if there is child in batch details } } else { diff --git a/htdocs/expedition/shipment.php b/htdocs/expedition/shipment.php index 325559882d8..0470da549e5 100644 --- a/htdocs/expedition/shipment.php +++ b/htdocs/expedition/shipment.php @@ -773,7 +773,7 @@ if ($id > 0 || !empty($ref)) { $toBeShippedTotal += $toBeShipped[$objp->fk_product]; print $toBeShipped[$objp->fk_product]; } else { - print '0 ('.$langs->trans("Service").')'; + print '0 ('.$langs->trans("Service").')'; } print ($objp->unit_order ? ' '.$objp->unit_order : '').''; From f26b5b58c907ccdd87ab5ecfa33c6b66947ed6d6 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Mon, 10 Apr 2023 14:27:39 +0200 Subject: [PATCH 18/42] Fix error management --- htdocs/expedition/card.php | 3 +++ htdocs/expedition/class/expedition.class.php | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/htdocs/expedition/card.php b/htdocs/expedition/card.php index dcc50eff7dc..ec615119c67 100644 --- a/htdocs/expedition/card.php +++ b/htdocs/expedition/card.php @@ -161,6 +161,9 @@ if (empty($reshook)) { if ($action == 'reopen' && $user->rights->expedition->creer) { $object->fetch($id); $result = $object->reOpen(); + if ($result < 0) { + setEventMessages($object->error, $object->errors, 'errors'); + } } // Set incoterm diff --git a/htdocs/expedition/class/expedition.class.php b/htdocs/expedition/class/expedition.class.php index 8a5a84dbb78..4eb81a980bb 100644 --- a/htdocs/expedition/class/expedition.class.php +++ b/htdocs/expedition/class/expedition.class.php @@ -2388,7 +2388,7 @@ class Expedition extends CommonObject // line without batch detail // We decrement stock of product (and sub-products) -> update table llx_product_stock (key of this table is fk_product+fk_entrepot) and add a movement record - $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, -$qty, $obj->subprice, $langs->trans("ShipmentUnClassifyCloseddInDolibarr", $numref)); + $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, -$qty, $obj->subprice, $langs->trans("ShipmentUnClassifyCloseddInDolibarr", $this->ref)); if ($result < 0) { $this->error = $mouvS->error; $this->errors = $mouvS->errors; @@ -2399,7 +2399,7 @@ class Expedition extends CommonObject // line with batch detail // We decrement stock of product (and sub-products) -> update table llx_product_stock (key of this table is fk_product+fk_entrepot) and add a movement record - $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, -$qty, $obj->subprice, $langs->trans("ShipmentUnClassifyCloseddInDolibarr", $numref), '', $this->db->jdate($obj->eatby), $this->db->jdate($obj->sellby), $obj->batch, $obj->fk_origin_stock); + $result = $mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, -$qty, $obj->subprice, $langs->trans("ShipmentUnClassifyCloseddInDolibarr", $this->ref), '', $this->db->jdate($obj->eatby), $this->db->jdate($obj->sellby), $obj->batch, $obj->fk_origin_stock); if ($result < 0) { $this->error = $mouvS->error; $this->errors = $mouvS->errors; From f57dfd14c3bff9571d69d381d0d2838820ca3bd8 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Mon, 10 Apr 2023 15:00:46 +0200 Subject: [PATCH 19/42] Fix label of warehouse no more visible --- htdocs/expedition/card.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/htdocs/expedition/card.php b/htdocs/expedition/card.php index ec615119c67..81620632772 100644 --- a/htdocs/expedition/card.php +++ b/htdocs/expedition/card.php @@ -352,6 +352,7 @@ if (empty($reshook)) { if ($totalqty > 0 && !$error) { // There is at least one thing to ship and no error for ($i = 0; $i < $num; $i++) { $qty = "qtyl".$i; + if (!isset($batch_line[$i])) { // not batch mode if (isset($stockLine[$i])) { @@ -403,7 +404,7 @@ if (empty($reshook)) { } if (!$error) { - $ret = $object->create($user); // This create shipment (like Odoo picking) and lines of shipments. Stock movement will be done when validating shipment. + $ret = $object->create($user); // This create shipment (like Odoo picking) and lines of shipments. Stock movement will be done when validating or closing shipment. if ($ret <= 0) { setEventMessages($object->error, $object->errors, 'errors'); $error++; @@ -2393,7 +2394,7 @@ if ($action == 'create') { if ($detail_entrepot->entrepot_id > 0) { $entrepot = new Entrepot($db); $entrepot->fetch($detail_entrepot->entrepot_id); - $detail .= $langs->trans("DetailWarehouseFormat", $entrepot->libelle, $detail_entrepot->qty_shipped).'
'; + $detail .= $langs->trans("DetailWarehouseFormat", $entrepot->label, $detail_entrepot->qty_shipped).'
'; } } print $form->textwithtooltip(img_picto('', 'object_stock').' '.$langs->trans("DetailWarehouseNumber"), $detail); From 8ba802ed5d5b34cef9f441a29b6bfa99587d74bb Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Mon, 10 Apr 2023 15:34:13 +0200 Subject: [PATCH 20/42] Fix when reverting a shipment validation or closing and stock empty --- htdocs/expedition/card.php | 5 +++++ htdocs/langs/en_US/agenda.lang | 2 +- htdocs/langs/en_US/stocks.lang | 2 ++ htdocs/langs/fr_FR/agenda.lang | 2 +- htdocs/product/stock/class/mouvementstock.class.php | 10 ++++++++-- 5 files changed, 17 insertions(+), 4 deletions(-) diff --git a/htdocs/expedition/card.php b/htdocs/expedition/card.php index 81620632772..dfa33478e66 100644 --- a/htdocs/expedition/card.php +++ b/htdocs/expedition/card.php @@ -1739,6 +1739,11 @@ if ($action == 'create') { } $text = $langs->trans("ConfirmValidateSending", $numref); + if (getDolGlobalString('STOCK_CALCULATE_ON_SHIPMENT')) { + $text .= ' '.$langs->trans("StockMovementWillBeRecorded").'.'; + } elseif (getDolGlobalString('STOCK_CALCULATE_ON_SHIPMENT_CLOSE')) { + $text .= ' '.$langs->trans("StockMovementNotYetRecorded").'.'; + } if (isModEnabled('notification')) { require_once DOL_DOCUMENT_ROOT.'/core/class/notify.class.php'; diff --git a/htdocs/langs/en_US/agenda.lang b/htdocs/langs/en_US/agenda.lang index adc9c4177ad..43718271919 100644 --- a/htdocs/langs/en_US/agenda.lang +++ b/htdocs/langs/en_US/agenda.lang @@ -62,7 +62,7 @@ MemberSubscriptionAddedInDolibarr=Subscription %s for member %s added MemberSubscriptionModifiedInDolibarr=Subscription %s for member %s modified MemberSubscriptionDeletedInDolibarr=Subscription %s for member %s deleted ShipmentValidatedInDolibarr=Shipment %s validated -ShipmentClassifyClosedInDolibarr=Shipment %s classified billed +ShipmentClassifyClosedInDolibarr=Shipment %s classified closed ShipmentUnClassifyCloseddInDolibarr=Shipment %s classified re-open ShipmentBackToDraftInDolibarr=Shipment %s go back to draft status ShipmentDeletedInDolibarr=Shipment %s deleted diff --git a/htdocs/langs/en_US/stocks.lang b/htdocs/langs/en_US/stocks.lang index 6a8821de833..62ebfc53ef9 100644 --- a/htdocs/langs/en_US/stocks.lang +++ b/htdocs/langs/en_US/stocks.lang @@ -318,3 +318,5 @@ StockTransferRightRead=Read stocks transfers StockTransferRightCreateUpdate=Create/Update stocks transfers StockTransferRightDelete=Delete stocks transfers BatchNotFound=Lot / serial not found for this product +StockMovementWillBeRecorded=Stock movement will be recorded +StockMovementNotYetRecorded=Stock movement will not be affected by this step \ No newline at end of file diff --git a/htdocs/langs/fr_FR/agenda.lang b/htdocs/langs/fr_FR/agenda.lang index 86e97805dd5..33d925ac777 100644 --- a/htdocs/langs/fr_FR/agenda.lang +++ b/htdocs/langs/fr_FR/agenda.lang @@ -62,7 +62,7 @@ MemberSubscriptionAddedInDolibarr=Cotisation %s pour l'adhérent %s ajoutée MemberSubscriptionModifiedInDolibarr=Cotisation %s pour l'adhérent %s modifié MemberSubscriptionDeletedInDolibarr=Cotisation %s pour l'adhérent %s supprimé ShipmentValidatedInDolibarr=Expédition %s validée -ShipmentClassifyClosedInDolibarr=Expédition %s classée payée +ShipmentClassifyClosedInDolibarr=Expédition %s classée close ShipmentUnClassifyCloseddInDolibarr=Expédition %s réouverte ShipmentBackToDraftInDolibarr=Expédition %s remise au statut brouillon ShipmentDeletedInDolibarr=Expédition %s supprimée diff --git a/htdocs/product/stock/class/mouvementstock.class.php b/htdocs/product/stock/class/mouvementstock.class.php index 083ea381015..b596ac551b1 100644 --- a/htdocs/product/stock/class/mouvementstock.class.php +++ b/htdocs/product/stock/class/mouvementstock.class.php @@ -301,7 +301,9 @@ class MouvementStock extends CommonObject // If not found, we add record $sql = "SELECT pb.rowid, pb.batch, pb.eatby, pb.sellby FROM ".$this->db->prefix()."product_lot as pb"; $sql .= " WHERE pb.fk_product = ".((int) $fk_product)." AND pb.batch = '".$this->db->escape($batch)."'"; + dol_syslog(get_class($this)."::_create scan serial for this product to check if eatby and sellby match", LOG_DEBUG); + $resql = $this->db->query($sql); if ($resql) { $num = $this->db->num_rows($resql); @@ -560,6 +562,10 @@ class MouvementStock extends CommonObject if (!$error && isModEnabled('productbatch') && $product->hasbatch() && !$skip_batch) { if ($id_product_batch > 0) { $result = $this->createBatch($id_product_batch, $qty); + if ($result == -2 && $fk_product_stock > 0) { // The entry for this product batch does not exists anymore, bu we already have a llx_product_stock, so we recreate the batch entry in product_batch + $param_batch = array('fk_product_stock' =>$fk_product_stock, 'batchnumber'=>$batch); + $result = $this->createBatch($param_batch, $qty); + } } else { $param_batch = array('fk_product_stock' =>$fk_product_stock, 'batchnumber'=>$batch); $result = $this->createBatch($param_batch, $qty); @@ -863,10 +869,10 @@ class MouvementStock extends CommonObject * Create or update batch record (update table llx_product_batch). No check is done here, done by parent. * * @param array|int $dluo Could be either - * - int if row id of product_batch table + * - int if row id of product_batch table (for update) * - or complete array('fk_product_stock'=>, 'batchnumber'=>) * @param int $qty Quantity of product with batch number. May be a negative amount. - * @return int <0 if KO, else return productbatch id + * @return int <0 if KO, -2 if we try to update a product_batchid that does not exist, else return productbatch id */ private function createBatch($dluo, $qty) { From 90bca8b4d7dd411cda5de4eb6847839fc45774b5 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Mon, 10 Apr 2023 15:55:58 +0200 Subject: [PATCH 21/42] NEW Can go back to draft on shipment when stock change not on validate --- htdocs/core/class/commonobject.class.php | 2 +- htdocs/core/class/html.form.class.php | 30 ++++++++--------- htdocs/expedition/card.php | 35 ++++++++++++++------ htdocs/expedition/class/expedition.class.php | 19 ++++++++++- 4 files changed, 59 insertions(+), 27 deletions(-) diff --git a/htdocs/core/class/commonobject.class.php b/htdocs/core/class/commonobject.class.php index e53cc85c21f..10a85c3a16f 100644 --- a/htdocs/core/class/commonobject.class.php +++ b/htdocs/core/class/commonobject.class.php @@ -9982,7 +9982,7 @@ abstract class CommonObject $this->db->begin(); $statusfield = 'status'; - if ($this->element == 'don' || $this->element == 'donation') { + if (in_array($this->element, array('don', 'donation', 'shipping'))) { $statusfield = 'fk_statut'; } diff --git a/htdocs/core/class/html.form.class.php b/htdocs/core/class/html.form.class.php index eea1047bc51..5484fea6663 100644 --- a/htdocs/core/class/html.form.class.php +++ b/htdocs/core/class/html.form.class.php @@ -5087,21 +5087,21 @@ class Form * print '});'."\n"; * print ''."\n"; * - * @param string $page Url of page to call if confirmation is OK. Can contains parameters (param 'action' and 'confirm' will be reformated) - * @param string $title Title - * @param string $question Question - * @param string $action Action - * @param array|string $formquestion An array with complementary inputs to add into forms: array(array('label'=> ,'type'=> , 'size'=>, 'morecss'=>, 'moreattr'=>'autofocus' or 'style=...')) - * 'type' can be 'text', 'password', 'checkbox', 'radio', 'date', 'datetime', 'select', 'multiselect', 'morecss', - * 'other', 'onecolumn' or 'hidden'... - * @param int|string $selectedchoice '' or 'no', or 'yes' or '1', 1, '0' or 0 - * @param int|string $useajax 0=No, 1=Yes use Ajax to show the popup, 2=Yes and also submit page with &confirm=no if choice is No, 'xxx'=Yes and preoutput confirm box with div id=dialog-confirm-xxx - * @param int|string $height Force height of box (0 = auto) - * @param int $width Force width of box ('999' or '90%'). Ignored and forced to 90% on smartphones. - * @param int $disableformtag 1=Disable form tag. Can be used if we are already inside a section. - * @param string $labelbuttonyes Label for Yes - * @param string $labelbuttonno Label for No - * @return string HTML ajax code if a confirm ajax popup is required, Pure HTML code if it's an html form + * @param string $page Url of page to call if confirmation is OK. Can contains parameters (param 'action' and 'confirm' will be reformated) + * @param string $title Title + * @param string $question Question + * @param string $action Action + * @param array|string $formquestion An array with complementary inputs to add into forms: array(array('label'=> ,'type'=> , 'size'=>, 'morecss'=>, 'moreattr'=>'autofocus' or 'style=...')) + * 'type' can be 'text', 'password', 'checkbox', 'radio', 'date', 'datetime', 'select', 'multiselect', 'morecss', + * 'other', 'onecolumn' or 'hidden'... + * @param int|string $selectedchoice '' or 'no', or 'yes' or '1', 1, '0' or 0 + * @param int|string $useajax 0=No, 1=Yes use Ajax to show the popup, 2=Yes and also submit page with &confirm=no if choice is No, 'xxx'=Yes and preoutput confirm box with div id=dialog-confirm-xxx + * @param int|string $height Force height of box (0 = auto) + * @param int $width Force width of box ('999' or '90%'). Ignored and forced to 90% on smartphones. + * @param int $disableformtag 1=Disable form tag. Can be used if we are already inside a section. + * @param string $labelbuttonyes Label for Yes + * @param string $labelbuttonno Label for No + * @return string HTML ajax code if a confirm ajax popup is required, Pure HTML code if it's an html form */ public function formconfirm($page, $title, $question, $action, $formquestion = '', $selectedchoice = '', $useajax = 0, $height = 0, $width = 500, $disableformtag = 0, $labelbuttonyes = 'Yes', $labelbuttonno = 'No') { diff --git a/htdocs/expedition/card.php b/htdocs/expedition/card.php index dfa33478e66..34ed7f80f84 100644 --- a/htdocs/expedition/card.php +++ b/htdocs/expedition/card.php @@ -157,6 +157,14 @@ if (empty($reshook)) { $upload_dir = $conf->expedition->dir_output.'/sending'; include DOL_DOCUMENT_ROOT.'/core/actions_builddoc.inc.php'; + // Back to draft + if ($action == 'setdraft' && $user->rights->expedition->creer) { + $object->fetch($id); + $result = $object->setDraft($user, 0); + if ($result < 0) { + setEventMessages($object->error, $object->errors, 'errors'); + } + } // Reopen if ($action == 'reopen' && $user->rights->expedition->creer) { $object->fetch($id); @@ -1740,9 +1748,9 @@ if ($action == 'create') { $text = $langs->trans("ConfirmValidateSending", $numref); if (getDolGlobalString('STOCK_CALCULATE_ON_SHIPMENT')) { - $text .= ' '.$langs->trans("StockMovementWillBeRecorded").'.'; + $text .= '
'.$langs->trans("StockMovementWillBeRecorded").'.'; } elseif (getDolGlobalString('STOCK_CALCULATE_ON_SHIPMENT_CLOSE')) { - $text .= ' '.$langs->trans("StockMovementNotYetRecorded").'.'; + $text .= '
'.$langs->trans("StockMovementNotYetRecorded").'.'; } if (isModEnabled('notification')) { @@ -1752,7 +1760,7 @@ if ($action == 'create') { $text .= $notify->confirmMessage('SHIPPING_VALIDATE', $object->socid, $object); } - $formconfirm = $form->formconfirm($_SERVER['PHP_SELF'].'?id='.$object->id, $langs->trans('ValidateSending'), $text, 'confirm_valid', '', 0, 1); + $formconfirm = $form->formconfirm($_SERVER['PHP_SELF'].'?id='.$object->id, $langs->trans('ValidateSending'), $text, 'confirm_valid', '', 0, 1, 250); } // Confirm cancelation if ($action == 'cancel') { @@ -2540,13 +2548,20 @@ if ($action == 'create') { } } - // TODO add alternative status - // 0=draft, 1=validated, 2=billed, we miss a status "delivered" (only available on order) - if ($object->statut == Expedition::STATUS_CLOSED && $user->rights->expedition->creer) { - if (isModEnabled('facture') && !empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT)) { // Quand l'option est on, il faut avoir le bouton en plus et non en remplacement du Close ? - print dolGetButtonAction('', $langs->trans('ClassifyUnbilled'), 'default', $_SERVER["PHP_SELF"].'?action=reopen&token='.newToken().'&id='.$object->id, ''); - } else { - print dolGetButtonAction('', $langs->trans('ReOpen'), 'default', $_SERVER["PHP_SELF"].'?action=reopen&token='.newToken().'&id='.$object->id, ''); + // 0=draft, 1=validated/delivered, 2=closed/delivered + // If WORKFLOW_BILL_ON_SHIPMENT: 0=draft, 1=validated, 2=billed (no status delivered) + if ($object->statut == Expedition::STATUS_VALIDATED && !getDolGlobalString('STOCK_CALCULATE_ON_SHIPMENT')) { + if ($user->hasRight('expedition', 'creer')) { + print dolGetButtonAction('', $langs->trans('SetToDraft'), 'default', $_SERVER["PHP_SELF"].'?action=setdraft&token='.newToken().'&id='.$object->id, ''); + } + } + if ($object->statut == Expedition::STATUS_CLOSED) { + if ($user->hasRight('expedition', 'creer')) { + if (isModEnabled('facture') && !empty($conf->global->WORKFLOW_BILL_ON_SHIPMENT)) { // Quand l'option est on, il faut avoir le bouton en plus et non en remplacement du Close ? + print dolGetButtonAction('', $langs->trans('ClassifyUnbilled'), 'default', $_SERVER["PHP_SELF"].'?action=reopen&token='.newToken().'&id='.$object->id, ''); + } else { + print dolGetButtonAction('', $langs->trans('ReOpen'), 'default', $_SERVER["PHP_SELF"].'?action=reopen&token='.newToken().'&id='.$object->id, ''); + } } } diff --git a/htdocs/expedition/class/expedition.class.php b/htdocs/expedition/class/expedition.class.php index 4eb81a980bb..c3ddf973049 100644 --- a/htdocs/expedition/class/expedition.class.php +++ b/htdocs/expedition/class/expedition.class.php @@ -2318,6 +2318,23 @@ class Expedition extends CommonObject } } + /** + * Set draft status + * + * @param User $user Object user that modify + * @param int $notrigger 1=Does not execute triggers, 0=Execute triggers + * @return int <0 if KO, >0 if OK + */ + public function setDraft($user, $notrigger = 0) + { + // Protection + if ($this->statut <= self::STATUS_DRAFT) { + return 0; + } + + return $this->setStatusCommon($user, self::STATUS_DRAFT, $notrigger, 'SHIPMENT_UNVALIDATE'); + } + /** * Classify the shipping as validated/opened * @@ -2338,7 +2355,7 @@ class Expedition extends CommonObject $oldbilled = $this->billed; - $sql = 'UPDATE '.MAIN_DB_PREFIX.'expedition SET fk_statut=1'; + $sql = 'UPDATE '.MAIN_DB_PREFIX.'expedition SET fk_statut = 1'; $sql .= " WHERE rowid = ".((int) $this->id).' AND fk_statut > 0'; $resql = $this->db->query($sql); From 6914e2bb2abaa796e97edfbd86b8dd13ddcc6df6 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Mon, 10 Apr 2023 16:58:38 +0200 Subject: [PATCH 22/42] NEW Can receive more than qty ordered on reception --- htdocs/fourn/js/lib_dispatch.js.php | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/htdocs/fourn/js/lib_dispatch.js.php b/htdocs/fourn/js/lib_dispatch.js.php index ff822543ddb..6bd2d39ce21 100644 --- a/htdocs/fourn/js/lib_dispatch.js.php +++ b/htdocs/fourn/js/lib_dispatch.js.php @@ -69,13 +69,15 @@ function addDispatchLine(index, type, mode) { mode = mode || 'qtymissing' - console.log("fourn/js/lib_dispatch.js.php Split line type="+type+" index="+index+" mode="+mode); var $row0 = $("tr[name='"+type+'_0_'+index+"']"); var $dpopt = $row0.find('.hasDatepicker').first().datepicker('option', 'all'); // get current datepicker options to apply the same to the cloned datepickers var $row = $row0.clone(true); // clone first batch line to jQuery object var nbrTrs = $("tr[name^='"+type+"_'][name$='_"+index+"']").length; // position of line for batch var qtyOrdered = parseFloat($("#qty_ordered_0_"+index).val()); // Qty ordered is same for all rows var qty = parseFloat($("#qty_"+(nbrTrs - 1)+"_"+index).val()); + + console.log("fourn/js/lib_dispatch.js.php Split line type="+type+" index="+index+" mode="+mode+" qtyOrdered="+qtyOrdered+" qty="+qty); + var qtyDispatched; if (mode === 'lessone') @@ -93,9 +95,17 @@ function addDispatchLine(index, type, mode) } console.log("qtyDispatched="+qtyDispatched+" qtyOrdered="+qtyOrdered); - if (qtyDispatched >= qtyOrdered || qtyOrdered <= 1) { + if (qty <= 1) { window.alert("Remain quantity to dispatch is too low to be split"); - } else if (qtyDispatched < qtyOrdered) { + } else { + oldlineqty = qtyDispatched; + newlineqty = qtyOrdered - qtyDispatched; + if (newlineqty <= 0) { + newlineqty = qty - 1; + oldlineqty = 1; + $("#qty_"+(nbrTrs - 1)+"_"+index).val(oldlineqty); + } + //replace tr suffix nbr $row.html($row.html().replace(/_0_/g,"_"+nbrTrs+"_")); @@ -127,7 +137,7 @@ function addDispatchLine(index, type, mode) /* Suffix of lines are: _ trs.length _ index */ $("#qty_"+nbrTrs+"_"+index).focus(); - $("#qty_dispatched_0_"+index).val(qtyDispatched); + $("#qty_dispatched_0_"+index).val(oldlineqty); //hide all buttons then show only the last one $("tr[name^='"+type+"_'][name$='_"+index+"'] .splitbutton").hide(); @@ -138,7 +148,7 @@ function addDispatchLine(index, type, mode) qty = 1; // keep 1 in old line $("#qty_"+(nbrTrs-1)+"_"+index).val(qty); } - $("#qty_"+nbrTrs+"_"+index).val(qtyOrdered - qtyDispatched); + $("#qty_"+nbrTrs+"_"+index).val(newlineqty); // Store arbitrary data for dispatch qty input field change event $("#qty_"+(nbrTrs-1)+"_"+index).data('qty', qty); $("#qty_"+(nbrTrs-1)+"_"+index).data('type', type); From 3e372602d884a31848866a2d4ddc68132cfe8f2f Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Mon, 10 Apr 2023 17:07:20 +0200 Subject: [PATCH 23/42] Trans --- htdocs/core/modules/modStockTransfer.class.php | 2 +- htdocs/langs/en_US/admin.lang | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/htdocs/core/modules/modStockTransfer.class.php b/htdocs/core/modules/modStockTransfer.class.php index 279add98bad..34de040fdb8 100644 --- a/htdocs/core/modules/modStockTransfer.class.php +++ b/htdocs/core/modules/modStockTransfer.class.php @@ -61,7 +61,7 @@ class modStockTransfer extends DolibarrModules // Module description, used if translation string 'ModuleStockTransferDesc' not found (StockTransfer is name of module). $this->description = $langs->trans("ModuleStockTransferDesc"); // Used only if file README.md and README-LL.md not found. - $this->descriptionlong = "StockTransfer description (Long)"; + $this->descriptionlong = "Advanced management of stock transfer orders with generation of stock transfer sheets"; // Possible values for version are: 'development', 'experimental', 'dolibarr', 'dolibarr_deprecated' or a version string like 'x.y.z' $this->version = 'experimental'; // Url to the file with your last numberversion of this module diff --git a/htdocs/langs/en_US/admin.lang b/htdocs/langs/en_US/admin.lang index 9fb7a487118..a493b104036 100644 --- a/htdocs/langs/en_US/admin.lang +++ b/htdocs/langs/en_US/admin.lang @@ -573,7 +573,7 @@ Module50Desc=Management of Products Module51Name=Mass mailings Module51Desc=Mass paper mailing management Module52Name=Stocks -Module52Desc=Stock management +Module52Desc=Stock management (stock movement tracking and inventory) Module53Name=Services Module53Desc=Management of Services Module54Name=Contracts/Subscriptions From 18b78d28abfcdb47c0071ddcfd7f5698dd70fcdf Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Mon, 10 Apr 2023 18:56:33 +0200 Subject: [PATCH 24/42] Clean content of column "Already shipped into other shipment" --- htdocs/expedition/card.php | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/htdocs/expedition/card.php b/htdocs/expedition/card.php index 34ed7f80f84..f2947e9b09f 100644 --- a/htdocs/expedition/card.php +++ b/htdocs/expedition/card.php @@ -2285,6 +2285,8 @@ if ($action == 'create') { // Qty in other shipments (with shipment and warehouse used) if ($origin && $origin_id > 0) { print '
'; } From 5184f7c168ff892b4a02356ce90f8a9d89aad8da Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Mon, 10 Apr 2023 19:13:41 +0200 Subject: [PATCH 25/42] css --- htdocs/core/tpl/objectline_title.tpl.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/htdocs/core/tpl/objectline_title.tpl.php b/htdocs/core/tpl/objectline_title.tpl.php index cbe5d0dee10..f7afa80bf55 100644 --- a/htdocs/core/tpl/objectline_title.tpl.php +++ b/htdocs/core/tpl/objectline_title.tpl.php @@ -134,14 +134,14 @@ if (isset($this->situation_cycle_ref) && $this->situation_cycle_ref) { if ($usemargins && isModEnabled('margin') && empty($user->socid)) { if (!empty($user->rights->margins->creer)) { if ($conf->global->MARGIN_TYPE == "1") { - print ''; + print ''; } else { - print ''; + print ''; } } if (!empty($conf->global->DISPLAY_MARGIN_RATES) && $user->rights->margins->liretous) { - print ''; } if (!empty($conf->global->DISPLAY_MARK_RATES) && $user->rights->margins->liretous) { - print ''; + print ''; } } From 8747cc142349f28dcb94fb22a02535ac4fb5f9be Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Mon, 10 Apr 2023 19:17:37 +0200 Subject: [PATCH 26/42] Fix phpunit --- htdocs/product/reassortlot.php | 1 - 1 file changed, 1 deletion(-) diff --git a/htdocs/product/reassortlot.php b/htdocs/product/reassortlot.php index 4346854460a..a25fadede1d 100644 --- a/htdocs/product/reassortlot.php +++ b/htdocs/product/reassortlot.php @@ -532,7 +532,6 @@ print_barre_liste($texte, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sort /* if ($search_categ > 0) { - var_dump($search_categ); print "
"; $c = new Categorie($db); $c->fetch($search_categ); From 6d18cdb062ffc86ec30c9ec5f44876ea26886ace Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Mon, 10 Apr 2023 19:22:38 +0200 Subject: [PATCH 27/42] First test with PHP 8.2 --- .travis.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1f4fc328a49..ee378d191dd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -53,6 +53,10 @@ addons: - php8.1-mysqli - php8.1-xml - php8.1-intl + - php8.2-pgsql + - php8.2-mysqli + - php8.2-xml + - php8.2-intl env: global: @@ -73,8 +77,8 @@ jobs: php: '8.1' env: DB=mysql - stage: PHP Dev - if: type = push AND branch = 17.0 - php: nightly + if: type = push AND branch = develop + php: '8.2' env: DB=mysql notifications: @@ -120,7 +124,7 @@ install: squizlabs/php_codesniffer ^3 fi # phpunit 9 is required for php 8 - if [ "$TRAVIS_PHP_VERSION" = '8.0' ] || [ "$TRAVIS_PHP_VERSION" = '8.1' ] || [ "$TRAVIS_PHP_VERSION" = 'nightly' ]; then + if [ "$TRAVIS_PHP_VERSION" = '8.0' ] || [ "$TRAVIS_PHP_VERSION" = '8.1' ] || [ "$TRAVIS_PHP_VERSION" = '8.2' ] || [ "$TRAVIS_PHP_VERSION" = 'nightly' ]; then composer self-update 2.4.4 composer -n require --ignore-platform-reqs phpunit/phpunit ^8 \ php-parallel-lint/php-parallel-lint ^1.2 \ @@ -260,7 +264,7 @@ before_script: # enable php-fpm - sudo cp ~/.phpenv/versions/$(phpenv version-name)/etc/php-fpm.conf.default ~/.phpenv/versions/$(phpenv version-name)/etc/php-fpm.conf - | - if [ "$TRAVIS_PHP_VERSION" = '7.0' ] || [ "$TRAVIS_PHP_VERSION" = '7.1' ] || [ "$TRAVIS_PHP_VERSION" = '7.2' ] || [ "$TRAVIS_PHP_VERSION" = '7.3' ] || [ "$TRAVIS_PHP_VERSION" = '7.4' ] || [ "$TRAVIS_PHP_VERSION" = '8.0' ] || [ "$TRAVIS_PHP_VERSION" = '8.1' ] || [ "$TRAVIS_PHP_VERSION" = 'nightly' ]; then + if [ "$TRAVIS_PHP_VERSION" = '7.0' ] || [ "$TRAVIS_PHP_VERSION" = '7.1' ] || [ "$TRAVIS_PHP_VERSION" = '7.2' ] || [ "$TRAVIS_PHP_VERSION" = '7.3' ] || [ "$TRAVIS_PHP_VERSION" = '7.4' ] || [ "$TRAVIS_PHP_VERSION" = '8.0' ] || [ "$TRAVIS_PHP_VERSION" = '8.1' ] || [ "$TRAVIS_PHP_VERSION" = '8.2' ] || [ "$TRAVIS_PHP_VERSION" = 'nightly' ]; then # Copy the included pool sudo cp ~/.phpenv/versions/$(phpenv version-name)/etc/php-fpm.d/www.conf.default ~/.phpenv/versions/$(phpenv version-name)/etc/php-fpm.d/www.conf fi From 1964a020aa3696a991a53af6921ff1f2e8945b21 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Mon, 10 Apr 2023 19:35:58 +0200 Subject: [PATCH 28/42] Look and feel v18 --- htdocs/product/stock/movement_list.php | 413 +++++++++++++++---------- 1 file changed, 257 insertions(+), 156 deletions(-) diff --git a/htdocs/product/stock/movement_list.php b/htdocs/product/stock/movement_list.php index 56d39ae0e88..72f36220185 100644 --- a/htdocs/product/stock/movement_list.php +++ b/htdocs/product/stock/movement_list.php @@ -50,20 +50,23 @@ if (isModEnabled('productbatch')) { $langs->load("productbatch"); } -$id = GETPOST('id', 'int'); -$ref = GETPOST('ref', 'alpha'); -$msid = GETPOST('msid', 'int'); -$product_id = GETPOST("product_id", 'int'); $action = GETPOST('action', 'aZ09'); $massaction = GETPOST('massaction', 'alpha'); // The bulk action (combo box choice into lists) $confirm = GETPOST('confirm', 'alpha'); // Result of a confirmation $cancel = GETPOST('cancel', 'alpha'); -$contextpage = GETPOST('contextpage', 'aZ') ?GETPOST('contextpage', 'aZ') : 'movementlist'; +$contextpage = GETPOST('contextpage', 'aZ') ? GETPOST('contextpage', 'aZ') : str_replace('_', '', basename(dirname(__FILE__)).basename(__FILE__, '.php')); // To manage different context of search $toselect = GETPOST('toselect', 'array'); // Array of ids of elements selected into a list $backtopage = GETPOST("backtopage", "alpha"); +$optioncss = GETPOST('optioncss', 'aZ'); // Option for the css output (always '' except when 'print') +$mode = GETPOST('mode', 'aZ'); // The output mode ('list', 'kanban', 'hierarchy', 'calendar', ...) +$id = GETPOST('id', 'int'); +$ref = GETPOST('ref', 'alpha'); +$msid = GETPOST('msid', 'int'); $idproduct = GETPOST('idproduct', 'int'); -$sall = trim((GETPOST('search_all', 'alphanohtml') != '') ?GETPOST('search_all', 'alphanohtml') : GETPOST('sall', 'alphanohtml')); +$product_id = GETPOST("product_id", 'int'); + +$search_all = trim((GETPOST('search_all', 'alphanohtml') != '') ? GETPOST('search_all', 'alphanohtml') : GETPOST('sall', 'alphanohtml')); $search_date_startday = GETPOST('search_date_startday', 'int'); $search_date_startmonth = GETPOST('search_date_startmonth', 'int'); $search_date_startyear = GETPOST('search_date_startyear', 'int'); @@ -83,7 +86,7 @@ $search_batch = trim(GETPOST("search_batch")); $search_qty = trim(GETPOST("search_qty")); $search_type_mouvement = GETPOST('search_type_mouvement', 'int'); $search_fk_projet=GETPOST("search_fk_projet", 'int'); -$optioncss = GETPOST('optioncss', 'alpha'); + $type = GETPOST("type", "int"); // Load variable for pagination @@ -108,15 +111,15 @@ if (!$sortorder) { $pdluoid = GETPOST('pdluoid', 'int'); -// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context +// Initialize technical objects $object = new MouvementStock($db); $extrafields = new ExtraFields($db); $diroutputmassaction = $conf->stock->dir_output.'/temp/massgeneration/'.$user->id; -$hookmanager->initHooks(array('movementlist')); +$hookmanager->initHooks(array($contextpage)); // Note that conf->hooks_modules contains array of activated contexes $formfile = new FormFile($db); -// fetch optionals attributes and labels +// Fetch optionals attributes and labels $extrafields->fetch_name_optionals_label($object->table_element); $search_array_options = $extrafields->getOptionalsFromPost($object->table_element, '', 'search_'); @@ -224,7 +227,7 @@ if (empty($reshook)) { $search_batch = ""; $search_qty = ''; $search_fk_projet=0; - $sall = ""; + $search_all = ""; $toselect = array(); $search_array_options = array(); } @@ -588,18 +591,21 @@ if ($action == "transfert_stock" && !$cancel) { * View */ -$productlot = new ProductLot($db); -$productstatic = new Product($db); -$warehousestatic = new Entrepot($db); -$movement = new MouvementStock($db); -$userstatic = new User($db); $form = new Form($db); $formproduct = new FormProduct($db); if (!empty($conf->project->enabled)) { $formproject = new FormProjets($db); } +$productlot = new ProductLot($db); +$productstatic = new Product($db); +$warehousestatic = new Entrepot($db); +$movement = new MouvementStock($db); +$userstatic = new User($db); + +$now = dol_now(); // Build and execute select +// -------------------------------------------------------------------- $sql = "SELECT p.rowid, p.ref as product_ref, p.label as produit, p.tosell, p.tobuy, p.tobatch, p.fk_product_type as type, p.entity,"; $sql .= " e.ref as warehouse_ref, e.rowid as entrepot_id, e.lieu, e.fk_parent, e.statut,"; $sql .= " m.rowid as mid, m.value as qty, m.datem, m.fk_user_author, m.label, m.inventorycode, m.fk_origin, m.origintype,"; @@ -616,7 +622,7 @@ if (!empty($extrafields->attributes[$object->table_element]['label'])) { } // Add fields from hooks $parameters = array(); -$reshook = $hookmanager->executeHooks('printFieldListSelect', $parameters); // Note that $action and $object may have been modified by hook +$reshook = $hookmanager->executeHooks('printFieldListSelect', $parameters, $object, $action); // Note that $action and $object may have been modified by hook $sql .= $hookmanager->resPrint; $sql = preg_replace('/,\s*$/', '', $sql); @@ -697,7 +703,7 @@ if ($search_type_mouvement != '' && $search_type_mouvement != '-1') { include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_sql.tpl.php'; // Add where from hooks $parameters = array(); -$reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters); // Note that $action and $object may have been modified by hook +$reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters, $object, $action); // Note that $action and $object may have been modified by hook $sql .= $hookmanager->resPrint; // Count total nb of records @@ -714,7 +720,7 @@ if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST)) { dol_print_error($db); } - if (($page * $limit) > $nbtotalofrecords) { // if total resultset is smaller then paging size (filtering), goto and load page 0 + if (($page * $limit) > $nbtotalofrecords) { // if total resultset is smaller than the paging size (filtering), goto and load page 0 $page = 0; $offset = 0; } @@ -733,6 +739,9 @@ if (!$resql) { exit; } +$num = $db->num_rows($resql); + + $product = new Product($db); $object = new Entrepot($db); @@ -746,8 +755,6 @@ if ($id > 0 || $ref) { } } -$num = $db->num_rows($resql); - // Output page // -------------------------------------------------------------------- @@ -767,9 +774,11 @@ if ($msid) { } } -llxHeader('', $title, $help_url); -$arrayofselected = is_array($toselect) ? $toselect : array(); +// Output page +// -------------------------------------------------------------------- + +llxHeader('', $title, $help_url); /* * Show tab only if we ask a particular warehouse @@ -939,7 +948,12 @@ if ((empty($action) || $action == 'list') && $id > 0) { print '

'; } +$arrayofselected = is_array($toselect) ? $toselect : array(); + $param = ''; +if (!empty($mode)) { + $param .= '&mode='.urlencode($mode); +} if (!empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) { $param .= '&contextpage='.urlencode($contextpage); } @@ -996,6 +1010,10 @@ if ($idproduct > 0) { } // Add $param from extra fields include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_param.tpl.php'; +// Add $param from hooks +$parameters = array(); +$reshook = $hookmanager->executeHooks('printFieldListSearchParam', $parameters, $object, $action); // Note that $action and $object may have been modified by hook +$param .= $hookmanager->resPrint; // List of mass actions available $arrayofmassactions = array(); @@ -1023,10 +1041,15 @@ print ''; print ''; print ''; print ''; +print ''; +print ''; if ($id > 0) { print ''; } + +$newcardbutton = ''; + if ($id > 0) { print_barre_liste($title, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'movement', 0, '', '', $limit, 0, 0, 1); } else { @@ -1040,17 +1063,20 @@ $objecttmp = new MouvementStock($db); $trackid = 'mov'.$object->id; include DOL_DOCUMENT_ROOT.'/core/tpl/massactions_pre.tpl.php'; -if ($sall) { +if ($search_all) { + $setupstring = ''; foreach ($fieldstosearchall as $key => $val) { $fieldstosearchall[$key] = $langs->trans($val); + $setupstring .= $key."=".$val.";"; } - print '
'.$langs->trans("FilterOnInto", $sall).join(', ', $fieldstosearchall).'
'; + print ''."\n"; + print '
'.$langs->trans("FilterOnInto", $search_all).join(', ', $fieldstosearchall).'
'."\n"; } $moreforfilter = ''; $parameters = array('arrayfields'=>&$arrayfields); -$reshook = $hookmanager->executeHooks('printFieldPreListTitle', $parameters, $object); // Note that $action and $object may have been modified by hook +$reshook = $hookmanager->executeHooks('printFieldPreListTitle', $parameters, $object, $action); // Note that $action and $object may have been modified by hook if (empty($reshook)) { $moreforfilter .= $hookmanager->resPrint; } else { @@ -1064,7 +1090,7 @@ if (!empty($moreforfilter)) { } $varpage = empty($contextpage) ? $_SERVER["PHP_SELF"] : $contextpage; -$selectedfields = $form->multiSelectArrayWithCheckbox('selectedfields', $arrayfields, $varpage); // This also change content of $arrayfields +$selectedfields = ($mode != 'kanban' ? $form->multiSelectArrayWithCheckbox('selectedfields', $arrayfields, $varpage, getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN', '')) : ''); // This also change content of $arrayfields $selectedfields .= (count($arrayofmassactions) ? $form->showCheckAddButtons('checkforselect', 1) : ''); print '
'; // You can use div-table-responsive-no-min if you dont need reserved height for your table @@ -1073,6 +1099,13 @@ print '
'.$langs->trans('CarCategory').''.$langs->trans('Description').''.$langs->trans('Description').''.$langs->trans('VAT').''.$langs->trans('PriceUHT').''.$langs->trans('PriceUTTC').''; + print ''; if ($line->fk_project > 0) { $projecttmp->id = $line->fk_project; $projecttmp->ref = $line->projet_ref; @@ -2475,7 +2476,7 @@ if ($action == 'create') { if (!empty($conf->global->MAIN_USE_EXPENSE_IK)) { print ''.$langs->trans('CarCategory').''.$langs->trans('Description').''.$langs->trans('Description').''.$langs->trans('VAT').''.$langs->trans('PriceUHT').''.$langs->trans('PriceUTTC').''; - print $form->buttonsSaveCancel("Add", '', '', 1); + print $form->buttonsSaveCancel("Add", '', '', 1, 'reposition'); print '
'; - print $form->select_produits('', 'fk_product', '1', 0, $projectstatic->thirdparty->price_level, 1, 2, '', 0, array(), $projectstatic->thirdparty->id, 'None', 0, 'maxwidth500'); + print ''; + print img_picto('', 'product'); + print $form->select_produits('', 'fk_product', '1', 0, $projectstatic->thirdparty->price_level, 1, 2, '', 1, array(), $projectstatic->thirdparty->id, 'None', 0, 'maxwidth150', 0, '', null, 1); print ''; $form->buttonsSaveCancel(); - print ''; + print ''; print ''; print '
'; + print ''; if ($task_time->fk_soc > 0) { if (empty($conf->cache['thridparty'][$task_time->fk_soc])) { $tmpsociete = new Societe($db); @@ -2185,9 +2186,9 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser } } - //Product + // Product if (!empty($arrayfields['t.fk_product']['checked'])) { - print ''; + print ''; if ($action == 'editline' && $_GET['lineid'] == $task_time->rowid) { $form->select_produits($task_time->fk_product, 'fk_product', '1', 0, $projectstatic->thirdparty->price_level, 1, 2, '', 0, array(), $projectstatic->thirdparty->id, 'None', 0, 'maxwidth500'); } elseif (!empty($task_time->fk_product)) { @@ -2200,6 +2201,9 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser } } print ''; if (($action == 'editline' || $action == 'splitline') && GETPOST('lineid', 'int') == $task_time->rowid) { print ''; - print ''; + print ''; print ' '; - print ''; + print ''; } elseif ($user->hasRight('projet', 'time') || $user->hasRight('projet', 'all', 'creer')) { // Read project and enter time consumed on assigned tasks if (in_array($task_time->fk_user, $childids) || $user->hasRight('projet', 'all', 'creer')) { if (getDolGlobalString('MAIN_FEATURES_LEVEL') >= 2) { @@ -2311,6 +2315,7 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser // Add line to split if ($action == 'splitline' && GETPOST('lineid', 'int') == $task_time->rowid) { + print ''; print '
'; + print ''; + print ''; + print ''; if ($action == 'splitline' && GETPOST('lineid', 'int') == $task_time->rowid) { if (empty($object->id)) { $object->fetch($id); @@ -2410,6 +2427,12 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser print ''; + print ''; @@ -2449,7 +2472,7 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser // Line for second dispatching - print '
'; + print ''; + print ''; - print $task_time->label; + print dol_escape_htmltag($task_time->label); print ''; + print ''; if ($action == 'splitline' && GETPOST('lineid', 'int') == $task_time->rowid) { if (empty($object->id)) { $object->fetch($id); @@ -2548,6 +2583,12 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser print ''; + print ''; From 89f69068515f04d023b2e86226e5d026ee710534 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Mon, 10 Apr 2023 12:09:52 +0200 Subject: [PATCH 14/42] Trans --- htdocs/langs/en_US/commercial.lang | 4 ++-- htdocs/projet/card.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/htdocs/langs/en_US/commercial.lang b/htdocs/langs/en_US/commercial.lang index eba95a8aabb..a2f5c6d21ce 100644 --- a/htdocs/langs/en_US/commercial.lang +++ b/htdocs/langs/en_US/commercial.lang @@ -65,8 +65,8 @@ ActionAC_SUP_ORD=Send purchase order by mail ActionAC_SUP_INV=Send vendor invoice by mail ActionAC_OTH=Other ActionAC_OTH_AUTO=Other auto -ActionAC_MANUAL=Manually inserted events -ActionAC_AUTO=Automatically inserted events +ActionAC_MANUAL=Events inserted manually (by a user) +ActionAC_AUTO=Events inserted automatically ActionAC_OTH_AUTOShort=Other ActionAC_EVENTORGANIZATION=Event organization events Stats=Sales statistics diff --git a/htdocs/projet/card.php b/htdocs/projet/card.php index 3b5a0175d03..040b3ea2772 100644 --- a/htdocs/projet/card.php +++ b/htdocs/projet/card.php @@ -1157,9 +1157,9 @@ if ($action == 'create' && $user->rights->projet->creer) { if (!empty($object->usage_task) && !empty($object->lines)) { print ' '; + print '/>'; } print '
'; + $htmltooltip = ''; + $qtyalreadysent = 0; foreach ($alreadysent as $key => $val) { if ($lines[$i]->fk_origin_line == $key) { $j = 0; @@ -2295,20 +2297,23 @@ if ($action == 'create') { $j++; if ($j > 1) { - print '
'; + $htmltooltip .= '
'; } $shipment_static->fetch($shipmentline_var['shipment_id']); - print $shipment_static->getNomUrl(1); - print ' - '.$shipmentline_var['qty_shipped']; - $htmltext = $langs->trans("DateValidation").' : '.(empty($shipmentline_var['date_valid']) ? $langs->trans("Draft") : dol_print_date($shipmentline_var['date_valid'], 'dayhour')); - if (isModEnabled('stock') && $shipmentline_var['warehouse'] > 0) { + $htmltooltip .= $shipment_static->getNomUrl(1, '', 0, 0, 1); + $htmltooltip .= ' - '.$shipmentline_var['qty_shipped']; + $htmltooltip .= ' - '.$langs->trans("DateValidation").' : '.(empty($shipmentline_var['date_valid']) ? $langs->trans("Draft") : dol_print_date($shipmentline_var['date_valid'], 'dayhour')); + /*if (isModEnabled('stock') && $shipmentline_var['warehouse'] > 0) { $warehousestatic->fetch($shipmentline_var['warehouse']); $htmltext .= '
'.$langs->trans("FromLocation").' : '.$warehousestatic->getNomUrl(1, '', 0, 1); - } - print ' '.$form->textwithpicto('', $htmltext, 1); + }*/ + //print ' '.$form->textwithpicto('', $htmltext, 1); + + $qtyalreadysent += $shipmentline_var['qty_shipped']; } } } + print $form->textwithpicto($qtyalreadysent, $htmltooltip, 1, 'info', '', 0, 3, 'tooltip'.$lines[$i]->id); print '
'.$langs->trans('BuyingPrice').''.$langs->trans('BuyingPrice').''.$langs->trans('CostPrice').''.$langs->trans('CostPrice').''.$langs->trans('MarginRate'); + print ''.$langs->trans('MarginRate'); if ($user->hasRight("propal", "creer")) { print 'id.'">'.img_edit($langs->trans("UpdateForAllLines"), 0, 'class="clickmarginforalllines opacitymedium paddingleft cursorpointer"').''; if (GETPOST('mode', 'aZ09') == 'marginforalllines') { @@ -154,7 +154,7 @@ if ($usemargins && isModEnabled('margin') && empty($user->socid)) { print ''.$langs->trans('MarkRate').''.$langs->trans('MarkRate').'
'; +// Action column +if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) { + print ''; +} if (!empty($arrayfields['m.rowid']['checked'])) { // Ref print ''; } // Action column -print ''; +if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) { + print ''; +} print ''."\n"; +$totalarray = array(); +$totalarray['nbfield'] = 0; // Fields title label // -------------------------------------------------------------------- print ''; +// Action column +if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) { + print getTitleFieldOfList($selectedfields, 0, $_SERVER["PHP_SELF"], '', '', '', '', $sortfield, $sortorder, 'center maxwidthsearch ')."\n"; + $totalarray['nbfield']++; +} if (!empty($arrayfields['m.rowid']['checked'])) { print_liste_field_titre($arrayfields['m.rowid']['label'], $_SERVER["PHP_SELF"], 'm.rowid', '', $param, '', $sortfield, $sortorder); } @@ -1262,8 +1304,8 @@ if (!empty($arrayfields['m.price']['checked'])) { include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_title.tpl.php'; // Hook fields -$parameters = array('arrayfields'=>$arrayfields, 'param'=>$param, 'sortfield'=>$sortfield, 'sortorder'=>$sortorder); -$reshook = $hookmanager->executeHooks('printFieldListTitle', $parameters, $object); // Note that $action and $object may have been modified by hook +$parameters = array('arrayfields'=>$arrayfields, 'param'=>$param, 'sortfield'=>$sortfield, 'sortorder'=>$sortorder, 'totalarray'=>&$totalarray); +$reshook = $hookmanager->executeHooks('printFieldListTitle', $parameters, $object, $action); // Note that $action and $object may have been modified by hook print $hookmanager->resPrint; if (!empty($arrayfields['m.datec']['checked'])) { print_liste_field_titre($arrayfields['p.datec']['label'], $_SERVER["PHP_SELF"], "p.datec", "", $param, '', $sortfield, $sortorder, 'center nowrap '); @@ -1272,7 +1314,10 @@ if (!empty($arrayfields['m.tms']['checked'])) { print_liste_field_titre($arrayfields['p.tms']['label'], $_SERVER["PHP_SELF"], "p.tms", "", $param, '', $sortfield, $sortorder, 'center nowrap '); } // Action column -print_liste_field_titre($selectedfields, $_SERVER["PHP_SELF"], "", '', '', '', $sortfield, $sortorder, 'center maxwidthsearch '); +if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) { + print getTitleFieldOfList($selectedfields, 0, $_SERVER["PHP_SELF"], '', '', '', '', $sortfield, $sortorder, 'center maxwidthsearch ')."\n"; + $totalarray['nbfield']++; +} print ''."\n"; @@ -1282,9 +1327,11 @@ $arrayofuniqueproduct = array(); // Loop on record // -------------------------------------------------------------------- $i = 0; +$savnbfield = $totalarray['nbfield']; $totalarray = array(); $totalarray['nbfield'] = 0; -while ($i < ($limit ? min($num, $limit) : $num)) { +$imaxinloop = ($limit ? min($num, $limit) : $num); +while ($i < $imaxinloop) { $obj = $db->fetch_object($resql); if (empty($obj)) { break; // Should not happen @@ -1346,139 +1393,193 @@ while ($i < ($limit ? min($num, $limit) : $num)) { $origin = ''; } - print ''; - // Id movement - if (!empty($arrayfields['m.rowid']['checked'])) { - print ''; // This is primary not movement id - } - if (!empty($arrayfields['m.datem']['checked'])) { - // Date - print ''; - } - if (!empty($arrayfields['p.ref']['checked'])) { - // Product ref - print '\n"; - } - if (!empty($arrayfields['p.label']['checked'])) { - // Product label - print '\n"; - } - if (!empty($arrayfields['m.batch']['checked'])) { - print ''; - } - if (!empty($arrayfields['pl.eatby']['checked'])) { - print ''; - } - if (!empty($arrayfields['pl.sellby']['checked'])) { - print ''; - } - // Warehouse - if (!empty($arrayfields['e.ref']['checked'])) { - print '\n"; - } - // Author - if (!empty($arrayfields['m.fk_user_author']['checked'])) { - print '\n"; - } - if (!empty($arrayfields['m.inventorycode']['checked'])) { - // Inventory code - print ''; - } - if (!empty($arrayfields['m.label']['checked'])) { - // Label of movement - print ''; - } - if (!empty($arrayfields['origin']['checked'])) { - // Origin of movement - print ''; - } - if (!empty($arrayfields['m.fk_projet']['checked'])) { - // fk_project - print ''; - } - if (!empty($arrayfields['m.type_mouvement']['checked'])) { - // Type of movement - print ''; - } - if (!empty($arrayfields['m.value']['checked'])) { - // Qty - print ''; } - print ''; - } - if (!empty($arrayfields['m.price']['checked'])) { - // Price - print ''; + // Action column + if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) { + print ''; + if (!$i) { + $totalarray['nbfield']++; + } + } + // Id movement + if (!empty($arrayfields['m.rowid']['checked'])) { + print ''; // This is primary not movement id + } + if (!empty($arrayfields['m.datem']['checked'])) { + // Date + print ''; + } + if (!empty($arrayfields['p.ref']['checked'])) { + // Product ref + print '\n"; + } + if (!empty($arrayfields['p.label']['checked'])) { + // Product label + print '\n"; + } + if (!empty($arrayfields['m.batch']['checked'])) { + print ''; + } + if (!empty($arrayfields['pl.eatby']['checked'])) { + print ''; + } + if (!empty($arrayfields['pl.sellby']['checked'])) { + print ''; + } + // Warehouse + if (!empty($arrayfields['e.ref']['checked'])) { + print '\n"; + } + // Author + if (!empty($arrayfields['m.fk_user_author']['checked'])) { + print '\n"; + } + if (!empty($arrayfields['m.inventorycode']['checked'])) { + // Inventory code + print ''; + } + if (!empty($arrayfields['m.label']['checked'])) { + // Label of movement + print ''; + } + if (!empty($arrayfields['origin']['checked'])) { + // Origin of movement + print ''; + } + if (!empty($arrayfields['m.fk_projet']['checked'])) { + // fk_project + print ''; + } + if (!empty($arrayfields['m.type_mouvement']['checked'])) { + // Type of movement + print ''; + } + if (!empty($arrayfields['m.value']['checked'])) { + // Qty + print ''; + } + if (!empty($arrayfields['m.price']['checked'])) { + // Price + print ''; } - print ''; - } - // Extra fields - $object = $movement; - include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_print_fields.tpl.php'; - // Fields from hook - $parameters = array('arrayfields'=>$arrayfields, 'object'=>$object, 'obj'=>$obj, 'i'=>$i, 'totalarray'=>&$totalarray); - $reshook = $hookmanager->executeHooks('printFieldListValue', $parameters, $object); // Note that $action and $object may have been modified by hook - print $hookmanager->resPrint; + // Extra fields + $object = $movement; + include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_print_fields.tpl.php'; + // Fields from hook + $parameters = array('arrayfields'=>$arrayfields, 'object'=>$object, 'obj'=>$obj, 'i'=>$i, 'totalarray'=>&$totalarray); + $reshook = $hookmanager->executeHooks('printFieldListValue', $parameters, $object, $action); // Note that $action and $object may have been modified by hook + print $hookmanager->resPrint; - // Action column - print ''; + if (!$i) { + $totalarray['nbfield']++; + } } - print ''; - } - print ''; - if (!$i) { - $totalarray['nbfield']++; - } - print ''."\n"; + print ''."\n"; + } $i++; } +// If no record found +if ($num == 0) { + $colspan = 1; + foreach ($arrayfields as $key => $val) { + if (!empty($val['checked'])) { + $colspan++; + } + } + print ''; +} + $db->free($resql); -print "
'; + $searchpicto = $form->showFilterButtons('left'); + print $searchpicto; + print ''; @@ -1185,7 +1218,7 @@ include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_input.tpl.php'; // Fields from hook $parameters = array('arrayfields'=>$arrayfields); -$reshook = $hookmanager->executeHooks('printFieldListOption', $parameters, $object); // Note that $action and $object may have been modified by hook +$reshook = $hookmanager->executeHooks('printFieldListOption', $parameters, $object, $action); // Note that $action and $object may have been modified by hook print $hookmanager->resPrint; // Date creation if (!empty($arrayfields['m.datec']['checked'])) { @@ -1198,16 +1231,25 @@ if (!empty($arrayfields['m.tms']['checked'])) { print ''; -$searchpicto = $form->showFilterButtons(); -print $searchpicto; -print ''; + $searchpicto = $form->showFilterButtons(); + print $searchpicto; + print '
'; - print img_picto($langs->trans("StockMovement"), 'movement', 'class="pictofixedwidth"'); - print $obj->mid; - print ''.dol_print_date($db->jdate($obj->datem), 'dayhour', 'tzuserrel').''; - print $productstatic->getNomUrl(1, 'stock', 16); - print "'; - print $productstatic->label; - print "'; - if ($productlot->id > 0) { - print $productlot->getNomUrl(1); - } else { - print $productlot->batch; // the id may not be defined if movement was entered when lot was not saved or if lot was removed after movement. + if ($mode == 'kanban') { + if ($i == 0) { + print '
'; + print '
'; } - print '
'.dol_print_date($obj->eatby, 'day').''.dol_print_date($obj->sellby, 'day').''; - print $warehousestatic->getNomUrl(1); - print "'; - print $userstatic->getNomUrl(-1); - print "inventorycode.'$').'&search_type_mouvement='.urlencode($obj->type_mouvement).'">'.$obj->inventorycode.''.$obj->label.''.$origin.''; - if ($obj->fk_project != 0) { - print $movement->get_origin($obj->fk_project, 'project'); + // Output Kanban + if ($massactionbutton || $massaction) { // If we are in select mode (massactionbutton defined) or if we have already selected and sent an action ($massaction) defined + $selected = 0; + if (in_array($object->id, $arrayofselected)) { + $selected = 1; + } } - print ''; - print $movement->getTypeMovement(); - print ''; - if ($obj->qty > 0) { - print ''; - print '+'; - print $obj->qty; - print ''; - } else { - print ''; - print $obj->qty; - print ''; + print $object->getKanbanView(''); + if ($i == ($imaxinloop - 1)) { + print ''; + print '
'; - if ($obj->price != 0) { - print price($obj->price); + } else { + // Show here line of result + $j = 0; + print '
'; + if ($massactionbutton || $massaction) { // If we are in select mode (massactionbutton defined) or if we have already selected and sent an action ($massaction) defined + $selected = 0; + if (in_array($obj->mid, $arrayofselected)) { + $selected = 1; + } + print ''; + } + print ''; + print img_picto($langs->trans("StockMovement"), 'movement', 'class="pictofixedwidth"'); + print $obj->mid; + print ''.dol_print_date($db->jdate($obj->datem), 'dayhour', 'tzuserrel').''; + print $productstatic->getNomUrl(1, 'stock', 16); + print "'; + print $productstatic->label; + print "'; + if ($productlot->id > 0) { + print $productlot->getNomUrl(1); + } else { + print $productlot->batch; // the id may not be defined if movement was entered when lot was not saved or if lot was removed after movement. + } + print ''.dol_print_date($obj->eatby, 'day').''.dol_print_date($obj->sellby, 'day').''; + print $warehousestatic->getNomUrl(1); + print "'; + print $userstatic->getNomUrl(-1); + print "inventorycode.'$').'&search_type_mouvement='.urlencode($obj->type_mouvement).'">'.$obj->inventorycode.''.$obj->label.''.$origin.''; + if ($obj->fk_project != 0) { + print $movement->get_origin($obj->fk_project, 'project'); + } + print ''; + print $movement->getTypeMovement(); + print ''; + if ($obj->qty > 0) { + print ''; + print '+'; + print $obj->qty; + print ''; + } else { + print ''; + print $obj->qty; + print ''; + } + print ''; + if ($obj->price != 0) { + print price($obj->price); + } + print ''; - if ($massactionbutton || $massaction) { // If we are in select mode (massactionbutton defined) or if we have already selected and sent an action ($massaction) defined - $selected = 0; - if (in_array($obj->mid, $arrayofselected)) { - $selected = 1; + // Action column + if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) { + print ''; + if ($massactionbutton || $massaction) { // If we are in select mode (massactionbutton defined) or if we have already selected and sent an action ($massaction) defined + $selected = 0; + if (in_array($obj->mid, $arrayofselected)) { + $selected = 1; + } + print ''; + } + print '
'.$langs->trans("NoRecordFound").'
"; -print '
'; -print ""; +$parameters = array('arrayfields'=>$arrayfields, 'sql'=>$sql); +$reshook = $hookmanager->executeHooks('printFieldListFooter', $parameters, $object, $action); // Note that $action and $object may have been modified by hook +print $hookmanager->resPrint; + +print ''."\n"; +print ''."\n"; + +print ''."\n"; // Add number of product when there is a filter on period if (count($arrayofuniqueproduct) == 1 && !empty($year) && is_numeric($year)) { @@ -1527,7 +1628,7 @@ if (in_array('builddoc', $arrayofmassactions) && ($nbtotalofrecords === '' || $n $genallowed = $permissiontoread; $delallowed = $permissiontoadd; - print $formfile->showdocuments('massfilesarea_mymodule', '', $filedir, $urlsource, 0, $delallowed, '', 1, 1, 0, 48, 1, $param, $title, '', '', '', null, $hidegeneratedfilelistifempty); + print $formfile->showdocuments('massfilesarea_'.$object->module, '', $filedir, $urlsource, 0, $delallowed, '', 1, 1, 0, 48, 1, $param, $title, '', '', '', null, $hidegeneratedfilelistifempty); } // End of page From 84a81fde360cc83608b59c18bdc0791cd29ed88e Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Mon, 10 Apr 2023 19:39:56 +0200 Subject: [PATCH 29/42] Debug v18 --- htdocs/product/reassortlot.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/htdocs/product/reassortlot.php b/htdocs/product/reassortlot.php index a25fadede1d..8c655df44ff 100644 --- a/htdocs/product/reassortlot.php +++ b/htdocs/product/reassortlot.php @@ -38,7 +38,7 @@ require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php'; require_once DOL_DOCUMENT_ROOT.'/product/stock/class/productlot.class.php'; // Load translation files required by the page -$langs->loadLangs(array('products', 'stocks', 'productbatch')); +$langs->loadLangs(array('products', 'stocks', 'productbatch', 'categories')); $action = GETPOST('action', 'aZ09') ?GETPOST('action', 'aZ09') : 'view'; // The action 'add', 'create', 'edit', 'update', 'view', ... $massaction = GETPOST('massaction', 'alpha'); // The bulk action (combo box choice into lists) @@ -545,7 +545,7 @@ if ($search_categ > 0) { $moreforfilter = ''; if (isModEnabled('categorie')) { $moreforfilter .= '
'; - $moreforfilter .= img_picto($langs->trans('ProductsCategoryShort'), 'category', 'class="pictofixedwidth"'); + $moreforfilter .= img_picto($langs->trans('ProductsCategoriesShort'), 'category', 'class="pictofixedwidth"'); $moreforfilter .= $htmlother->select_categories(Categorie::TYPE_PRODUCT, $search_categ, 'search_categ', 1, $langs->trans("ProductsCategoryShort"), 'maxwidth400'); $moreforfilter .= '
'; } From 0f58368d5d8215387e28bc8d6456baa47a6da8b5 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 11 Apr 2023 10:13:15 +0200 Subject: [PATCH 30/42] Fix do imap expunge only if no error --- htdocs/emailcollector/class/emailcollector.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/emailcollector/class/emailcollector.class.php b/htdocs/emailcollector/class/emailcollector.class.php index 7da3da4e83a..1b7c7ef2f27 100644 --- a/htdocs/emailcollector/class/emailcollector.class.php +++ b/htdocs/emailcollector/class/emailcollector.class.php @@ -2888,7 +2888,7 @@ class EmailCollector extends CommonObject if (!empty($conf->global->MAIN_IMAP_USE_PHPIMAP)) { $client->disconnect(); } else { - if (empty($mode)) { + if (empty($mode) && empty($error)) { imap_expunge($connection); // To validate any move } imap_close($connection); From a1e6fe7f5050500ab580ac969b2d27cba9adcfef Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 11 Apr 2023 10:34:03 +0200 Subject: [PATCH 31/42] Revert "Try fix MS OAuth2 Imap (from feedback at dolibarr.org/forum/t/emailcollectors-with-microsoft-365-oauth2)" This reverts commit 976c2d76f260d73cd5508fe837e487675228d156. --- htdocs/core/lib/oauth.lib.php | 2 +- htdocs/includes/OAuth/OAuth2/Service/Microsoft.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/htdocs/core/lib/oauth.lib.php b/htdocs/core/lib/oauth.lib.php index aaac40a525f..8a81d9a80e5 100644 --- a/htdocs/core/lib/oauth.lib.php +++ b/htdocs/core/lib/oauth.lib.php @@ -32,7 +32,7 @@ if (isModEnabled('stripe')) { $supportedoauth2array['OAUTH_STRIPE_LIVE_NAME'] = array('callbackfile' => 'stripelive', 'picto' => 'stripe', 'urlforapp' => '', 'name'=>'StripeLive', 'urlforcredentials'=>'', 'availablescopes'=>'read_write', 'returnurl'=>'/core/modules/oauth/stripelive_oauthcallback.php'); } $supportedoauth2array['OAUTH_GITHUB_NAME'] = array('callbackfile' => 'github', 'picto' => 'github', 'urlforapp' => 'OAUTH_GITHUB_DESC', 'name'=>'GitHub', 'urlforcredentials'=>'https://github.com/settings/developers', 'availablescopes'=>'user,public_repo', 'returnurl'=>'/core/modules/oauth/github_oauthcallback.php'); -$supportedoauth2array['OAUTH_MICROSOFT_NAME'] = array('callbackfile' => 'microsoft', 'picto' => 'microsoft', 'urlforapp' => 'OAUTH_MICROSOFT_DESC', 'name'=>'Microsoft', 'urlforcredentials'=>'https://portal.azure.com/', 'availablescopes'=>'openid,offline_access,profile,email,User.Read,https://outlook.office.com/IMAP.AccessAsUser.All,https://outlook.office.com/SMTP.Send', 'returnurl'=>'/core/modules/oauth/microsoft_oauthcallback.php'); +$supportedoauth2array['OAUTH_MICROSOFT_NAME'] = array('callbackfile' => 'microsoft', 'picto' => 'microsoft', 'urlforapp' => 'OAUTH_MICROSOFT_DESC', 'name'=>'Microsoft', 'urlforcredentials'=>'https://portal.azure.com/', 'availablescopes'=>'openid,offline_access,profile,email,User.Read,https://outlook.office365.com/IMAP.AccessAsUser.All,https://outlook.office365.com/SMTP.Send', 'returnurl'=>'/core/modules/oauth/microsoft_oauthcallback.php'); if (getDolGlobalInt('MAIN_FEATURES_LEVEL') >= 2) { $supportedoauth2array['OAUTH_OTHER_NAME'] = array('callbackfile' => 'generic', 'picto' => 'generic', 'urlforapp' => 'OAUTH_OTHER_DESC', 'name'=>'Other', 'urlforcredentials'=>'', 'availablescopes'=>'Standard', 'returnurl'=>'/core/modules/oauth/generic_oauthcallback.php'); // See https://learn.microsoft.com/fr-fr/azure/active-directory/develop/quickstart-register-app#register-an-application diff --git a/htdocs/includes/OAuth/OAuth2/Service/Microsoft.php b/htdocs/includes/OAuth/OAuth2/Service/Microsoft.php index 90c20405e6b..6c3b18b3c0f 100644 --- a/htdocs/includes/OAuth/OAuth2/Service/Microsoft.php +++ b/htdocs/includes/OAuth/OAuth2/Service/Microsoft.php @@ -38,8 +38,8 @@ class Microsoft extends AbstractService const SCOPE_APPLICATIONS = 'applications'; const SCOPE_APPLICATIONS_CREATE = 'applications_create'; const SCOPE_IMAP = 'imap'; - const SOCPE_IMAP_ACCESSASUSERALL = 'https://outlook.office.com/IMAP.AccessAsUser.All'; - const SOCPE_SMTPSEND = 'https://outlook.office.com/SMTP.Send'; + const SOCPE_IMAP_ACCESSASUSERALL = 'https://outlook.office365.com/IMAP.AccessAsUser.All'; + const SOCPE_SMTPSEND = 'https://outlook.office365.com/SMTP.Send'; const SOCPE_USERREAD = 'User.Read'; const SOCPE_MAILREAD = 'Mail.Read'; const SOCPE_MAILSEND = 'Mail.Send'; From c14471e41980d0ff5fa03244cd914d4cd450c533 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 11 Apr 2023 15:39:38 +0200 Subject: [PATCH 32/42] css --- htdocs/projet/list.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/htdocs/projet/list.php b/htdocs/projet/list.php index 26119927fda..e7e8cc40b68 100644 --- a/htdocs/projet/list.php +++ b/htdocs/projet/list.php @@ -1858,8 +1858,8 @@ while ($i < $imaxinloop) { } // Email MsgID if (!empty($arrayfields['p.email_msgid']['checked'])) { - print ''; - print $obj->email_msgid; + print ''; + print dol_escape_htmltag($obj->email_msgid); print ''; if (!$i) $totalarray['nbfield']++; } From ce8a8c617c750ef4f66843eb725325d1b901289d Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 11 Apr 2023 17:24:19 +0200 Subject: [PATCH 33/42] Fix Move of message with PHP-IMAP, Fix substitution for fetch thirdparty Fix php8 compatibility --- .../class/emailcollector.class.php | 130 +++++++++++++++--- 1 file changed, 111 insertions(+), 19 deletions(-) diff --git a/htdocs/emailcollector/class/emailcollector.class.php b/htdocs/emailcollector/class/emailcollector.class.php index 1b7c7ef2f27..ac1f1cf4cab 100644 --- a/htdocs/emailcollector/class/emailcollector.class.php +++ b/htdocs/emailcollector/class/emailcollector.class.php @@ -868,7 +868,7 @@ class EmailCollector extends CommonObject /** * overwitePropertiesOfObject * - * @param object $object Current object + * @param object $object Current object we will set ->properties * @param string $actionparam Action parameters * @param string $messagetext Body * @param string $subject Subject @@ -883,8 +883,13 @@ class EmailCollector extends CommonObject // Overwrite values with values extracted from source email // $this->actionparam = 'opportunity_status=123;abc=EXTRACT:BODY:....' $arrayvaluetouse = dolExplodeIntoArray($actionparam, ';', '='); + + $tmp = array(); + + // Loop on each property set into actionparam foreach ($arrayvaluetouse as $propertytooverwrite => $valueforproperty) { - $tmpclass = ''; $tmpproperty = ''; + $tmpclass = ''; + $tmpproperty = ''; $tmparray = explode('.', $propertytooverwrite); if (count($tmparray) == 2) { $tmpclass = $tmparray[0]; @@ -939,13 +944,21 @@ class EmailCollector extends CommonObject if (preg_match('/^options_/', $tmpproperty)) { $object->array_options[preg_replace('/^options_/', '', $tmpproperty)] = $this->decodeSMTPSubject($valueextracted); } else { - $object->$tmpproperty = $this->decodeSMTPSubject($valueextracted); + if (property_exists($object, $tmpproperty)) { + $object->$tmpproperty = $this->decodeSMTPSubject($valueextracted); + } else { + $tmp[$tmpproperty] = $this->decodeSMTPSubject($valueextracted); + } } } else { // extract from BODY if (preg_match('/^options_/', $tmpproperty)) { $object->array_options[preg_replace('/^options_/', '', $tmpproperty)] = $this->decodeSMTPSubject($valueextracted); } else { - $object->$tmpproperty = $this->decodeSMTPSubject($valueextracted); + if (property_exists($object, $tmpproperty)) { + $object->$tmpproperty = $this->decodeSMTPSubject($valueextracted); + } else { + $tmp[$tmpproperty] = $this->decodeSMTPSubject($valueextracted); + } } } if (preg_match('/^options_/', $tmpproperty)) { @@ -955,7 +968,11 @@ class EmailCollector extends CommonObject } } else { // Regex not found - $object->$tmpproperty = null; + if (property_exists($object, $tmpproperty)) { + $object->$tmpproperty = null; + } else { + $tmp[$tmpproperty] = null; + } $operationslog .= '
Regex /'.dol_escape_htmltag($regexstring).'/'.dol_escape_htmltag($regexoptions).' into '.strtolower($sourcefield).' -> not found, so property '.dol_escape_htmltag($tmpproperty).' is set to null.'; } @@ -970,7 +987,11 @@ class EmailCollector extends CommonObject if (preg_match('/^options_/', $tmpproperty)) { $valuecurrent = $object->array_options[preg_replace('/^options_/', '', $tmpproperty)]; } else { - $valuecurrent = $object->$tmpproperty; + if (property_exists($object, $tmpproperty)) { + $valuecurrent = $object->$tmpproperty; + } else { + $valuecurrent = $tmp[$tmpproperty]; + } } if ($regforregex[1] == 'SET' || empty($valuecurrent)) { @@ -979,24 +1000,38 @@ class EmailCollector extends CommonObject $matcharray = array(); preg_match_all('/__([a-z0-9]+(?:_[a-z0-9]+)?)__/i', $valuetouse, $matcharray); //var_dump($tmpproperty.' - '.$object->$tmpproperty.' - '.$valuetouse); var_dump($matcharray); - if (is_array($matcharray[1])) { // $matcharray[1] is array with list of substitution key found without the __ + if (is_array($matcharray[1])) { // $matcharray[1] is an array with the list of substitution key found without the __X__ syntax into the SET entry foreach ($matcharray[1] as $keytoreplace) { - if ($keytoreplace && isset($object->$keytoreplace)) { - $substitutionarray['__'.$keytoreplace.'__'] = $object->$keytoreplace; + if ($keytoreplace) { + if (preg_match('/^options_/', $keytoreplace)) { + $substitutionarray['__'.$keytoreplace.'__'] = $object->array_options[preg_replace('/^options_/', '', $keytoreplace)]; + } else { + if (property_exists($object, $keytoreplace)) { + $substitutionarray['__'.$keytoreplace.'__'] = $object->$keytoreplace; + } else { + $substitutionarray['__'.$keytoreplace.'__'] = $tmp[$keytoreplace]; + } + } } } } //var_dump($substitutionarray); - dol_syslog(var_export($substitutionarray, true)); + dol_syslog('substitutionarray='.var_export($substitutionarray, true)); //var_dump($substitutionarray); $valuetouse = make_substitutions($valuetouse, $substitutionarray); if (preg_match('/^options_/', $tmpproperty)) { $object->array_options[preg_replace('/^options_/', '', $tmpproperty)] = $valuetouse; - } else { - $object->$tmpproperty = $valuetouse; - } - $operationslog .= '
Set value '.dol_escape_htmltag($valuetouse).' into variable '.dol_escape_htmltag($tmpproperty); + $operationslog .= '
Set value '.dol_escape_htmltag($valuetouse).' into object->array_options['.dol_escape_htmltag(preg_replace('/^options_/', '', $tmpproperty)).']'; + } else { + if (property_exists($object, $tmpproperty)) { + $object->$tmpproperty = $valuetouse; + } else { + $tmp[$tmpproperty] = $valuetouse; + } + + $operationslog .= '
Set value '.dol_escape_htmltag($valuetouse).' into object->'.dol_escape_htmltag($tmpproperty); + } } } else { $errorforthisaction++; @@ -1411,6 +1446,9 @@ class EmailCollector extends CommonObject try { //var_dump($Query->count()); + if ($mode > 0) { + $Query->leaveUnread(); + } $arrayofemail = $Query->limit($this->maxemailpercollect)->setFetchOrder("asc")->get(); //var_dump($arrayofemail); } catch (Exception $e) { @@ -1957,6 +1995,7 @@ class EmailCollector extends CommonObject $result = $contactstatic->fetch(0, null, '', $from); if ($result > 0) { + dol_syslog("We found a contact with the email ".$from); $contactid = $contactstatic->id; $contactfoundby = 'email of contact ('.$from.')'; if (empty($thirdpartyid) && $contactstatic->socid > 0) { @@ -1972,12 +2011,44 @@ class EmailCollector extends CommonObject if (empty($thirdpartyid)) { // Try to find thirdparty using email $result = $thirdpartystatic->fetch(0, '', '', '', '', '', '', '', '', '', $from); if ($result > 0) { + dol_syslog("We found a thirdparty with the email ".$from); + $thirdpartyid = $thirdpartystatic->id;; $thirdpartyfoundby = 'email ('.$from.')'; } } - // Do operations - if ($mode < 2) { + /* + if ($replyto) { + if (empty($contactid)) { // Try to find contact using email + $result = $contactstatic->fetch(0, null, '', $replyto); + + if ($result > 0) { + dol_syslog("We found a contact with the email ".$replyto); + $contactid = $contactstatic->id; + $contactfoundby = 'email of contact ('.$replyto.')'; + if (empty($thirdpartyid) && $contactstatic->socid > 0) { + $result = $thirdpartystatic->fetch($contactstatic->socid); + if ($result > 0) { + $thirdpartyid = $thirdpartystatic->id; + $thirdpartyfoundby = 'email of contact ('.$replyto.')'; + } + } + } + } + + if (empty($thirdpartyid)) { // Try to find thirdparty using email + $result = $thirdpartystatic->fetch(0, '', '', '', '', '', '', '', '', '', $replyto); + if ($result > 0) { + dol_syslog("We found a thirdparty with the email ".$replyto); + $thirdpartyid = $thirdpartystatic->id;; + $thirdpartyfoundby = 'email ('.$replyto.')'; + } + } + } + */ + + // Do operations (extract variables and creating data) + if ($mode < 2) { // 0=Mode production, 1=Mode test (read IMAP and try SQL update then rollback), 2=Mode test with no SQL updates foreach ($this->actions as $operation) { $errorforthisaction = 0; @@ -2118,6 +2189,23 @@ class EmailCollector extends CommonObject } if (!$errorforactions && ($idtouseforthirdparty || $emailtouseforthirdparty || $nametouseforthirdparty || $namealiastouseforthirdparty)) { + // We make another search on thirdparty + //$namealiastouseforthirdparty + + $tmpobject = new stdClass(); + $tmpobject->element == 'generic'; + $tmpobject->id = $idtouseforthirdparty; + $tmpobject->name = $nametouseforthirdparty; + $tmpobject->name_alias = $namealiastouseforthirdparty; + $tmpobject->email = $emailtouseforthirdparty; + + $this->overwritePropertiesOfObject($tmpobject, $operation['actionparam'], $messagetext, $subject, $header, $operationslog); + + $idtouseforthirdparty = $tmpobject->id; + $nametouseforthirdparty = $tmpobject->name; + $namealiastouseforthirdparty = $tmpobject->name_alias; + $emailtouseforthirdparty = $tmpobject->name_alias; + $result = $thirdpartystatic->fetch($idtouseforthirdparty, $nametouseforthirdparty, '', '', '', '', '', '', '', '', $emailtouseforthirdparty, $namealiastouseforthirdparty); if ($result < 0) { $errorforactions++; @@ -2555,6 +2643,8 @@ class EmailCollector extends CommonObject } } } else { + dol_syslog("Project already exists for msgid = ".dol_escape_htmltag($msgid).", so we do not recreate it."); + $operationslog .= '
Project already exists for msgid ='.dol_escape_htmltag($msgid); } } elseif ($operation['type'] == 'ticket') { @@ -2824,9 +2914,9 @@ class EmailCollector extends CommonObject // Error for email or not ? if (!$errorforactions) { - if ($targetdir && empty($mode)) { + if (!empty($targetdir) && empty($mode)) { if (empty($conf->global->MAIN_IMAP_USE_PHPIMAP)) { - dol_syslog("EmailCollector::doCollectOneCollector move message ".$imapemail." to ".$connectstringtarget, LOG_DEBUG); + dol_syslog("EmailCollector::doCollectOneCollector move message ".((string) $imapemail)." to ".$connectstringtarget, LOG_DEBUG); $res = imap_mail_move($connection, $imapemail, $targetdir, 0); if ($res == false) { $errorforemail++; @@ -2835,7 +2925,9 @@ class EmailCollector extends CommonObject dol_syslog(imap_last_error()); } } else { - // TODO Move mail using PHP-IMAP + // Move mail using PHP-IMAP + dol_syslog("EmailCollector::doCollectOneCollector move message ".($imapemail->getHeader()->get('subject'))." to ".$targetdir, LOG_DEBUG); + $imapemail->move($targetdir); } } else { if (empty($conf->global->MAIN_IMAP_USE_PHPIMAP)) { From 1b19a30386a38e23c0520f313408a3771ea6af33 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 11 Apr 2023 18:00:46 +0200 Subject: [PATCH 34/42] Debug email collector --- .../class/emailcollector.class.php | 41 ++++++++++++------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/htdocs/emailcollector/class/emailcollector.class.php b/htdocs/emailcollector/class/emailcollector.class.php index ac1f1cf4cab..97dbc20551d 100644 --- a/htdocs/emailcollector/class/emailcollector.class.php +++ b/htdocs/emailcollector/class/emailcollector.class.php @@ -964,7 +964,11 @@ class EmailCollector extends CommonObject if (preg_match('/^options_/', $tmpproperty)) { $operationslog .= '
Regex /'.dol_escape_htmltag($regexstring).'/'.dol_escape_htmltag($regexoptions).' into '.strtolower($sourcefield).' -> found '.dol_escape_htmltag(dol_trunc($object->array_options[preg_replace('/^options_/', '', $tmpproperty)], 128)); } else { - $operationslog .= '
Regex /'.dol_escape_htmltag($regexstring).'/'.dol_escape_htmltag($regexoptions).' into '.strtolower($sourcefield).' -> found '.dol_escape_htmltag(dol_trunc($object->$tmpproperty, 128)); + if (property_exists($object, $tmpproperty)) { + $operationslog .= '
Regex /'.dol_escape_htmltag($regexstring).'/'.dol_escape_htmltag($regexoptions).' into '.strtolower($sourcefield).' -> found '.dol_escape_htmltag(dol_trunc($object->$tmpproperty, 128)); + } else { + $operationslog .= '
Regex /'.dol_escape_htmltag($regexstring).'/'.dol_escape_htmltag($regexoptions).' into '.strtolower($sourcefield).' -> found '.dol_escape_htmltag(dol_trunc($tmp[$tmpproperty], 128)); + } } } else { // Regex not found @@ -2059,6 +2063,8 @@ class EmailCollector extends CommonObject continue; } + $operationslog .= '
* Process operation '.$operation['type']; + // Make Operation dol_syslog("Execute action ".$operation['type']." actionparam=".$operation['actionparam'].' thirdpartystatic->id='.$thirdpartystatic->id.' contactstatic->id='.$contactstatic->id.' projectstatic->id='.$projectstatic->id); dol_syslog("Execute action fk_element_id=".$fk_element_id." fk_element_type=".$fk_element_type); // If a Dolibarr tracker id is found, we should now the id of object @@ -2096,6 +2102,8 @@ class EmailCollector extends CommonObject $emailtouseforthirdparty = ''; $namealiastouseforthirdparty = ''; + $operationslog .= '
Loop on each property to set into actionparam'; + // $actionparam = 'param=SET:aaa' or 'param=EXTRACT:BODY:....' $arrayvaluetouse = dolExplodeIntoArray($actionparam, ';', '='); foreach ($arrayvaluetouse as $propertytooverwrite => $valueforproperty) { @@ -2127,21 +2135,21 @@ class EmailCollector extends CommonObject if ($propertytooverwrite == 'id') { $idtouseforthirdparty = isset($regforval[count($regforval) - 1]) ? trim($regforval[count($regforval) - 1]) : null; - $operationslog .= '
Regex /'.dol_escape_htmltag($regexstring).'/ms into '.strtoupper($sourcefield).' -> Found idtouseforthirdparty='.dol_escape_htmltag($idtouseforthirdparty); + $operationslog .= '
propertytooverwrite='.$propertytooverwrite.' Regex /'.dol_escape_htmltag($regexstring).'/ms into '.strtoupper($sourcefield).' -> Found idtouseforthirdparty='.dol_escape_htmltag($idtouseforthirdparty); } elseif ($propertytooverwrite == 'email') { $emailtouseforthirdparty = isset($regforval[count($regforval) - 1]) ? trim($regforval[count($regforval) - 1]) : null; - $operationslog .= '
Regex /'.dol_escape_htmltag($regexstring).'/ms into '.strtoupper($sourcefield).' -> Found emailtouseforthirdparty='.dol_escape_htmltag($emailtouseforthirdparty); + $operationslog .= '
propertytooverwrite='.$propertytooverwrite.' Regex /'.dol_escape_htmltag($regexstring).'/ms into '.strtoupper($sourcefield).' -> Found emailtouseforthirdparty='.dol_escape_htmltag($emailtouseforthirdparty); } elseif ($propertytooverwrite == 'name') { $nametouseforthirdparty = isset($regforval[count($regforval) - 1]) ? trim($regforval[count($regforval) - 1]) : null; - $operationslog .= '
Regex /'.dol_escape_htmltag($regexstring).'/ms into '.strtoupper($sourcefield).' -> Found nametouseforthirdparty='.dol_escape_htmltag($nametouseforthirdparty); + $operationslog .= '
propertytooverwrite='.$propertytooverwrite.' Regex /'.dol_escape_htmltag($regexstring).'/ms into '.strtoupper($sourcefield).' -> Found nametouseforthirdparty='.dol_escape_htmltag($nametouseforthirdparty); } elseif ($propertytooverwrite == 'name_alias') { - $nametouseforthirdparty = isset($regforval[count($regforval) - 1]) ? trim($regforval[count($regforval) - 1]) : null; + $namealiastouseforthirdparty = isset($regforval[count($regforval) - 1]) ? trim($regforval[count($regforval) - 1]) : null; - $operationslog .= '
Regex /'.dol_escape_htmltag($regexstring).'/ms into '.strtoupper($sourcefield).' -> Found namealiastouseforthirdparty='.dol_escape_htmltag($namealiastouseforthirdparty); + $operationslog .= '
propertytooverwrite='.$propertytooverwrite.' Regex /'.dol_escape_htmltag($regexstring).'/ms into '.strtoupper($sourcefield).' -> Found namealiastouseforthirdparty='.dol_escape_htmltag($namealiastouseforthirdparty); } else { - $operationslog .= '
Regex /'.dol_escape_htmltag($regexstring).'/ms into '.strtoupper($sourcefield).' -> We discard this, not used to search existing thirdparty'; + $operationslog .= '
propertytooverwrite='.$propertytooverwrite.' Regex /'.dol_escape_htmltag($regexstring).'/ms into '.strtoupper($sourcefield).' -> We discard this, not used to search existing thirdparty'; } } else { // Regex not found @@ -2150,7 +2158,7 @@ class EmailCollector extends CommonObject $emailtouseforthirdparty = null; $namealiastouseforthirdparty = null; - $operationslog .= '
Regex /'.dol_escape_htmltag($regexstring).'/ms into '.strtoupper($sourcefield).' -> Not found'; + $operationslog .= '
propertytooverwrite='.$propertytooverwrite.' Regex /'.dol_escape_htmltag($regexstring).'/ms into '.strtoupper($sourcefield).' -> Not found'; } //var_dump($object->$tmpproperty);exit; } else { @@ -2166,19 +2174,19 @@ class EmailCollector extends CommonObject if ($propertytooverwrite == 'id') { $idtouseforthirdparty = $reg[2]; - $operationslog .= '
We set property idtouseforthrdparty='.dol_escape_htmltag($idtouseforthirdparty); + $operationslog .= '
propertytooverwrite='.$propertytooverwrite.'We set property idtouseforthrdparty='.dol_escape_htmltag($idtouseforthirdparty); } elseif ($propertytooverwrite == 'email') { $emailtouseforthirdparty = $reg[2]; - $operationslog .= '
We set property emailtouseforthrdparty='.dol_escape_htmltag($emailtouseforthirdparty); + $operationslog .= '
propertytooverwrite='.$propertytooverwrite.'We set property emailtouseforthrdparty='.dol_escape_htmltag($emailtouseforthirdparty); } elseif ($propertytooverwrite == 'name') { $nametouseforthirdparty = $reg[2]; - $operationslog .= '
We set property nametouseforthirdparty='.dol_escape_htmltag($nametouseforthirdparty); + $operationslog .= '
propertytooverwrite='.$propertytooverwrite.'We set property nametouseforthirdparty='.dol_escape_htmltag($nametouseforthirdparty); } elseif ($propertytooverwrite == 'name_alias') { $namealiastouseforthirdparty = $reg[2]; - $operationslog .= '
We set property namealiastouseforthirdparty='.dol_escape_htmltag($namealiastouseforthirdparty); + $operationslog .= '
propertytooverwrite='.$propertytooverwrite.'We set property namealiastouseforthirdparty='.dol_escape_htmltag($namealiastouseforthirdparty); } } else { $errorforactions++; @@ -2190,7 +2198,7 @@ class EmailCollector extends CommonObject if (!$errorforactions && ($idtouseforthirdparty || $emailtouseforthirdparty || $nametouseforthirdparty || $namealiastouseforthirdparty)) { // We make another search on thirdparty - //$namealiastouseforthirdparty + $operationslog .= '
We have this data to search thirdparty: '.$idtouseforthirdparty.' '.$emailtouseforthirdparty.' '.$nametouseforthirdparty.' '.$namealiastouseforthirdparty; $tmpobject = new stdClass(); $tmpobject->element == 'generic'; @@ -2204,7 +2212,9 @@ class EmailCollector extends CommonObject $idtouseforthirdparty = $tmpobject->id; $nametouseforthirdparty = $tmpobject->name; $namealiastouseforthirdparty = $tmpobject->name_alias; - $emailtouseforthirdparty = $tmpobject->name_alias; + $emailtouseforthirdparty = $tmpobject->email; + + $operationslog .= '
We try to search existing thirdparty with '.$idtouseforthirdparty.' '.$emailtouseforthirdparty.' '.$nametouseforthirdparty.' '.$namealiastouseforthirdparty; $result = $thirdpartystatic->fetch($idtouseforthirdparty, $nametouseforthirdparty, '', '', '', '', '', '', '', '', $emailtouseforthirdparty, $namealiastouseforthirdparty); if ($result < 0) { @@ -2224,6 +2234,7 @@ class EmailCollector extends CommonObject dol_syslog("Third party with id=".$idtouseforthirdparty." email=".$emailtouseforthirdparty." name=".$nametouseforthirdparty." name_alias=".$namealiastouseforthirdparty." was not found. We try to create it."); // Create thirdparty + $thirdpartystatic = new Societe($db); $thirdpartystatic->name = $nametouseforthirdparty; if (!empty($namealiastouseforthirdparty)) { if ($namealiastouseforthirdparty != $nametouseforthirdparty) { @@ -2259,6 +2270,8 @@ class EmailCollector extends CommonObject } } else { dol_syslog("One and only one existing third party has been found"); + + $operationslog .= '
Thirdparty already exists with id = '.dol_escape_htmltag($thirdpartystatic->id); } } } From 5f1503d7eacfae0691c3b135d8d5e9ffb1e6d21c Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 11 Apr 2023 18:05:47 +0200 Subject: [PATCH 35/42] Log --- htdocs/emailcollector/class/emailcollector.class.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/htdocs/emailcollector/class/emailcollector.class.php b/htdocs/emailcollector/class/emailcollector.class.php index 97dbc20551d..7391d6e6acd 100644 --- a/htdocs/emailcollector/class/emailcollector.class.php +++ b/htdocs/emailcollector/class/emailcollector.class.php @@ -1066,7 +1066,7 @@ class EmailCollector extends CommonObject require_once DOL_DOCUMENT_ROOT.'/includes/webklex/php-imap/vendor/autoload.php'; } - dol_syslog("EmailCollector::doCollectOneCollector start for id=".$this->id." - ".$this->ref, LOG_DEBUG); + dol_syslog("EmailCollector::doCollectOneCollector start for id=".$this->id." - ".$this->ref, LOG_INFO); $langs->loadLangs(array("project", "companies", "mails", "errors", "ticket", "agenda", "commercial")); @@ -2994,6 +2994,7 @@ class EmailCollector extends CommonObject $client->disconnect(); } else { if (empty($mode) && empty($error)) { + dol_syslog("Expunge", LOG_DEBUG); imap_expunge($connection); // To validate any move } imap_close($connection); @@ -3022,7 +3023,7 @@ class EmailCollector extends CommonObject $this->update($user); } - dol_syslog("EmailCollector::doCollectOneCollector end", LOG_DEBUG); + dol_syslog("EmailCollector::doCollectOneCollector end", LOG_INFO); return $error ? -1 : 1; } From 66923ccf8c609cd3ec1ab32fd0b43cdee386abbd Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 11 Apr 2023 18:47:54 +0200 Subject: [PATCH 36/42] Code comment --- htdocs/includes/webklex/php-imap/vendor/autoload.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/htdocs/includes/webklex/php-imap/vendor/autoload.php b/htdocs/includes/webklex/php-imap/vendor/autoload.php index 298bedf9b9c..12f2cb6cb45 100644 --- a/htdocs/includes/webklex/php-imap/vendor/autoload.php +++ b/htdocs/includes/webklex/php-imap/vendor/autoload.php @@ -17,6 +17,13 @@ return ComposerAutoloaderInit4da13270269c89a28e472e1f7324e6d1::getLoader(); // Add class/method of PHP8 for compatibility with older versions of PHP require_once(__DIR__.'/symfony/polyfill-php80/bootstrap.php'); +//'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php', +//'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php', +//'60799491728b879e74601d83e38b2cad' => $vendorDir . '/illuminate/collections/helpers.php', +//'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php', +//'6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php', +require_once(__DIR__.'/illuminate/collections/helpers.php'); + spl_autoload_register(function ($class_name) { // Enable this to detect what we need for require_once From 9ffc86a8726974001f33f631b076aa686b8d1882 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 11 Apr 2023 18:49:15 +0200 Subject: [PATCH 37/42] Remove warning 8.2 --- htdocs/core/class/conf.class.php | 19 ++++++++++++++++++- test/phpunit/AllTests.php | 2 +- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/htdocs/core/class/conf.class.php b/htdocs/core/class/conf.class.php index 756b2771e39..b3991ecfcf2 100644 --- a/htdocs/core/class/conf.class.php +++ b/htdocs/core/class/conf.class.php @@ -109,6 +109,24 @@ class Conf public $tzuserinputkey = 'tzserver'; // Use 'tzuserrel' to always store date in GMT and show date in time zone of user. + // TODO Remove this part. + public $fournisseur; + public $product; + public $service; + public $contrat; + public $actions; + public $agenda; + public $commande; + public $propal; + public $facture; + public $user; + public $adherent; + public $bank; + public $notification; + public $expensereport; + public $productbatch; + + /** * Constructor @@ -165,7 +183,6 @@ class Conf $this->commande = new stdClass(); $this->propal = new stdClass(); $this->facture = new stdClass(); - $this->contrat = new stdClass(); $this->user = new stdClass(); $this->adherent = new stdClass(); $this->bank = new stdClass(); diff --git a/test/phpunit/AllTests.php b/test/phpunit/AllTests.php index ba4d743f603..84ea7a42a6e 100644 --- a/test/phpunit/AllTests.php +++ b/test/phpunit/AllTests.php @@ -24,7 +24,7 @@ * \remarks To run this script as CLI: phpunit filename.php */ print "PHP Version: ".phpversion()."\n"; -print "Memory: ". ini_get('memory_limit')."\n"; +print "Memory limit: ". ini_get('memory_limit')."\n"; global $conf,$user,$langs,$db; //define('TEST_DB_FORCE_TYPE','mysql'); // This is to force using mysql driver From 45d79232d20c5275696132d9829ae0d66d381d90 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 11 Apr 2023 19:36:19 +0200 Subject: [PATCH 38/42] Clean code --- htdocs/product/class/product.class.php | 1 + test/phpunit/MouvementStockTest.php | 35 ++++++++++++++------------ 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/htdocs/product/class/product.class.php b/htdocs/product/class/product.class.php index 67002e48efb..db5ebf7bf4c 100644 --- a/htdocs/product/class/product.class.php +++ b/htdocs/product/class/product.class.php @@ -871,6 +871,7 @@ class Product extends CommonObject $langs->load("products"); $error++; $this->error = "ErrorProductAlreadyExists"; + dol_syslog(get_class($this)."::Create fails, ref ".$this->ref." already exists"); } } else { $error++; diff --git a/test/phpunit/MouvementStockTest.php b/test/phpunit/MouvementStockTest.php index a781ea78e12..d084f50d6e0 100644 --- a/test/phpunit/MouvementStockTest.php +++ b/test/phpunit/MouvementStockTest.php @@ -146,41 +146,44 @@ class MouvementStockTest extends PHPUnit\Framework\TestCase // We create a product for tests $product0=new Product($db); $product0->initAsSpecimen(); - $product0->ref.=' 0'; - $product0->label.=' 0'; + $product0->ref.=' phpunit 0'; + $product0->label.=' phpunit 0'; $product0->status_batch = 1; $product0id=$product0->create($user); + print __METHOD__." product0id=".$product0id."\n"; + $this->assertGreaterThan(0, $product0id, 'Failed to create product'); + $product1=new Product($db); $product1->initAsSpecimen(); - $product1->ref.=' 1'; - $product1->label.=' 1'; + $product1->ref.=' phpunit 1'; + $product1->label.=' phpunit 1'; $product1id=$product1->create($user); $product2=new Product($db); $product2->initAsSpecimen(); - $product2->ref.=' 2'; - $product2->label.=' 2'; + $product2->ref.=' phpunit 2'; + $product2->label.=' phpunit 2'; $product2id=$product2->create($user); // We create a product for tests $warehouse0=new Entrepot($db); $warehouse0->initAsSpecimen(); - $warehouse0->label.=' 0'; - $warehouse0->description.=' 0'; + $warehouse0->label.=' phpunit 0'; + $warehouse0->description.=' phpunit 0'; $warehouse0->statut = 0; $warehouse0id=$warehouse0->create($user); $warehouse1=new Entrepot($db); $warehouse1->initAsSpecimen(); - $warehouse1->label.=' 1'; - $warehouse1->description.=' 1'; + $warehouse1->label.=' phpunit 1'; + $warehouse1->description.=' phpunit 1'; $warehouse1id=$warehouse1->create($user); $warehouse2=new Entrepot($db); $warehouse2->initAsSpecimen(); - $warehouse2->label.=' 2'; - $warehouse2->description.=' 2'; + $warehouse2->label.=' phpunit 2'; + $warehouse2->description.=' phpunit 2'; $warehouse2id=$warehouse2->create($user); $localobject=new MouvementStock($this->savdb); @@ -188,10 +191,10 @@ class MouvementStockTest extends PHPUnit\Framework\TestCase $datetest1 = dol_mktime(0, 0, 0, 1, 1, 2000); $datetest2 = dol_mktime(0, 0, 0, 1, 2, 2000); - // Create an input movement movement (type = 3) with value for eatby date and a lot + // Create an input movement movement (type = 3) with value for eatby date and a lot $datetest1 $result=$localobject->reception($user, $product0id, $warehouse0id, 5, 999, 'Movement for unit test with batch', $datetest1, $datetest1, 'anotyetuselotnumberA', '', 0, 'Inventory Code Test with batch'); print __METHOD__." result=".$result."\n"; - $this->assertGreaterThan(0, $result, 'Failed to create a movement with a lot number of product with status_batch=1'); + $this->assertGreaterThan(0, $result, 'Failed to create a movement with a lot number '.$datetest1.' for product id='.$product0id.' with status_batch=1'); $result=$localobject->reception($user, $product0id, $warehouse0id, 5, 999, 'Movement for unit test with batch', $datetest1, $datetest1, 'anotyetuselotnumberB', '', 0, 'Inventory Code Test with batch'); print __METHOD__." result=".$result."\n"; @@ -250,12 +253,12 @@ class MouvementStockTest extends PHPUnit\Framework\TestCase // Create an output movement (type = 1) of price 9.7 -> should update PMP to 9.9/9.7 = 9.8 $result=$localobject->_create($user, $product1id, $warehouse2id, 1, 0, 0, 'Input from transfer wh 2', 'Transfert X 2'); print __METHOD__." result=".$result."\n"; - $this->assertGreaterThan(0, $result); + $this->assertGreaterThan(0, $result, 'Test create A'); // Create an output movement (type = 1) of price 9.7 -> should update PMP to 9.9/9.7 = 9.8 $result=$localobject->_create($user, $product1id, $warehouse2id, -2, 1, 0, 'Output from transfer wh 2', 'Transfert Y 2'); print __METHOD__." result=".$result."\n"; - $this->assertGreaterThan(0, $result); + $this->assertGreaterThan(0, $result, 'Test create B'); return $localobject; } From a8cd44cafccff61845b15f335840865a31bacc59 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 11 Apr 2023 22:17:15 +0200 Subject: [PATCH 39/42] Clean code --- test/phpunit/CategorieTest.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/phpunit/CategorieTest.php b/test/phpunit/CategorieTest.php index 3bd870914e3..1faab723bb2 100644 --- a/test/phpunit/CategorieTest.php +++ b/test/phpunit/CategorieTest.php @@ -259,11 +259,12 @@ class CategorieTest extends PHPUnit\Framework\TestCase $langs=$this->savlangs; $db=$this->savdb; - $localobject->note='New note after update'; + $localobject->note_private ='New note after update'; $result=$localobject->update($user); print __METHOD__." id=".$localobject->id." result=".$result."\n"; $this->assertGreaterThan(0, $result); + return $localobject; } @@ -288,8 +289,6 @@ class CategorieTest extends PHPUnit\Framework\TestCase print __METHOD__." id=".$localobject->id." result=".$result."\n"; $this->assertLessThan($result, 0); */ - $localobject2=new Categorie($db); - $localobject2->initAsSpecimen(); $retarray=$localobject->liste_photos('/'); print __METHOD__." retarray size=".count($retarray)."\n"; From bc711b05040315220a359cbb0bcc772cae49ff02 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 11 Apr 2023 22:20:54 +0200 Subject: [PATCH 40/42] FIX Deployment of external module failed with copy dir error. --- htdocs/admin/modules.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/admin/modules.php b/htdocs/admin/modules.php index ac8ff6161a9..f6e281f5e2d 100644 --- a/htdocs/admin/modules.php +++ b/htdocs/admin/modules.php @@ -231,7 +231,7 @@ if ($action == 'install') { if (!$error) { @dol_delete_dir_recursive($dirins.'/'.$modulenameval); // delete the target directory $submodulenamedir = $conf->admin->dir_temp.'/'.$tmpdir.'/'.$modulenameval; - if (!dol_is_dir($modulenamedir)) { + if (!dol_is_dir($submodulenamedir)) { $submodulenamedir = $conf->admin->dir_temp.'/'.$tmpdir.'/htdocs/'.$modulenameval; } dol_syslog("We copy now directory ".$submodulenamedir." into target dir ".$dirins.'/'.$modulenameval); From c552633095140f68ece17b3d6a6b715a15e1964a Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 12 Apr 2023 00:13:06 +0200 Subject: [PATCH 41/42] Log --- htdocs/admin/modules.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/admin/modules.php b/htdocs/admin/modules.php index f6e281f5e2d..226ab6d70b3 100644 --- a/htdocs/admin/modules.php +++ b/htdocs/admin/modules.php @@ -443,7 +443,7 @@ foreach ($modulesdir as $dir) { dol_syslog("Failed to load ".$dir.$file." ".$e->getMessage(), LOG_ERR); } } else { - print "Warning bad descriptor file : ".$dir.$file." (Class ".$modName." not found into file)
"; + print "admin/modules.php Warning bad descriptor file : ".$dir.$file." (Class ".$modName." not found into file)
"; } } catch (Exception $e) { dol_syslog("Failed to load ".$dir.$file." ".$e->getMessage(), LOG_ERR); From 28c625dc6b286835c09e5660e1b83232131c3fbf Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 12 Apr 2023 03:38:25 +0200 Subject: [PATCH 42/42] NEW Show main currency in company info user tooltip --- htdocs/main.inc.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/htdocs/main.inc.php b/htdocs/main.inc.php index ce7a7a9ec8d..8ca8d5e06af 100644 --- a/htdocs/main.inc.php +++ b/htdocs/main.inc.php @@ -2312,7 +2312,9 @@ function top_menu_user($hideloginname = 0, $urllogout = '') } $dropdownBody .= '
'.$langs->trans("VATIntraShort").': '.dol_print_profids(getDolGlobalString("MAIN_INFO_TVAINTRA"), 'VAT').''; $dropdownBody .= '
'.$langs->trans("Country").': '.($mysoc->country_code ? $langs->trans("Country".$mysoc->country_code) : '').''; - + if (isModEnabled('multicurrency')) { + $dropdownBody .= '
'.$langs->trans("Currency").': '.$conf->currency.''; + } $dropdownBody .= ''; $dropdownBody .= '
';