This commit is contained in:
Laurent Destailleur 2020-01-03 15:01:14 +01:00
parent 68d7195964
commit 15ad41ee6d
5 changed files with 166 additions and 24 deletions

View File

@ -223,6 +223,7 @@ ErrorSearchCriteriaTooSmall=Search criteria too small.
ErrorObjectMustHaveStatusActiveToBeDisabled=Objects must have status 'Active' to be disabled ErrorObjectMustHaveStatusActiveToBeDisabled=Objects must have status 'Active' to be disabled
ErrorObjectMustHaveStatusDraftOrDisabledToBeActivated=Objects must have status 'Draft' or 'Disabled' to be enabled ErrorObjectMustHaveStatusDraftOrDisabledToBeActivated=Objects must have status 'Draft' or 'Disabled' to be enabled
ErrorNoFieldWithAttributeShowoncombobox=No fields has property 'showoncombobox' into definition of object '%s'. No way to show the combolist. ErrorNoFieldWithAttributeShowoncombobox=No fields has property 'showoncombobox' into definition of object '%s'. No way to show the combolist.
ErrorFieldRequiredForProduct=Field '%s' is required for product %s
# Warnings # Warnings
WarningParamUploadMaxFileSizeHigherThanPostMaxSize=Your PHP parameter upload_max_filesize (%s) is higher than PHP parameter post_max_size (%s). This is not a consistent setup. WarningParamUploadMaxFileSizeHigherThanPostMaxSize=Your PHP parameter upload_max_filesize (%s) is higher than PHP parameter post_max_size (%s). This is not a consistent setup.
WarningPasswordSetWithNoAccount=A password was set for this member. However, no user account was created. So this password is stored but can't be used to login to Dolibarr. It may be used by an external module/interface but if you don't need to define any login nor password for a member, you can disable option "Manage a login for each member" from Member module setup. If you need to manage a login but don't need any password, you can keep this field empty to avoid this warning. Note: Email can also be used as a login if the member is linked to a user. WarningPasswordSetWithNoAccount=A password was set for this member. However, no user account was created. So this password is stored but can't be used to login to Dolibarr. It may be used by an external module/interface but if you don't need to define any login nor password for a member, you can disable option "Manage a login for each member" from Member module setup. If you need to manage a login but don't need any password, you can keep this field empty to avoid this warning. Note: Email can also be used as a login if the member is linked to a user.

View File

@ -44,8 +44,8 @@ StatusMOProduced=Produced
QtyFrozen=Frozen Qty QtyFrozen=Frozen Qty
QuantityFrozen=Frozen Quantity QuantityFrozen=Frozen Quantity
QuantityConsumedInvariable=When this flag is set, the quantity consumed is always the value defined and is not relative to the quantity produced. QuantityConsumedInvariable=When this flag is set, the quantity consumed is always the value defined and is not relative to the quantity produced.
DisableStockChange=Disable stock change DisableStockChange=Stock change disabled
DisableStockChangeHelp=When this flag is set, there is no stock change on this product, whatever is the quantity produced DisableStockChangeHelp=When this flag is set, there is no stock change on this product, whatever is the quantity consumed
BomAndBomLines=Bills Of Material and lines BomAndBomLines=Bills Of Material and lines
BOMLine=Line of BOM BOMLine=Line of BOM
WarehouseForProduction=Warehouse for production WarehouseForProduction=Warehouse for production
@ -61,3 +61,5 @@ ForAQuantityOf1=For a quantity to produce of 1
ConfirmValidateMo=Are you sure you want to validate this Manufacturing Order? ConfirmValidateMo=Are you sure you want to validate this Manufacturing Order?
ConfirmProductionDesc=By clicking on '%s', you will validate the consumption and/or production for the quantities set. This will also update the stock and record stock movements. ConfirmProductionDesc=By clicking on '%s', you will validate the consumption and/or production for the quantities set. This will also update the stock and record stock movements.
ProductionForRefAndDate=Production %s - %s ProductionForRefAndDate=Production %s - %s
AutoCloseMO=Close automatically the Manufacturing Order if quantities to consume and to produce are reached
NoStockChangeOnServices=No stock change on services

View File

