diff --git a/htdocs/install/mysql/migration/15.0.0-16.0.0.sql b/htdocs/install/mysql/migration/15.0.0-16.0.0.sql index b57cab5b4eb..c2cb299a63c 100644 --- a/htdocs/install/mysql/migration/15.0.0-16.0.0.sql +++ b/htdocs/install/mysql/migration/15.0.0-16.0.0.sql @@ -648,4 +648,7 @@ ALTER TABLE llx_paiement MODIFY COLUMN ext_payment_id varchar(255); ALTER TABLE llx_payment_donation MODIFY COLUMN ext_payment_id varchar(255); ALTER TABLE llx_prelevement_facture_demande MODIFY COLUMN ext_payment_id varchar(255); +ALTER TABLE llx_product ADD COLUMN fk_default_workstation integer DEFAULT NULL; + + diff --git a/htdocs/install/mysql/tables/llx_product.sql b/htdocs/install/mysql/tables/llx_product.sql index 83e8882caa7..9b124c8fede 100644 --- a/htdocs/install/mysql/tables/llx_product.sql +++ b/htdocs/install/mysql/tables/llx_product.sql @@ -107,4 +107,5 @@ create table llx_product mandatory_period tinyint DEFAULT 0, -- is used to signal to the user that the start and end dates are mandatory for this type of product the fk_product_type == 1 (service) (non-blocking action) fk_default_bom integer DEFAULT NULL + fk_default_workstation integer DEFAULT NULL )ENGINE=innodb; diff --git a/htdocs/langs/fr_FR/mrp.lang b/htdocs/langs/fr_FR/mrp.lang index e65244b584f..6211db86e72 100644 --- a/htdocs/langs/fr_FR/mrp.lang +++ b/htdocs/langs/fr_FR/mrp.lang @@ -91,6 +91,7 @@ WorkstationSetup = Configuration du module Poste de travail WorkstationSetupPage = Configuration du module Poste de travail WorkstationList=Liste des postes de travail WorkstationCreate=Ajouter un nouveau poste de travail +DefaultWorkstation=Poste de travail par défaut ConfirmEnableWorkstation=Voulez-vous vraiment activer le poste de travail %s? EnableAWorkstation=Activer le module Poste de travail ConfirmDisableWorkstation=Voulez-vous vraiment désactiver la station de travail %s? diff --git a/htdocs/product/card.php b/htdocs/product/card.php index d0b7e4ece73..087c7bc11ce 100644 --- a/htdocs/product/card.php +++ b/htdocs/product/card.php @@ -52,6 +52,7 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/modules/product/modules_product.class.php'; +require_once DOL_DOCUMENT_ROOT.'/workstation/class/workstation.class.php'; if (!empty($conf->propal->enabled)) { require_once DOL_DOCUMENT_ROOT.'/comm/propal/class/propal.class.php'; @@ -552,6 +553,7 @@ if (empty($reshook)) { $object->duration_value = $duration_value; $object->duration_unit = $duration_unit; $object->fk_default_warehouse = GETPOST('fk_default_warehouse'); + $object->fk_default_workstation = GETPOST('fk_default_workstation'); $object->seuil_stock_alerte = GETPOST('seuil_stock_alerte') ?GETPOST('seuil_stock_alerte') : 0; $object->desiredstock = GETPOST('desiredstock') ?GETPOST('desiredstock') : 0; $object->canvas = GETPOST('canvas'); @@ -704,6 +706,7 @@ if (empty($reshook)) { $object->status_batch = GETPOST('status_batch', 'aZ09'); $object->batch_mask = GETPOST('batch_mask', 'alpha'); $object->fk_default_warehouse = GETPOST('fk_default_warehouse'); + $object->fk_default_workstation = GETPOST('fk_default_workstation'); // removed from update view so GETPOST always empty /* $object->seuil_stock_alerte = GETPOST('seuil_stock_alerte'); @@ -1996,6 +1999,15 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) { print ''; */ } + + if($object->isService() && $conf->workstation->enabled) { + // Default workstation + print ''.$langs->trans("DefaultWorkstation").''; + print img_picto($langs->trans("DefaultWorkstation"), 'workstation', 'class="pictofixedwidth"'); + print $formproduct->selectWorkstations($object->fk_default_workstation, 'fk_default_workstation', 1); + print ''; + } + /* else { @@ -2475,6 +2487,15 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) { print ''; } + if($object->isService() && $conf->workstation->enabled) { + $workstation = new Workstation($db); + $workstation->fetch($object->fk_default_workstation); + + print ''.$langs->trans("DefaultWorkstation").''; + print (!empty($workstation->id) ? $workstation->getNomUrl(1) : ''); + print ''; + } + // Parent product. if (!empty($conf->variants->enabled) && ($object->isProduct() || $object->isService())) { $combination = new ProductCombination($db); diff --git a/htdocs/product/class/html.formproduct.class.php b/htdocs/product/class/html.formproduct.class.php index 4ede9e1e297..d6017877375 100644 --- a/htdocs/product/class/html.formproduct.class.php +++ b/htdocs/product/class/html.formproduct.class.php @@ -42,6 +42,7 @@ class FormProduct // Cache arrays public $cache_warehouses = array(); public $cache_lot = array(); + public $cache_workstations = array(); /** @@ -172,6 +173,63 @@ class FormProduct } } + /** + * Load in cache array list of workstations + * If fk_product is not 0, we do not use cache + * + * @param int $fk_product Add quantity of stock in label for product with id fk_product. Nothing if 0. + * @param array $exclude warehouses ids to exclude + * @param string $orderBy [='e.ref'] Order by + * @return int Nb of loaded lines, 0 if already loaded, <0 if KO + * @throws Exception + */ + public function loadWorkstations($fk_product = 0, $exclude = array(), $orderBy = 'w.ref') + { + global $conf, $langs; + + if (empty($fk_product) && count($this->cache_workstations)) { + return 0; // Cache already loaded and we do not want a list with information specific to a product + } + + $sql = "SELECT w.rowid, w.ref as label, w.type, w.nb_operators_required,w.thm_operator_estimated,w.thm_machine_estimated"; + $sql .= " FROM ".$this->db->prefix()."workstation_workstation as w"; + $sql .= " WHERE 1 = 1"; + if (!empty($fk_product) && $fk_product > 0) { + $sql .= " AND w.fk_product = ".((int) $fk_product); + } + $sql .= " AND w.entity IN (".getEntity('workstation').")"; + + if (is_array($exclude) && !empty($exclude)) { + $sql .= ' AND w.rowid NOT IN('.$this->db->sanitize(implode(',', $exclude)).')'; + } + + $sql .= " ORDER BY ".$orderBy; + + dol_syslog(get_class($this).'::loadWorkstations', LOG_DEBUG); + $resql = $this->db->query($sql); + if ($resql) { + $num = $this->db->num_rows($resql); + $i = 0; + while ($i < $num) { + $obj = $this->db->fetch_object($resql); + + $this->cache_workstations[$obj->rowid]['id'] = $obj->rowid; + $this->cache_workstations[$obj->rowid]['ref'] = $obj->ref; + $this->cache_workstations[$obj->rowid]['label'] = $obj->label; + $this->cache_workstations[$obj->rowid]['type'] = $obj->type; + $this->cache_workstations[$obj->rowid]['nb_operators_required'] = $obj->nb_operators_required; + $this->cache_workstations[$obj->rowid]['thm_operator_estimated'] = $obj->thm_operator_estimated; + $this->cache_workstations[$obj->rowid]['thm_machine_estimated'] = $obj->thm_machine_estimated; + $i++; + } + + return $num; + } else { + dol_print_error($this->db); + return -1; + } + } + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps /** * Return full path to current warehouse in $tab (recursive function) @@ -320,6 +378,105 @@ class FormProduct return $out; } + /** + * Return list of workstations + * + * @param string|int $selected Id of preselected warehouse ('' or '-1' for no value, 'ifone' and 'ifonenodefault' = select value if one value otherwise no value, '-2' to use the default value from setup) + * @param string $htmlname Name of html select html + * @param int $empty 1=Can be empty, 0 if not + * @param int $disabled 1=Select is disabled + * @param int $fk_product Add quantity of stock in label for product with id fk_product. Nothing if 0. + * @param string $empty_label Empty label if needed (only if $empty=1) + * @param int $forcecombo 1=Force combo iso ajax select2 + * @param array $events Events to add to select2 + * @param string $morecss Add more css classes to HTML select + * @param array $exclude Warehouses ids to exclude + * @param int $showfullpath 1=Show full path of name (parent ref into label), 0=Show only ref of current warehouse + * @param string $orderBy [='e.ref'] Order by + * @return string HTML select + * + * @throws Exception + */ + public function selectWorkstations($selected = '', $htmlname = 'idworkstations', $empty = 0, $disabled = 0, $fk_product = 0, $empty_label = '', $forcecombo = 0, $events = array(), $morecss = 'minwidth200', $exclude = array(), $showfullpath = 1, $orderBy = 'e.ref') + { + global $conf, $langs, $user, $hookmanager; + + dol_syslog(get_class($this)."::selectWorkstations $selected, $htmlname, $empty, $disabled, $fk_product, $empty_label, $forcecombo, $morecss", LOG_DEBUG); + + $out = ''; + if (!empty($fk_product) && $fk_product > 0) { + $this->cache_workstations = array(); + } + + $this->loadWorkstations($fk_product); + $nbofworkstations = count($this->cache_workstations); + + if ($conf->use_javascript_ajax && !$forcecombo) { + include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php'; + $comboenhancement = ajax_combobox($htmlname, $events); + $out .= $comboenhancement; + } + + if (strpos($htmlname, 'search_') !== 0) { + if (empty($user->fk_workstation) || $user->fk_workstation == -1) { + if (($selected == '-2' || $selected == 'ifone') && !empty($conf->global->MAIN_DEFAULT_WORKSTATION)) { + $selected = $conf->global->MAIN_DEFAULT_WORKSTATION; + } + } else { + if (($selected == '-2' || $selected == 'ifone') && !empty($conf->global->MAIN_DEFAULT_WORKSTATION)) { + $selected = $user->fk_workstation; + } + } + } + + $out .= '