Merge pull request #16112 from fappels/develop_mo_pmp

Add missing pmp valorisation to MO production. (Issue #16072)
This commit is contained in:
Laurent Destailleur 2021-02-26 16:46:40 +01:00 committed by GitHub
commit fcdd550d7c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 121 additions and 34 deletions

View File

@ -135,6 +135,9 @@ ALTER TABLE llx_menu ADD COLUMN prefix varchar(255) NULL AFTER titre;
ALTER TABLE llx_chargesociales ADD COLUMN fk_user integer DEFAULT NULL;
ALTER TABLE llx_mrp_production ADD COLUMN origin_id integer AFTER fk_mo;
ALTER TABLE llx_mrp_production ADD COLUMN origin_type varchar(10) AFTER origin_id;
ALTER TABLE llx_fichinter ADD COLUMN last_main_doc varchar(255) AFTER model_pdf;
ALTER TABLE llx_projet ADD COLUMN last_main_doc varchar(255) AFTER model_pdf;

View File

@ -17,6 +17,8 @@
CREATE TABLE llx_mrp_production(
rowid integer AUTO_INCREMENT PRIMARY KEY NOT NULL,
fk_mo integer NOT NULL,
origin_id integer,
origin_type varchar(10), -- 'bom' bom production line or 'free' free production line added after Mo creation from bom
position integer NOT NULL DEFAULT 0,
fk_product integer NOT NULL,
fk_warehouse integer,
@ -24,7 +26,7 @@ CREATE TABLE llx_mrp_production(
qty_frozen smallint DEFAULT 0,
disable_stock_change smallint DEFAULT 0,
batch varchar(128),
role varchar(10), -- 'toconsume' or 'toproduce' (initialized at MO creation), 'consumed' or 'produced' (added after MO validation)
role varchar(10), -- 'toconsume' or 'toproduce' (initialized at MO creation), 'consumed' or 'produced' (added after MO validation)
fk_mrp_production integer, -- if role = 'consumed', id of line with role 'toconsume', if role = 'produced' id of line with role 'toproduce'
fk_stock_movement integer, -- id of stock movement when movements are validated
date_creation datetime NOT NULL,

View File

@ -655,6 +655,8 @@ class Mo extends CommonObject
$moline = new MoLine($this->db);
$moline->fk_mo = $this->id;
$moline->origin_id = $line->id;
$moline->origin_type = 'bomline';
if ($line->qty_frozen) {
$moline->qty = $line->qty; // Qty to consume does not depends on quantity to produce
} else {
@ -1386,6 +1388,8 @@ class MoLine extends CommonObjectLine
public $fields = array(
'rowid' =>array('type'=>'integer', 'label'=>'ID', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>10),
'fk_mo' =>array('type'=>'integer', 'label'=>'Mo', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>15),
'origin_id' =>array('type'=>'integer', 'label'=>'Origin', 'enabled'=>1, 'visible'=>-1, 'notnull'=>0, 'position'=>17),
'origin_type' =>array('type'=>'varchar(10)', 'label'=>'Origin type', 'enabled'=>1, 'visible'=>-1, 'notnull'=>0, 'position'=>18),
'position' =>array('type'=>'integer', 'label'=>'Position', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>20),
'fk_product' =>array('type'=>'integer', 'label'=>'Product', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>25),
'fk_warehouse' =>array('type'=>'integer', 'label'=>'Warehouse', 'enabled'=>1, 'visible'=>-1, 'position'=>30),
@ -1405,6 +1409,8 @@ class MoLine extends CommonObjectLine
public $rowid;
public $fk_mo;
public $origin_id;
public $origin_type;
public $position;
public $fk_product;
public $fk_warehouse;
@ -1487,7 +1493,7 @@ class MoLine extends CommonObjectLine
public function fetch($id, $ref = null)
{
$result = $this->fetchCommon($id, $ref);
if ($result > 0 && !empty($this->table_element_line)) $this->fetchLines();
return $result;
}

View File

@ -119,7 +119,7 @@ $arrayfields = array(
'm.type_mouvement'=>array('label'=>$langs->trans("TypeMovement"), 'checked'=>1, 'position'=>48),
'origin'=>array('label'=>$langs->trans("Origin"), 'enabled'=>0, 'checked'=>0, 'position'=>50),
'm.value'=>array('label'=>$langs->trans("Qty"), 'checked'=>1, 'position'=>60),
'm.price'=>array('label'=>$langs->trans("UnitPurchaseValue"), 'enabled'=>0, 'checked'=>0, 'position'=>62),
'm.price'=>array('label'=>$langs->trans("UnitCost"), 'enabled'=>0, 'checked'=>0, 'position'=>62),
//'m.datec'=>array('label'=>$langs->trans("DateCreation"), 'checked'=>0, 'position'=>500),
//'m.tms'=>array('label'=>$langs->trans("DateModificationShort"), 'checked'=>0, 'position'=>500)
);
@ -139,7 +139,11 @@ $permissiontodelete = $user->rights->mrp->delete || ($permissiontoadd && isset($
$upload_dir = $conf->mrp->multidir_output[isset($object->entity) ? $object->entity : 1];
$permissiontoproduce = $permissiontoadd;
$permissiontoupdatecost = $user->rights->bom->write; // User who can define cost must have knowledge of pricing
if ($permissiontoupdatecost) {
$arrayfields['m.price']['enabled'] = 1;
}
/*
* Actions

View File

@ -34,6 +34,7 @@ require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php';
require_once DOL_DOCUMENT_ROOT.'/product/stock/class/productlot.class.php';
require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
dol_include_once('/mrp/class/mo.class.php');
dol_include_once('/bom/class/bom.class.php');
dol_include_once('/mrp/lib/mrp_mo.lib.php');
// Load translation files required by the page
@ -88,6 +89,7 @@ $permissiontodelete = $user->rights->mrp->delete || ($permissiontoadd && isset($
$upload_dir = $conf->mrp->multidir_output[isset($object->entity) ? $object->entity : 1];
$permissiontoproduce = $permissiontoadd;
$permissiontoupdatecost = $user->rights->bom->read; // User who can define cost must have knowledge of pricing
/*
@ -150,6 +152,7 @@ if (empty($reshook))
$moline->qty = GETPOST('qtytoadd', 'int'); ;
$moline->fk_product = GETPOST('productidtoadd', 'int');
$moline->role = 'toconsume';
$moline->origin_type = 'free'; // free consume line
$moline->position = 0;
$resultline = $moline->create($user, false); // Never use triggers here
@ -168,7 +171,7 @@ if (empty($reshook))
$codemovement = GETPOST('inventorycode', 'alphanohtml');
$db->begin();
$pos = 0;
// Process line to consume
foreach ($object->lines as $line) {
if ($line->role == 'toconsume') {
@ -199,7 +202,11 @@ if (empty($reshook))
// Record stock movement
$id_product_batch = 0;
$stockmove->origin = $object;
$idstockmove = $stockmove->livraison($user, $line->fk_product, GETPOST('idwarehouse-'.$line->id.'-'.$i), $qtytoprocess, 0, $labelmovement, dol_now(), '', '', GETPOST('batch-'.$line->id.'-'.$i), $id_product_batch, $codemovement);
if ($qtytoprocess >= 0) {
$idstockmove = $stockmove->livraison($user, $line->fk_product, GETPOST('idwarehouse-'.$line->id.'-'.$i), $qtytoprocess, 0, $labelmovement, dol_now(), '', '', GETPOST('batch-'.$line->id.'-'.$i), $id_product_batch, $codemovement);
} else {
$idstockmove = $stockmove->reception($user, $line->fk_product, GETPOST('idwarehouse-'.$line->id.'-'.$i), $qtytoprocess, 0, $labelmovement, dol_now(), '', '', GETPOST('batch-'.$line->id.'-'.$i), $id_product_batch, $codemovement);
}
if ($idstockmove < 0) {
$error++;
setEventMessages($stockmove->error, $stockmove->errors, 'errors');
@ -207,7 +214,6 @@ if (empty($reshook))
}
if (!$error) {
$pos = 0;
// Record consumption
$moline = new MoLine($db);
$moline->fk_mo = $object->id;
@ -237,6 +243,7 @@ if (empty($reshook))
}
// Process line to produce
$pos = 0;
foreach ($object->lines as $line) {
if ($line->role == 'toproduce') {
$tmpproduct = new Product($db);
@ -245,36 +252,35 @@ if (empty($reshook))
$i = 1;
while (GETPOSTISSET('qtytoproduce-'.$line->id.'-'.$i)) {
$qtytoprocess = price2num(GETPOST('qtytoproduce-'.$line->id.'-'.$i));
$pricetoprocess = GETPOST('pricetoproduce-'.$line->id.'-'.$i) ? price2num(GETPOST('pricetoproduce-'.$line->id.'-'.$i)) : 0;
// Check warehouse is set if we should have to
if (GETPOSTISSET('idwarehousetoproduce-'.$line->id.'-'.$i)) { // If there is a warehouse to set
if (!(GETPOST('idwarehousetoproduce-'.$line->id.'-'.$i) > 0)) { // If there is no warehouse set.
$langs->load("errors");
setEventMessages($langs->trans("ErrorFieldRequiredForProduct", $langs->transnoentitiesnoconv("Warehouse"), $tmpproduct->ref), null, 'errors');
$error++;
}
if (!empty($conf->productbatch->enabled) && $tmpproduct->status_batch && (!GETPOST('batchtoproduce-'.$line->id.'-'.$i))) {
$langs->load("errors");
setEventMessages($langs->trans("ErrorFieldRequiredForProduct", $langs->transnoentitiesnoconv("Batch"), $tmpproduct->ref), null, 'errors');
$error++;
}
}
$idstockmove = 0;
if (!$error && GETPOST('idwarehousetoproduce-'.$line->id.'-'.$i) > 0) {
// Record stock movement
$id_product_batch = 0;
$stockmove->origin = $object;
$idstockmove = $stockmove->reception($user, $line->fk_product, GETPOST('idwarehousetoproduce-'.$line->id.'-'.$i), $qtytoprocess, $pricetoprocess, $labelmovement, '', '', GETPOST('batchtoproduce-'.$line->id.'-'.$i), dol_now(), $id_product_batch, $codemovement);
if ($idstockmove < 0) {
$error++;
setEventMessages($stockmove->error, $stockmove->errors, 'errors');
}
}
if ($qtytoprocess != 0) {
// Check warehouse is set if we should have to
if (GETPOSTISSET('idwarehousetoproduce-'.$line->id.'-'.$i)) { // If there is a warehouse to set
if (!(GETPOST('idwarehousetoproduce-'.$line->id.'-'.$i) > 0)) { // If there is no warehouse set.
$langs->load("errors");
setEventMessages($langs->trans("ErrorFieldRequiredForProduct", $langs->transnoentitiesnoconv("Warehouse"), $tmpproduct->ref), null, 'errors');
$error++;
}
if (!empty($conf->productbatch->enabled) && $tmpproduct->status_batch && (!GETPOST('batchtoproduce-'.$line->id.'-'.$i))) {
$langs->load("errors");
setEventMessages($langs->trans("ErrorFieldRequiredForProduct", $langs->transnoentitiesnoconv("Batch"), $tmpproduct->ref), null, 'errors');
$error++;
}
}
$idstockmove = 0;
if (!$error && GETPOST('idwarehousetoproduce-'.$line->id.'-'.$i) > 0) {
// Record stock movement
$id_product_batch = 0;
$stockmove->origin = $object;
$idstockmove = $stockmove->reception($user, $line->fk_product, GETPOST('idwarehousetoproduce-'.$line->id.'-'.$i), $qtytoprocess, 0, $labelmovement, '', '', GETPOST('batchtoproduce-'.$line->id.'-'.$i), dol_now(), $id_product_batch, $codemovement);
if ($idstockmove < 0) {
$error++;
setEventMessages($stockmove->error, $stockmove->errors, 'errors');
}
}
if (!$error) {
$pos = 0;
// Record production
$moline = new MoLine($db);
$moline->fk_mo = $object->id;
@ -677,12 +683,23 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea
// Show object lines
$object->fetchLines();
$bomcost = 0;
if ($object->fk_bom > 0) {
$bom = new Bom($db);
$res = $bom->fetch($object->fk_bom);
if ($res > 0) {
$bomcost = $bom->unit_cost;
}
}
// consumtion
print '<div class="fichecenter">';
print '<div class="fichehalfleft">';
print '<div class="clearboth"></div>';
$newlinetext = '';
if ($action != 'consumeorproduce' && $action != 'consumeandproduceall') {
if ($object->status != $object::STATUS_PRODUCED && $object->status != $object::STATUS_CANCELED && $action != 'consumeorproduce' && $action != 'consumeandproduceall') {
$newlinetext = '<a href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=addconsumeline">'.$langs->trans("AddNewConsumeLines").'</a>';
}
print load_fiche_titre($langs->trans('Consumption'), '', '', 0, '', '', $newlinetext);
@ -693,6 +710,7 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea
print '<tr class="liste_titre">';
print '<td>'.$langs->trans("Product").'</td>';
print '<td class="right">'.$langs->trans("Qty").'</td>';
if ($permissiontoupdatecost) print '<td class="right">'.$langs->trans("UnitCost").'</td>';
print '<td class="right">'.$langs->trans("QtyAlreadyConsumed").'</td>';
print '<td>';
if ($collapse || in_array($action, array('consumeorproduce', 'consumeandproduceall'))) print $langs->trans("Warehouse");
@ -711,6 +729,7 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea
print $form->select_produits('', 'productidtoadd', '', 0, 0, -1, 2, '', 0, array(), 0, '1', 0, 'maxwidth300');
print '</td>';
print '<td class="right"><input type="text" name="qtytoadd" value="1" class="width50 right"></td>';
if ($permissiontoupdatecost) print '<td></td>';
print '<td class="right"></td>';
print '<td>';
print '<input type="submit" class="button buttongen" name="addconsumelinebutton" value="'.$langs->trans("Add").'">';
@ -739,6 +758,29 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea
$tmpproduct = new Product($db);
$tmpproduct->fetch($line->fk_product);
$linecost = price2num($tmpproduct->pmp, 'MT');
if ($line->origin_type == 'free' && $object->qty > 0) {
// add free consume line cost to bomcost
$costprice = price2num((!empty($tmpproduct->cost_price)) ? $tmpproduct->cost_price : $tmpproduct->pmp);
if (empty($costprice)) {
require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
$productFournisseur = new ProductFournisseur($db);
if ($productFournisseur->find_min_price_product_fournisseur($line->fk_product) > 0){
$costprice = $productFournisseur->fourn_unitprice;
} else {
$costprice = 0;
}
}
$linecost = price2num(($line->qty * $costprice) / $object->qty, 'MT');
$bomcost += $linecost;
} elseif ($line->origin_id > 0 && $line->origin_type == 'bom' && $object->qty > 0) {
foreach ($bom->lines as $bomline) {
if ($bomline->id == $line->origin_id) {
$linecost = price2num(($line->qty * $bomline->unit_cost) / $object->qty, 'MT');
}
}
}
$arrayoflines = $object->fetchLinesLinked('consumed', $line->id);
$alreadyconsumed = 0;
@ -760,6 +802,11 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea
print $line->qty;
}
print '</td>';
if ($permissiontoupdatecost) {
print '<td class="right nowraponall">';
print price($linecost);
print '</td>';
}
print '<td class="right">';
if ($alreadyconsumed) {
print '<script>';
@ -799,6 +846,7 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea
print '</td>';
print '<td></td>';
print '<td class="right">'.$line2['qty'].'</td>';
if ($permissiontoupdatecost) print '<td></td>';
print '<td class="tdoverflowmax150">';
if ($line2['fk_warehouse'] > 0) {
$result = $tmpwarehouse->fetch($line2['fk_warehouse']);
@ -823,6 +871,7 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea
$preselected = (GETPOSTISSET('qty-'.$line->id.'-'.$i) ? GETPOST('qty-'.$line->id.'-'.$i) : max(0, $line->qty - $alreadyconsumed));
if ($action == 'consumeorproduce' && !GETPOSTISSET('qty-'.$line->id.'-'.$i)) $preselected = 0;
print '<td class="right"><input type="text" class="width50 right" name="qty-'.$line->id.'-'.$i.'" value="'.$preselected.'"></td>';
if ($permissiontoupdatecost) print '<td></td>';
print '<td></td>';
print '<td>';
if ($tmpproduct->type == Product::TYPE_PRODUCT || !empty($conf->global->STOCK_SUPPORTS_SERVICES)) {
@ -868,6 +917,13 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea
print '<tr class="liste_titre">';
print '<td>'.$langs->trans("Product").'</td>';
print '<td class="right">'.$langs->trans("Qty").'</td>';
if ($permissiontoupdatecost) {
if (empty($bomcost)) {
print '<td class="right">'.$langs->trans("PMPValue").'</td>';
} else {
print '<td class="right">'.$langs->trans("UnitCost").'</td>';
}
}
print '<td class="right">'.$langs->trans("QtyAlreadyProduced").'</td>';
print '<td>';
if ($collapse || in_array($action, array('consumeorproduce', 'consumeandproduceall'))) print $langs->trans("Warehouse");
@ -899,6 +955,10 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea
$tmpproduct = new Product($db);
$tmpproduct->fetch($line->fk_product);
if (empty($bomcost)) {
$bomcost = $tmpproduct->pmp;
}
$arrayoflines = $object->fetchLinesLinked('produced', $line->id);
$alreadyproduced = 0;
foreach ($arrayoflines as $line2) {
@ -916,6 +976,11 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea
print '<br><span class="opacitymedium small">'.$tmpproduct->label.'</span>';
print '</td>';
print '<td class="right">'.$line->qty.'</td>';
if ($permissiontoupdatecost) {
print '<td class="right nowraponall">';
print price($bomcost);
print '</td>';
}
print '<td class="right nowraponall">';
if ($alreadyproduced) {
print '<script>';
@ -952,6 +1017,7 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea
print '</td>';
print '<td></td>';
print '<td class="right">'.$line2['qty'].'</td>';
if ($permissiontoupdatecost) print '<td></td>';
print '<td class="tdoverflowmax150">';
if ($line2['fk_warehouse'] > 0) {
$result = $tmpwarehouse->fetch($line2['fk_warehouse']);
@ -977,6 +1043,12 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea
$preselected = (GETPOSTISSET('qtytoproduce-'.$line->id.'-'.$i) ? GETPOST('qtytoproduce-'.$line->id.'-'.$i) : max(0, $line->qty - $alreadyproduced));
if ($action == 'consumeorproduce' && !GETPOSTISSET('qtytoproduce-'.$line->id.'-'.$i)) $preselected = 0;
print '<td class="right"><input type="text" class="width50 right" id="qtytoproduce-'.$line->id.'-'.$i.'" name="qtytoproduce-'.$line->id.'-'.$i.'" value="'.$preselected.'"></td>';
if ($permissiontoupdatecost) {
$preselected = (GETPOSTISSET('pricetoproduce-'.$line->id.'-'.$i) ? GETPOST('pricetoproduce-'.$line->id.'-'.$i) : price($bomcost));
print '<td class="right"><input type="text" class="width50 right" name="pricetoproduce-'.$line->id.'-'.$i.'" value="'.$preselected.'"></td>';
} else {
print '<input type="hidden" class="width50 right" name="pricetoproduce-'.$line->id.'-'.$i.'" value="'.$bomcost.'"></td>';
}
print '<td></td>';
print '<td>';
if ($tmpproduct->type == Product::TYPE_PRODUCT || !empty($conf->global->STOCK_SUPPORTS_SERVICES)) {