diff --git a/htdocs/admin/delais.php b/htdocs/admin/delais.php
index 092ec5bc5fc..a74c23ee267 100644
--- a/htdocs/admin/delais.php
+++ b/htdocs/admin/delais.php
@@ -143,6 +143,12 @@ $modules = array(
'img' => 'holiday'
),
),
+ 'mrp' => array(
+ array(
+ 'code' => 'MAIN_DELAY_MRP',
+ 'img' => 'mrp'
+ ),
+ ),
);
$labelmeteo = array(0 => $langs->trans("No"), 1 => $langs->trans("Yes"), 2 => $langs->trans("OnMobileOnly"));
@@ -185,7 +191,6 @@ if ($action == 'update') {
}
}
}
-
dolibarr_set_const($db, "MAIN_DISABLE_METEO", GETPOST("MAIN_DISABLE_METEO"), 'chaine', 0, '', $conf->entity);
dolibarr_set_const($db, "MAIN_USE_METEO_WITH_PERCENTAGE", GETPOST("MAIN_USE_METEO_WITH_PERCENTAGE"), 'chaine', 0, '', $conf->entity);
diff --git a/htdocs/compta/facture/class/facture.class.php b/htdocs/compta/facture/class/facture.class.php
index dd0c94694c3..3139eb4ab11 100644
--- a/htdocs/compta/facture/class/facture.class.php
+++ b/htdocs/compta/facture/class/facture.class.php
@@ -5128,7 +5128,6 @@ class Facture extends CommonInvoice
if ($resql) {
$langs->load("bills");
$now = dol_now();
-
$response = new WorkboardResponse();
$response->warning_delay = $conf->facture->client->warning_delay / 60 / 60 / 24;
$response->label = $langs->trans("CustomerBillsUnpaid");
diff --git a/htdocs/core/class/conf.class.php b/htdocs/core/class/conf.class.php
index 137ea285287..ecaceb1d87b 100644
--- a/htdocs/core/class/conf.class.php
+++ b/htdocs/core/class/conf.class.php
@@ -377,6 +377,10 @@ class Conf extends stdClass
*/
public $category;
+ /**
+ * @var ?stdClass
+ */
+ public $mrp;
/**
* Constructor
@@ -1236,6 +1240,10 @@ class Conf extends stdClass
$this->holiday->approve = new stdClass();
$this->holiday->approve->warning_delay = getDolGlobalInt('MAIN_DELAY_HOLIDAYS') * 86400;
}
+ if (isset($this->mrp)) {
+ $this->mrp->progress = new stdClass();
+ $this->mrp->progress->warning_delay = getDolGlobalInt('MAIN_DELAY_MRP') * 86400;
+ }
if ((getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) && !getDolGlobalString('PRODUIT_MULTIPRICES_LIMIT')) {
$this->global->PRODUIT_MULTIPRICES_LIMIT = 5;
diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php
index 8268f5cba04..f45716fb67d 100644
--- a/htdocs/core/lib/functions.lib.php
+++ b/htdocs/core/lib/functions.lib.php
@@ -12203,7 +12203,6 @@ function natural_search($fields, $value, $mode = 0, $nofirstand = 0)
if (!is_array($fields)) {
$fields = array($fields);
}
-
$i1 = 0; // count the nb of "and" criteria added (all fields / criteria)
foreach ($crits as $crit) { // Loop on each AND criteria
$crit = trim($crit);
@@ -12341,7 +12340,6 @@ function natural_search($fields, $value, $mode = 0, $nofirstand = 0)
$i2++; // a criteria for 1 more field was added to string
}
}
-
if ($newres) {
$res = $res.($res ? ' AND ' : '').($i2 > 1 ? '(' : '').$newres.($i2 > 1 ? ')' : '');
}
diff --git a/htdocs/index.php b/htdocs/index.php
index b8e6d089292..c682ea55960 100644
--- a/htdocs/index.php
+++ b/htdocs/index.php
@@ -341,6 +341,12 @@ if (!getDolGlobalString('MAIN_DISABLE_GLOBAL_WORKBOARD') && getDolGlobalInt('MAI
$dashboardlines[$board->element] = $board->load_board($user);
}
+ if (isModEnabled('mrp')) {
+ include_once DOL_DOCUMENT_ROOT.'/mrp/class/mo.class.php';
+ $board = new Mo($db);
+ $dashboardlines[$board->element] = $board->load_board($user);
+ }
+
$object = new stdClass();
$parameters = array();
$action = '';
@@ -353,7 +359,6 @@ if (!getDolGlobalString('MAIN_DISABLE_GLOBAL_WORKBOARD') && getDolGlobalInt('MAI
if ($reshook == 0) {
$dashboardlines = array_merge($dashboardlines, $hookmanager->resArray);
}
-
/* Open object dashboard */
$dashboardgroup = array(
'action' =>
@@ -451,8 +456,14 @@ if (!getDolGlobalString('MAIN_DISABLE_GLOBAL_WORKBOARD') && getDolGlobalInt('MAI
'stats' =>
array('holiday'),
),
+ 'cubes' =>
+ array(
+ 'groupName' => 'Mo',
+ 'globalStatsKey' => 'mrp',
+ 'stats' =>
+ array('mo'),
+ ),
);
-
$object = new stdClass();
$parameters = array(
'dashboardgroup' => $dashboardgroup
@@ -475,7 +486,6 @@ if (!getDolGlobalString('MAIN_DISABLE_GLOBAL_WORKBOARD') && getDolGlobalInt('MAI
$valid_dashboardlines[$workboardid] = $tmp;
}
}
-
// We calculate $totallate. Must be defined before start of next loop because it is show in first fetch on next loop
foreach ($valid_dashboardlines as $board) {
if (is_numeric($board->nbtodo) && is_numeric($board->nbtodolate) && $board->nbtodolate > 0) {
@@ -527,7 +537,6 @@ if (!getDolGlobalString('MAIN_DISABLE_GLOBAL_WORKBOARD') && getDolGlobalInt('MAI
$openedDashBoard = '';
if (!empty($valid_dashboardlines)) {
$boxwork .= '
';
-
foreach ($dashboardgroup as $groupKey => $groupElement) {
$boards = array();
@@ -541,6 +550,7 @@ if (!getDolGlobalString('MAIN_DISABLE_GLOBAL_WORKBOARD') && getDolGlobalInt('MAI
}
}
+
if (!empty($boards)) {
if (!empty($groupElement['lang'])) {
$langs->load($groupElement['lang']);
@@ -583,7 +593,20 @@ if (!getDolGlobalString('MAIN_DISABLE_GLOBAL_WORKBOARD') && getDolGlobalInt('MAI
}
$textLateTitle = $langs->trans("NActionsLate", $board->nbtodolate);
- $textLateTitle .= ' ('.$langs->trans("Late").' = '.$langs->trans("DateReference").' > '.$langs->trans("DateToday").' '.(ceil(empty($board->warning_delay) ? 0 : $board->warning_delay) >= 0 ? '+' : '').ceil(empty($board->warning_delay) ? 0 : $board->warning_delay).' '.$langs->trans("days").')';
+
+ $dateOrder = '';
+ if ($board->id == 'mo') {
+ $dateOrder = $langs->trans("DateToday") . " > " . $langs->trans("DateReference");
+ } else {
+ $dateOrder = $langs->trans("DateReference") . " > " . $langs->trans("DateToday");
+ }
+ $warningDelay = ceil(empty($board->warning_delay) ? 0 : $board->warning_delay);
+ $sign = '';
+ if ($warningDelay >= 0) {
+ $sign = '+';
+ }
+
+ $textLateTitle .= " (" . $langs->trans("Late") . " = $dateOrder $sign$warningDelay " . $langs->trans("days") . ")";
if ($board->id == 'bank_account') {
$textLateTitle .= ' '.$langs->trans("IfYouDontReconcileDisableProperty", $langs->transnoentitiesnoconv("Conciliable")).'';
diff --git a/htdocs/langs/en_US/admin.lang b/htdocs/langs/en_US/admin.lang
index 187b172e56a..a5ce3889bcc 100644
--- a/htdocs/langs/en_US/admin.lang
+++ b/htdocs/langs/en_US/admin.lang
@@ -1276,6 +1276,7 @@ Delays_MAIN_DELAY_MEMBERS=Delayed membership fee
Delays_MAIN_DELAY_CHEQUES_TO_DEPOSIT=Check deposit not done
Delays_MAIN_DELAY_EXPENSEREPORTS=Expense report to approve
Delays_MAIN_DELAY_HOLIDAYS=Leave requests to approve
+Delays_MAIN_DELAY_MRP=Manufacturing order in progress
SetupDescription1=Before starting to use Dolibarr some initial parameters must be defined and modules enabled/configured.
SetupDescription2=The following two sections are mandatory (the two first entries in the Setup menu):
SetupDescription3= %s -> %sBasic parameters used to customize the default behavior of your application (e.g for country-related features).
diff --git a/htdocs/langs/en_US/mrp.lang b/htdocs/langs/en_US/mrp.lang
index 3b001370270..1cb82161728 100644
--- a/htdocs/langs/en_US/mrp.lang
+++ b/htdocs/langs/en_US/mrp.lang
@@ -147,6 +147,10 @@ NoRemainQtyToDispatch=No quantity remaining to divide
THMOperatorEstimatedHelp=Estimated cost of operator per hour. Will be used to estimate cost of a BOM using this workstation.
THMMachineEstimatedHelp=Estimated cost of machine per hour. Will be used to estimate cost of a BOM using this workstation.
BadValueForquantityToConsume=Quantity to consume for a material cannot be 0 or negative
+MOProgress = In progress
+Mo=Manufacturing
+StatusMrpValidated=Validé
+StatusMrpProgress=En cours
NoValidatedStatusMo = Manufacturing order %s cannot be canceled because it is not validated
CancelMoValidated = Manufacturing order %s canceled
ErrorObjectMustHaveStatusValidatedToBeCanceled = The object %s must have the 'Validated' status to be canceled
diff --git a/htdocs/mrp/class/mo.class.php b/htdocs/mrp/class/mo.class.php
index df29e878765..a4056201417 100644
--- a/htdocs/mrp/class/mo.class.php
+++ b/htdocs/mrp/class/mo.class.php
@@ -1980,6 +1980,75 @@ class Mo extends CommonObject
}
}
+ // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
+ /**
+ * Load indicators for dashboard (this->nbtodo and this->nbtodolate)
+ *
+ * @param User $user Object user
+ * @return WorkboardResponse|int Return integer <0 if KO, WorkboardResponse if OK
+ */
+ public function load_board($user)
+ {
+ // phpcs:enable
+ global $conf, $langs;
+ if ($user->socid) {
+ return -1; // Protection pour éviter appel par utilisateur externe
+ }
+
+ $now = dol_now();
+
+ $sql = "SELECT rowid, date_end_planned FROM ".$this->db->prefix()."mrp_mo";
+ $sql .= " WHERE status IN (" . self::STATUS_VALIDATED . ", " . self::STATUS_INPROGRESS .")"; // 1 = Ouvert, 2 = En cours
+ $sql .= " AND entity IN (".getEntity('mrp_mo').")";
+
+ $resql = $this->db->query($sql);
+ if ($resql) {
+ $langs->load("mrp");
+ $response = new WorkboardResponse();
+ $warning_delay = $conf->mrp->progress->warning_delay ;
+ $response->warning_delay = $warning_delay / 86400;
+ $response->label = $langs->trans("MOProgress");
+ $response->labelShort = $langs->trans("MOProgress");
+ $response->url = DOL_URL_ROOT.'/mrp/mo_list.php?search_status=-2';
+ $response->img = img_object('', "mrp");
+
+
+ while ($obj = $this->db->fetch_object($resql)) {
+ $response->nbtodo++;
+
+ if (!empty($obj->date_end_planned)) {
+ $date_end_planned = $this->db->jdate($obj->date_end_planned);
+ if ($now > ($date_end_planned + $warning_delay)) {
+ $response->nbtodolate++;
+ $response->url_late = DOL_URL_ROOT.'/mrp/mo_list.php?search_status=-2&search_option=late';
+ }
+ }
+ }
+
+ return $response;
+ } else {
+ dol_print_error($this->db);
+ $this->error = $this->db->error();
+ return -1;
+ }
+ }
+
+ /**
+ * Is the manufactured delayed?
+ *
+ * @return bool
+ */
+ public function hasDelay()
+ {
+ global $conf;
+
+ if ($this->status != Mo::STATUS_VALIDATED && $this->status != Mo::STATUS_INPROGRESS) {
+ return false;
+ }
+ return (dol_now() > ($this->date_end_planned + $conf->mrp->progress->warning_delay));
+ }
+
+
/**
* Return clickable link of object (with eventually picto)
*
diff --git a/htdocs/mrp/mo_list.php b/htdocs/mrp/mo_list.php
index 57e47ff3ad9..da8775f5a5f 100644
--- a/htdocs/mrp/mo_list.php
+++ b/htdocs/mrp/mo_list.php
@@ -97,6 +97,8 @@ $extrafields->fetch_name_optionals_label($object->table_element);
$search_array_options = $extrafields->getOptionalsFromPost($object->table_element, '', 'search_');
+$search_option = GETPOST('search_option', 'alphanohtml');
+
// Default sort order (if not yet defined by previous GETPOST)
if (!$sortfield) {
$sortfield = "t.ref"; // Set here default search field. By default 1st field in definition.
@@ -117,7 +119,6 @@ foreach ($object->fields as $key => $val) {
$search[$key.'_dtend'] = dol_mktime(23, 59, 59, GETPOSTINT('search_'.$key.'_dtendmonth'), GETPOSTINT('search_'.$key.'_dtendday'), GETPOSTINT('search_'.$key.'_dtendyear'));
}
}
-
// List of fields to search into when doing a "search in all"
$fieldstosearchall = array();
foreach ($object->fields as $key => $val) {
@@ -343,17 +344,22 @@ foreach ($search as $key => $val) {
if ($key == 'status' && $search[$key] == -1) {
continue;
}
+ if ($key == 'status' && $search[$key] == -2) {
+ $sql .= " AND (t.status IN (".((int) $object::STATUS_VALIDATED).",".((int) $object::STATUS_INPROGRESS)."))";
+ if ($search_option == 'late') {
+ $sql .= " AND (t.date_end_planned < '".$db->idate(dol_now() - $conf->mrp->progress->warning_delay)."')";
+ }
+ continue;
+ }
if ($key == 'fk_parent_line' && $search[$key] != '') {
$sql .= natural_search('moparent.ref', $search[$key], 0);
continue;
}
-
if ($key == 'status') {
$sql .= natural_search('t.status', (string) $search[$key], 0);
continue;
}
-
$mode_search = (($object->isInt($object->fields[$key]) || $object->isFloat($object->fields[$key])) ? 1 : 0);
if ((strpos($object->fields[$key]['type'], 'integer:') === 0) || (strpos($object->fields[$key]['type'], 'sellist:') === 0) || !empty($object->fields[$key]['arrayofkeyval'])) {
if ($search[$key] == '-1' || ($search[$key] === '0' && (empty($object->fields[$key]['arrayofkeyval']) || !array_key_exists('0', $object->fields[$key]['arrayofkeyval'])))) {
@@ -378,9 +384,14 @@ foreach ($search as $key => $val) {
}
}
}
+
+
if ($search_all) {
$sql .= natural_search(array_keys($fieldstosearchall), $search_all);
}
+
+
+
//$sql.= dolSqlDateFilter("t.field", $search_xxxday, $search_xxxmonth, $search_xxxyear);
// Add where from extra fields
include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_sql.tpl.php';
@@ -388,7 +399,6 @@ include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_sql.tpl.php';
$parameters = array();
$reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
$sql .= $hookmanager->resPrint;
-
/* If a group by is required
$sql.= " GROUP BY ";
foreach($object->fields as $key => $val) {
@@ -433,7 +443,6 @@ $sql .= $db->order($sortfield, $sortorder);
if ($limit) {
$sql .= $db->plimit($limit + 1, $offset);
}
-
$resql = $db->query($sql);
if (!$resql) {
dol_print_error($db);
@@ -656,6 +665,9 @@ foreach ($object->fields as $key => $val) {
continue;
}
if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
+ if ($key == 'status') {
+ $val['arrayofkeyval'][-2] = $langs->trans("StatusMrpValidated").'+'.$langs->trans("StatusMrpProgress");
+ }
print $form->selectarray('search_'.$key, $val['arrayofkeyval'], (isset($search[$key]) ? $search[$key] : ''), $val['notnull'], 0, 0, '', 1, 0, 0, '', 'maxwidth100', 1);
} elseif ((strpos($val['type'], 'integer:') === 0) || (strpos($val['type'], 'sellist:') === 0)) {
print $object->showInputField($val, $key, (isset($search[$key]) ? $search[$key] : ''), '', '', 'search_', 'maxwidth125', 1);
@@ -853,6 +865,9 @@ while ($i < $imaxinloop) {
print $object->showOutputField($val, $key, (string) $object->id, '');
} else {
print $object->showOutputField($val, $key, (string) $object->$key, '');
+ if ($key == 'date_end_planned' && $object->hasDelay()) {
+ print img_warning($langs->trans('Alert').' - '.$langs->trans('Late'));
+ }
}
print ' | ';
if (!$i) {
diff --git a/htdocs/theme/eldy/info-box.inc.php b/htdocs/theme/eldy/info-box.inc.php
index 45ac072144f..2c404ec346d 100644
--- a/htdocs/theme/eldy/info-box.inc.php
+++ b/htdocs/theme/eldy/info-box.inc.php
@@ -427,6 +427,9 @@ if (GETPOSTISSET('THEME_SATURATE_RATIO')) {
.bg-infobox-holiday{
color: #755114 !important;
}
+.bg-infobox-cubes{
+ color: #b0a53e !important;
+}
/* Disable colors on left vmenu */
a.vmenu span, span.vmenu, span.vmenu span {
@@ -508,6 +511,9 @@ a.vmenu span, span.vmenu, span.vmenu span {
.fa-dol-holiday:before {
content: "\f5ca";
}
+.fa-dol-cubes:before {
+ content: "\f1b3";
+}
/* USING FONTAWESOME FOR WEATHER */
diff --git a/htdocs/theme/md/info-box.inc.php b/htdocs/theme/md/info-box.inc.php
index c8ea748a5ba..ad284bbacd5 100644
--- a/htdocs/theme/md/info-box.inc.php
+++ b/htdocs/theme/md/info-box.inc.php
@@ -120,6 +120,9 @@ if (GETPOSTISSET('THEME_SATURATE_RATIO')) {
.bg-infobox-holiday{
color: #755114 !important;
}
+.bg-infobox-cubes{
+ color: #b0a53e !important;
+}
/* Disable colors on left vmenu */
a.vmenu span, span.vmenu, span.vmenu span {
@@ -498,6 +501,9 @@ if (GETPOSTISSET('THEME_SATURATE_RATIO')) {
.bg-infobox-holiday i.fa{
color: #755114 !important;
}
+.bg-infobox-cubes i.fa{
+ color: #b0a53e !important;
+}
.fa-dol-action:before {