FIX: Allow editing quantity to produce on MO while in draft status (#38176)
Add an inline edit pencil next to 'QtyToProduce' on the MO card and the MO production view when the order is still in DRAFT status. On save: 1. The MO qty is updated through Mo::update(), which internally calls Mo::updateProduction() to scale 'toconsume'/'toproduce' lines proportionally (newQty/oldQty), respecting qty_frozen flags. 2. The 'toproduce' line for the MO's main product is then explicitly realigned to match the MO qty exactly. This guards against legacy inconsistent states and floating-point drift from successive ratio scaling, while leaving BOM sub-product ratios and frozen lines untouched. The handler is placed BEFORE the include of actions_addupdatedelete.inc.php, because that file has a generic 'set<key>' matcher that would otherwise intercept 'setqty', call $object->fetch() + update() without setting $object->oldQty, and silently skip the line scaling. The edit is gated both client-side (pencil only shown when DRAFT) and server-side (setqty handler checks STATUS_DRAFT) to prevent any modification once the MO has been validated. Note: the field type 'real' is temporarily overridden to 'numeric' only while the edit pencil is active (action=editqty), because Form::editfieldval() in html.form.class.php does not handle 'real' and would render the Save/Cancel buttons with no <input> field in between. Read-mode rendering (via price()) is preserved.
This commit is contained in:
parent
0d068d3cb6
commit
139ecd4a7c
2 changed files with 104 additions and 0 deletions
|
|
@ -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<key>' 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 <input> 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
|
||||
|
|
|
|||
|
|
@ -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<key>' 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 <input> 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
|
||||
|
|
|
|||
Loading…
Reference in a new issue