diff --git a/htdocs/mrp/mo_card.php b/htdocs/mrp/mo_card.php index ecc30ab68a5..75164a24bff 100644 --- a/htdocs/mrp/mo_card.php +++ b/htdocs/mrp/mo_card.php @@ -268,6 +268,46 @@ if (empty($reshook)) { } + // Allow editing qty while MO is still in draft status. + // IMPORTANT: this handler MUST run BEFORE actions_addupdatedelete.inc.php, + // which has a generic 'set' matcher that would intercept 'setqty' and + // call $object->fetch() + update() without setting $object->oldQty, so + // Mo::updateProduction() would skip the line scaling (its condition is + // !empty($this->oldQty)). + if ($action == 'setqty' && $permissiontoadd && $object->status == Mo::STATUS_DRAFT) { + $newqty = GETPOSTFLOAT('qty'); + if ($newqty > 0) { + $object->oldQty = (float) $object->qty; + $object->qty = $newqty; + $res = $object->update($user); + if ($res > 0) { + // Enforce invariant: the 'toproduce' line for the MO's main product + // must equal the MO qty. Mo::updateProduction() scales by ratio + // (newQty/oldQty) which can drift if the line state was already + // inconsistent (e.g. legacy data from before this patch). Realign + // the main product line, leaving sub-products and frozen lines + // untouched. + $object->fetchLines(); + foreach ($object->lines as $line) { + if ($line->role === 'toproduce' + && (int) $line->fk_product === (int) $object->fk_product + && empty($line->qty_frozen) + && (float) $line->qty != (float) $object->qty) { + $line->qty = (float) $object->qty; + $line->update($user); + } + } + setEventMessages($langs->trans("RecordSaved"), null, 'mesgs'); + } else { + setEventMessages($object->error, $object->errors, 'errors'); + } + } else { + setEventMessages($langs->trans("ErrorFieldRequired", $langs->trans("Qty")), null, 'errors'); + } + header("Location: ".$_SERVER["PHP_SELF"]."?id=".$object->id); + exit; + } + // Actions cancel, add, update, update_extras, confirm_validate, confirm_delete, confirm_deleteline, confirm_clone, confirm_close, confirm_setdraft, confirm_reopen include DOL_DOCUMENT_ROOT.'/core/actions_addupdatedelete.inc.php'; @@ -684,6 +724,18 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea $keyforbreak = 'fk_warehouse'; unset($object->fields['fk_project']); unset($object->fields['fk_soc']); + // Allow inline edit of qty while MO is in draft status. + // Note: editfieldval() does not handle type 'real' (no rendered, only + // Save/Cancel buttons appear). So we override the type to 'numeric' only when + // the user has actually clicked the edit pencil (action=editqty). This keeps + // the read-mode rendering (price() format) untouched. + if ($object->status == Mo::STATUS_DRAFT && $permissiontoadd && isset($object->fields['qty'])) { + $object->fields['qty']['alwayseditable'] = 1; + if ($action == 'editqty') { + $object->fields['qty']['type'] = 'numeric'; + } + } + include DOL_DOCUMENT_ROOT.'/core/tpl/commonfields_view.tpl.php'; // Other attributes diff --git a/htdocs/mrp/mo_production.php b/htdocs/mrp/mo_production.php index 5837880f1e8..a31ab3a6d3e 100644 --- a/htdocs/mrp/mo_production.php +++ b/htdocs/mrp/mo_production.php @@ -163,6 +163,46 @@ if (empty($reshook)) { } } + // Allow editing qty while MO is still in draft status. + // IMPORTANT: this handler MUST run BEFORE actions_addupdatedelete.inc.php, + // which has a generic 'set' matcher that would intercept 'setqty' and + // call $object->fetch() + update() without setting $object->oldQty, so + // Mo::updateProduction() would skip the line scaling (its condition is + // !empty($this->oldQty)). + if ($action == 'setqty' && $permissiontoadd && $object->status == Mo::STATUS_DRAFT) { + $newqty = GETPOSTFLOAT('qty'); + if ($newqty > 0) { + $object->oldQty = (float) $object->qty; + $object->qty = $newqty; + $res = $object->update($user); + if ($res > 0) { + // Enforce invariant: the 'toproduce' line for the MO's main product + // must equal the MO qty. Mo::updateProduction() scales by ratio + // (newQty/oldQty) which can drift if the line state was already + // inconsistent (e.g. legacy data from before this patch). Realign + // the main product line, leaving sub-products and frozen lines + // untouched. + $object->fetchLines(); + foreach ($object->lines as $line) { + if ($line->role === 'toproduce' + && (int) $line->fk_product === (int) $object->fk_product + && empty($line->qty_frozen) + && (float) $line->qty != (float) $object->qty) { + $line->qty = (float) $object->qty; + $line->update($user); + } + } + setEventMessages($langs->trans("RecordSaved"), null, 'mesgs'); + } else { + setEventMessages($object->error, $object->errors, 'errors'); + } + } else { + setEventMessages($langs->trans("ErrorFieldRequired", $langs->trans("Qty")), null, 'errors'); + } + header("Location: ".$_SERVER["PHP_SELF"]."?id=".$object->id); + exit; + } + // Actions cancel, add, update, delete or clone include DOL_DOCUMENT_ROOT.'/core/actions_addupdatedelete.inc.php'; @@ -714,6 +754,18 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea $keyforbreak = 'fk_warehouse'; unset($object->fields['fk_project']); unset($object->fields['fk_soc']); + // Allow inline edit of qty while MO is in draft status. + // Note: editfieldval() does not handle type 'real' (no rendered, only + // Save/Cancel buttons appear). So we override the type to 'numeric' only when + // the user has actually clicked the edit pencil (action=editqty). This keeps + // the read-mode rendering (price() format) untouched. + if ($object->status == Mo::STATUS_DRAFT && $permissiontoadd && isset($object->fields['qty'])) { + $object->fields['qty']['alwayseditable'] = 1; + if ($action == 'editqty') { + $object->fields['qty']['type'] = 'numeric'; + } + } + include DOL_DOCUMENT_ROOT.'/core/tpl/commonfields_view.tpl.php'; // Other attributes