@ -505,7 +505,7 @@ class Mo extends CommonObject
} }
/** /**
* Erase and update the line to produce * Erase and update the line to produce.
* *
* @param User $user User that modifies * @param User $user User that modifies
* @return int <0 if KO, >0 if OK * @return int <0 if KO, >0 if OK
@ -557,7 +557,11 @@ class Mo extends CommonObject
$moline = new MoLine($this->db); $moline = new MoLine($this->db);
$moline->fk_mo = $this->id; $moline->fk_mo = $this->id;
if ($line->qty_frozen) {
$moline->qty = $line->qty; // Qty to consume does not depends on quantity to produce
} else {
$moline->qty = round($line->qty * $this->qty / $bom->efficiency, 2); $moline->qty = round($line->qty * $this->qty / $bom->efficiency, 2);
}
if ($moline->qty <= 0) { if ($moline->qty <= 0) {
$error++; $error++;
$this->error = "BadValueForquantityToConsume"; $this->error = "BadValueForquantityToConsume";
@ -980,10 +984,16 @@ class Mo extends CommonObject
global $langs; global $langs;
//$langs->load("mrp"); //$langs->load("mrp");
$this->labelStatus[self::STATUS_DRAFT] = $langs->trans('Draft'); $this->labelStatus[self::STATUS_DRAFT] = $langs->trans('Draft');
$this->labelStatus[self::STATUS_VALIDATED] = $langs->trans('Validated'); $this->labelStatus[self::STATUS_VALIDATED] = $langs->trans('Validated').' ('.$langs->trans("ToProduce").')';
$this->labelStatus[self::STATUS_INPROGRESS] = $langs->trans('InProgress'); $this->labelStatus[self::STATUS_INPROGRESS] = $langs->trans('InProgress');
$this->labelStatus[self::STATUS_PRODUCED] = $langs->trans('StatusMOProduced'); $this->labelStatus[self::STATUS_PRODUCED] = $langs->trans('StatusMOProduced');
$this->labelStatus[self::STATUS_CANCELED] = $langs->trans('Canceled'); $this->labelStatus[self::STATUS_CANCELED] = $langs->trans('Canceled');
$this->labelStatusShort[self::STATUS_DRAFT] = $langs->trans('Draft');
$this->labelStatusShort[self::STATUS_VALIDATED] = $langs->trans('Validated');
$this->labelStatusShort[self::STATUS_INPROGRESS] = $langs->trans('InProgress');
$this->labelStatusShort[self::STATUS_PRODUCED] = $langs->trans('StatusMOProduced');
$this->labelStatusShort[self::STATUS_CANCELED] = $langs->trans('Canceled');
} }
$statusType = 'status'.$status; $statusType = 'status'.$status;
@ -992,7 +1002,7 @@ class Mo extends CommonObject
if ($status == self::STATUS_PRODUCED) $statusType = 'status5'; if ($status == self::STATUS_PRODUCED) $statusType = 'status5';
if ($status == self::STATUS_CANCELED) $statusType = 'status6'; if ($status == self::STATUS_CANCELED) $statusType = 'status6';
return dolGetStatus($this->labelStatus[$status], $this->labelStatus[$status], '', $statusType, $mode); return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusType, $mode);
} }
/** /**
@ -1448,6 +1458,24 @@ class MoLine extends CommonObjectLine
} }
} }
/**
* Get list of lines linked to current line for a defined role
*
* @param string $role Get lines linked to current line with the selected role ('consumed', 'produced', ...)
* @return array Array of lines
*/
public function fetchLinesLinked($role) {
$array = array();
$sql = 'SELECT rowid, qty ';
$sql .= $this->getFieldList();
$sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t';
if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) $sql .= ' WHERE t.entity IN ('.getEntity($this->table_element).')';
else $sql .= ' WHERE 1 = 1';
return $array;
}
/** /**
* Update object into database * Update object into database
* *

View File

@ -117,8 +117,6 @@ $upload_dir = $conf->mrp->multidir_output[isset($object->entity) ? $object->enti
/* /*
* Actions * Actions
*
* Put here all code to do according to value of "action" parameter
*/ */
$parameters = array(); $parameters = array();

View File

@ -47,6 +47,7 @@ require '../main.inc.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formprojet.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/html.formprojet.class.php';
require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php'; require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php';
dol_include_once('/mrp/class/mo.class.php'); dol_include_once('/mrp/class/mo.class.php');
dol_include_once('/mrp/lib/mrp_mo.lib.php'); dol_include_once('/mrp/lib/mrp_mo.lib.php');
@ -149,6 +150,89 @@ if (empty($reshook))
{ {
$object->setProject(GETPOST('projectid', 'int')); $object->setProject(GETPOST('projectid', 'int'));
} }
if ($action == 'confirm_consumeandproduceall') {
$db->begin();
// Process line to consume
foreach($object->lines as $line) {
if ($line->role == 'toconsume') {
$tmpproduct = new Product($db);
$tmpproduct->fetch($line->fk_product);
$i=1;
while (GETPOSTISSET('qty-'.$line->id.'-'.$i)) {
// Check warehouse is set if we should have to
if (GETPOST('idwarehouse-'.$line->id.'-'.$i)) {
if (! (GETPOST('idwarehouse-'.$line->id.'-'.$i) > 0)) {
$langs->load("errors");
setEventMessages($langs->trans("ErrorFieldRequiredForProduct", $langs->transnoentitiesnoconv("Warehouse"), $tmpproduct->ref), null, 'errors');
$error++;
}
if ($tmpproduct->status_batch && (! GETPOST('batch-'.$line->id.'-'.$i))) {
$langs->load("errors");
setEventMessages($langs->trans("ErrorFieldRequiredForProduct", $langs->transnoentitiesnoconv("Batch"), $tmpproduct->ref), null, 'errors');
$error++;
}
}
if (! $error) {
}
$i++;
}
}
}
// Process line to produce
foreach($object->lines as $line) {
if ($line->role == 'toproduce') {
$tmpproduct = new Product($db);
$tmpproduct->fetch($line->fk_product);
$i=1;
while (GETPOSTISSET('qtytoproduce-'.$line->id.'-'.$i)) {
// Check warehouse is set if we should have to
if (GETPOST('idwarehousetoproduce-'.$line->id.'-'.$i)) {
if (! (GETPOST('idwarehousetoproduce-'.$line->id.'-'.$i) > 0)) {
$langs->load("errors");
setEventMessages($langs->trans("ErrorFieldRequiredForProduct", $langs->transnoentitiesnoconv("Warehouse"), $tmpproduct->ref), null, 'errors');
$error++;
}
if ($tmpproduct->status_batch && (! GETPOST('batchtoproduce-'.$line->id.'-'.$i))) {
$langs->load("errors");
setEventMessages($langs->trans("ErrorFieldRequiredForProduct", $langs->transnoentitiesnoconv("Batch"), $tmpproduct->ref), null, 'errors');
$error++;
}
}
if (! $error) {
}
$i++;
}
}
}
if (! $error) {
// Update status of MO
}
if ($error) {
$action = str_replace('confirm_', '', $action);
$db->rollback();
} else {
$db->commit();
}
}
} }
@ -357,7 +441,7 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea
{ {
print '<form method="POST" action="'.$_SERVER["PHP_SELF"].'">'; print '<form method="POST" action="'.$_SERVER["PHP_SELF"].'">';
print '<input type="hidden" name="token" value="'.newToken().'">'; print '<input type="hidden" name="token" value="'.newToken().'">';
print '<input type="hidden" name="action" value="add">'; print '<input type="hidden" name="action" value="confirm_'.$action.'">';
print '<input type="hidden" name="backtopage" value="'.$backtopage.'">'; print '<input type="hidden" name="backtopage" value="'.$backtopage.'">';
print '<input type="hidden" name="id" value="'.$id.'">'; print '<input type="hidden" name="id" value="'.$id.'">';
@ -375,9 +459,10 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea
$defaultstockmovementcode = $object->ref.'_'.dol_print_date(dol_now(), 'dayhourlog'); $defaultstockmovementcode = $object->ref.'_'.dol_print_date(dol_now(), 'dayhourlog');
print '<div class="center">'; print '<div class="center">';
print '<span class="opacitymedium hideonsmartphone">'.$langs->trans("ConfirmProductionDesc", $langs->transnoentitiesnoconv("Confirm")).'<br><br></span>'; print '<span class="opacitymedium hideonsmartphone">'.$langs->trans("ConfirmProductionDesc", $langs->transnoentitiesnoconv("Confirm")).'<br></span>';
print $langs->trans("MovementLabel").': <input type="text" class="minwidth300" name="label" value="'.$defaultstockmovementlabel.'"> &nbsp; '; print $langs->trans("MovementLabel").': <input type="text" class="minwidth300" name="label" value="'.$defaultstockmovementlabel.'"> &nbsp; ';
print $langs->trans("InventoryCode").': <input type="text" class="maxwidth150" name="inventorycode" value="'.$defaultstockmovementcode.'"> &nbsp; '; print $langs->trans("InventoryCode").': <input type="text" class="minwidth200" name="inventorycode" value="'.$defaultstockmovementcode.'"><br><br>';
print '<input type="checkbox" name="autoclose" value="1" checked="checked"> '.$langs->trans("AutoCloseMO").'<br>';
print '<input class="button" type="submit" value="'.$langs->trans("Confirm").'" name="confirm">'; print '<input class="button" type="submit" value="'.$langs->trans("Confirm").'" name="confirm">';
print ' &nbsp; '; print ' &nbsp; ';
print '<input class="button" type="submit" value="'.$langs->trans("Cancel").'" name="cancel">'; print '<input class="button" type="submit" value="'.$langs->trans("Cancel").'" name="cancel">';
@ -425,10 +510,24 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea
$tmpproduct = new Product($db); $tmpproduct = new Product($db);
$tmpproduct->fetch($line->fk_product); $tmpproduct->fetch($line->fk_product);
$arrayoflines = $line->fetchLinesLinked('consumed');
$alreadyconsumed = 0;
foreach($arrayoflines as $line2) {
$alreadyconsumed += $line2->qty;
}
print '<tr>'; print '<tr>';
print '<td>'.$tmpproduct->getNomUrl(1).'</td>'; print '<td>'.$tmpproduct->getNomUrl(1).'</td>';
print '<td>'.$line->qty.'</td>'; print '<td>';
$alreadyconsumed = 0; $help = '';
if ($line->qty_frozen) $help.=($help ? '<br>' : '').'<strong>'.$langs->trans("QuantityFrozen").'</strong>: '.yn(1).' ('.$langs->trans("QuantityConsumedInvariable").')';
if ($line->disable_stock_change) $help.=($help ? '<br>' : '').'<strong>'.$langs->trans("DisableStockChange").'</strong>: '.yn(1).' ('.(($tmpproduct->type == Product::TYPE_SERVICE && empty($conf->global->STOCK_SUPPORTS_SERVICES)) ? $langs->trans("NoStockChangeOnServices") : $langs->trans("DisableStockChangeHelp")).')';
if ($help) {
print $form->textwithpicto($line->qty, $help);
} else {
print $line->qty;
}
print '</td>';
print '<td>'.$alreadyconsumed.'</td>'; print '<td>'.$alreadyconsumed.'</td>';
print '<td></td>'; // Warehouse print '<td></td>'; // Warehouse
if ($conf->productbatch->enabled) { if ($conf->productbatch->enabled) {
@ -440,19 +539,28 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea
//$arrayoflines = $line->fetchLinesLinked('consumed'); //$arrayoflines = $line->fetchLinesLinked('consumed');
if ($action == 'consumeandproduceall') { if ($action == 'consumeandproduceall') {
$i = 1;
print '<tr>'; print '<tr>';
print '<td>'.$langs->trans("ToConsume").'</td>'; print '<td>'.$langs->trans("ToConsume").'</td>';
print '<td><input type="text" class="width50" name="qty-'.$line->id.'" value="'.max(0, $line->qty - $alreadyconsumed).'"></td>'; print '<td><input type="text" class="width50" name="qty-'.$line->id.'-'.$i.'" value="'.(GETPOSTISSET('qty-'.$line->id.'-'.$i) ? GETPOST('qty-'.$line->id.'-'.$i) : max(0, $line->qty - $alreadyconsumed)).'"></td>';
print '<td></td>'; print '<td></td>';
print '<td>'; print '<td>';
if ($tmpproduct->type == Product::TYPE_PRODUCT || !empty($conf->global->STOCK_SUPPORTS_SERVICES)) { if ($tmpproduct->type == Product::TYPE_PRODUCT || !empty($conf->global->STOCK_SUPPORTS_SERVICES)) {
print $formproduct->selectWarehouses('ifone', 'idwarehouse-'.$line->id, '', 1, 0, $line->fk_product, '', 1); if (empty($line->disable_stock_change)) {
$preselected = (GETPOSTISSET('idwarehouse-'.$line->id.'-'.$i) ? GETPOST('idwarehouse-'.$line->id.'-'.$i) : 'ifone');
print $formproduct->selectWarehouses($preselected, 'idwarehouse-'.$line->id.'-'.$i, '', 1, 0, $line->fk_product, '', 1);
} else {
print '<span class="opacitymedium">'.$langs->trans("DisableStockChange").'</span>';
}
} else {
print '<span class="opacitymedium">'.$langs->trans("NoStockChangeOnServices").'</span>';
} }
print '</td>'; print '</td>';
if ($conf->productbatch->enabled) { if ($conf->productbatch->enabled) {
print '<td>'; print '<td>';
if ($tmpproduct->status_batch) { if ($tmpproduct->status_batch) {
print '<input type="text" class="width50" name="batch-'.$line->id.'" value="">'; $preselected = (GETPOSTISSET('batch-'.$line->id.'-'.$i) ? GETPOST('batch-'.$line->id.'-'.$i) : '');
print '<input type="text" class="width50" name="batch-'.$line->id.'-'.$i.'" value="'.$preselected.'">';
} }
print '</td>'; print '</td>';
} }
@ -508,12 +616,18 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea
{ {
foreach($object->lines as $line) { foreach($object->lines as $line) {
if ($line->role == 'toproduce') { if ($line->role == 'toproduce') {
print '<tr>';
$tmpproduct = new Product($db); $tmpproduct = new Product($db);
$tmpproduct->fetch($line->fk_product); $tmpproduct->fetch($line->fk_product);
$arrayoflines = $line->fetchLinesLinked('produced');
$alreadyproduced = 0;
foreach($arrayoflines as $line2) {
$alreadyproduced += $line2->qty;
}
print '<tr>';
print '<td>'.$tmpproduct->getNomUrl(1).'</td>'; print '<td>'.$tmpproduct->getNomUrl(1).'</td>';
print '<td>'.$line->qty.'</td>'; print '<td>'.$line->qty.'</td>';
$alreadyproduced = 0;
print '<td>'.$alreadyproduced.'</td>'; print '<td>'.$alreadyproduced.'</td>';
print '<td></td>'; // Warehouse print '<td></td>'; // Warehouse
if ($conf->productbatch->enabled) { if ($conf->productbatch->enabled) {
@ -521,23 +635,22 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea
} }
print '</tr>'; print '</tr>';
// Show detailed of already produced
//$arrayoflines = $line->fetchLinesLinked('produced');
if ($action == 'consumeandproduceall') { if ($action == 'consumeandproduceall') {
print '<tr>'; print '<tr>';
print '<td>'.$langs->trans("ToProduce").'</td>'; print '<td>'.$langs->trans("ToProduce").'</td>';
print '<td><input type="text" class="width50" name="qty-'.$line->id.'" value="'.max(0, $line->qty - $alreadyproduced).'"></td>'; print '<td><input type="text" class="width50" name="qtytoproduce-'.$line->id.'-'.$i.'" value="'.(GETPOSTISSET('qtytoproduce-'.$line->id.'-'.$i) ? GETPOST('qtytoproduce-'.$line->id.'-'.$i) : max(0, $line->qty - $alreadyproduced)).'"></td>';
print '<td></td>'; print '<td></td>';
print '<td>'; print '<td>';
if ($tmpproduct->type == Product::TYPE_PRODUCT || !empty($conf->global->STOCK_SUPPORTS_SERVICES)) { if ($tmpproduct->type == Product::TYPE_PRODUCT || !empty($conf->global->STOCK_SUPPORTS_SERVICES)) {
print $formproduct->selectWarehouses('ifone', 'idwarehouse-'.$line->id, '', 1, 0, $line->fk_product, '', 1); $preselected = (GETPOSTISSET('idwarehousetoproduce-'.$line->id.'-'.$i) ? GETPOST('idwarehousetoproduce-'.$line->id.'-'.$i) : ($object->fk_warehouse > 0 ? $object->fk_warehouse : 'ifone'));
print $formproduct->selectWarehouses($preselected, 'idwarehousetoproduce-'.$line->id.'-'.$i, '', 1, 0, $line->fk_product, '', 1);
} }
print '</td>'; print '</td>';
if ($conf->productbatch->enabled) { if ($conf->productbatch->enabled) {
print '<td>'; print '<td>';
if ($tmpproduct->status_batch) { if ($tmpproduct->status_batch) {
print '<input type="text" class="width50" name="batch-'.$line->id.'" value="">'; $preselected = (GETPOSTISSET('batchtoproduce-'.$line->id.'-'.$i) ? GETPOST('batchtoproduce-'.$line->id.'-'.$i) : '');
print '<input type="text" class="width50" name="batchtoproduce-'.$line->id.'-'.$i.'" value="'.$preselected.'">';
} }
print '</td>'; print '</td>';
} }