Merge branch 'atm-alexis-feature_Product_stock_subproduct_stock_independent' into develop
This commit is contained in:
commit
d688db3b78
@ -87,6 +87,9 @@ if($action)
|
||||
if($action == 'STOCK_MUST_BE_ENOUGH_FOR_SHIPMENT') {
|
||||
$res = dolibarr_set_const($db, "STOCK_MUST_BE_ENOUGH_FOR_SHIPMENT", GETPOST('STOCK_MUST_BE_ENOUGH_FOR_SHIPMENT','alpha'),'chaine',0,'',$conf->entity);
|
||||
}
|
||||
if($action == 'INDEPENDANT_SUBPRODUCT_STOCK') {
|
||||
$res = dolibarr_set_const($db, "INDEPENDANT_SUBPRODUCT_STOCK", GETPOST('INDEPENDANT_SUBPRODUCT_STOCK','alpha'),'chaine',0,'',$conf->entity);
|
||||
}
|
||||
|
||||
if (! $res > 0) $error++;
|
||||
|
||||
@ -339,9 +342,29 @@ print '</form>';
|
||||
print "</td>\n";
|
||||
print "</tr>\n";
|
||||
print '<br>';
|
||||
print '</table>';
|
||||
print '<br>';
|
||||
|
||||
/* I keep the option/feature, but hidden to end users for the moment. If feature is used by module, no need to have users see it.
|
||||
If not used by a module, I still need to understand in which case user may need this now we can set rule on product page.
|
||||
if ($conf->global->PRODUIT_SOUSPRODUITS)
|
||||
{
|
||||
$var=!$var;
|
||||
|
||||
print "<tr ".$bc[$var].">";
|
||||
print '<td width="60%">'.$langs->trans("IndependantSubProductStock").'</td>';
|
||||
|
||||
print '<td width="160" align="right">';
|
||||
print "<form method=\"post\" action=\"stock.php\">";
|
||||
print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
|
||||
print "<input type=\"hidden\" name=\"action\" value=\"INDEPENDANT_SUBPRODUCT_STOCK\">";
|
||||
print $form->selectyesno("INDEPENDANT_SUBPRODUCT_STOCK",$conf->global->INDEPENDANT_SUBPRODUCT_STOCK,1);
|
||||
print '<input type="submit" class="button" value="'.$langs->trans("Modify").'">';
|
||||
print '</form>';
|
||||
print "</td>\n";
|
||||
print "</tr>\n";
|
||||
}
|
||||
*/
|
||||
|
||||
print '</table>';
|
||||
|
||||
llxFooter();
|
||||
|
||||
|
||||
@ -250,4 +250,7 @@ PriceExpressionEditorHelp3=In both product/service and supplier prices there are
|
||||
PriceExpressionEditorHelp4=In product/service price only: <b>#supplier_min_price#</b><br>In supplier prices only: <b>#supplier_quantity# and #supplier_tva_tx#</b>
|
||||
PriceMode=Price mode
|
||||
PriceNumeric=Number
|
||||
DefaultPrice=Default price
|
||||
DefaultPrice=Default price
|
||||
ComposedProductDecreaseStock=Decrease Stock for sub-product
|
||||
ComposedProduct=Sub-product
|
||||
MinSupplierPrice=Minimun supplier price
|
||||
|
||||
@ -47,6 +47,7 @@ PMPValue=Weighted average price
|
||||
PMPValueShort=WAP
|
||||
EnhancedValueOfWarehouses=Warehouses value
|
||||
UserWarehouseAutoCreate=Create a warehouse automatically when creating a user
|
||||
IndependantSubProductStock=Product stock and subproduct stock are independant
|
||||
QtyDispatched=Quantity dispatched
|
||||
QtyDispatchedShort=Qty dispatched
|
||||
QtyToDispatchShort=Qty to dispatch
|
||||
|
||||
@ -2306,6 +2306,40 @@ class Product extends CommonObject
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify composed product
|
||||
*
|
||||
* @param int $id_pere Id of master product
|
||||
* @param int $id_fils Id of linked product
|
||||
* @param int $qty Quantity
|
||||
* @param int $incdec increase/descrease stock or not
|
||||
* * @return int < 0 if KO, > 0 if OK
|
||||
*/
|
||||
function update_sousproduit($id_pere, $id_fils,$qty, $incdec=1)
|
||||
{
|
||||
// Clean parameters
|
||||
if (! is_numeric($id_pere)) $id_pere=0;
|
||||
if (! is_numeric($id_fils)) $id_fils=0;
|
||||
if (! is_numeric($incdec)) $incdec=1;
|
||||
if (! is_numeric($qty)) $qty=1;
|
||||
|
||||
$sql = 'UPDATE '.MAIN_DB_PREFIX.'product_association SET ';
|
||||
$sql.= 'qty='.$qty;
|
||||
$sql.= ',incdec='.$incdec;
|
||||
$sql .= ' WHERE fk_product_pere='.$id_pere.' AND fk_product_fils='.$id_fils;
|
||||
|
||||
if (!$this->db->query($sql))
|
||||
{
|
||||
dol_print_error($this->db);
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Retire le lien entre un sousproduit et un produit/service
|
||||
*
|
||||
@ -2633,6 +2667,8 @@ class Product extends CommonObject
|
||||
$nb=(! empty($desc_pere[1]) ? $desc_pere[1] :'');
|
||||
$type=(! empty($desc_pere[2]) ? $desc_pere[2] :'');
|
||||
$label=(! empty($desc_pere[3]) ? $desc_pere[3] :'');
|
||||
$incdec=!empty($desc_pere[4]) ? $desc_pere[4] : 0;
|
||||
|
||||
if ($multiply < 1) $multiply=1;
|
||||
|
||||
//print "XXX We add id=".$id." - label=".$label." - nb=".$nb." - multiply=".$multiply." fullpath=".$compl_path.$label."\n";
|
||||
@ -2649,7 +2685,8 @@ class Product extends CommonObject
|
||||
'fullpath'=>$compl_path.$label, // Label
|
||||
'type'=>$type, // Nb of units that compose parent product
|
||||
'desiredstock'=>$this->desiredstock,
|
||||
'level'=>$level
|
||||
'level'=>$level,
|
||||
'incdec'=>$incdec
|
||||
);
|
||||
|
||||
// Recursive call if there is childs to child
|
||||
@ -2798,7 +2835,7 @@ class Product extends CommonObject
|
||||
*/
|
||||
function getChildsArbo($id)
|
||||
{
|
||||
$sql = "SELECT p.rowid, p.label as label, pa.qty as qty, pa.fk_product_fils as id, p.fk_product_type";
|
||||
$sql = "SELECT p.rowid, p.label as label, pa.qty as qty, pa.fk_product_fils as id, p.fk_product_type, pa.incdec";
|
||||
$sql.= " FROM ".MAIN_DB_PREFIX."product as p";
|
||||
$sql.= ", ".MAIN_DB_PREFIX."product_association as pa";
|
||||
$sql.= " WHERE p.rowid = pa.fk_product_fils";
|
||||
@ -2812,7 +2849,13 @@ class Product extends CommonObject
|
||||
$prods = array();
|
||||
while ($rec = $this->db->fetch_array($res))
|
||||
{
|
||||
$prods[$rec['rowid']]= array(0=>$rec['id'],1=>$rec['qty'],2=>$rec['fk_product_type'],3=>$this->db->escape($rec['label']));
|
||||
$prods[$rec['rowid']]= array(
|
||||
0=>$rec['id'],
|
||||
1=>$rec['qty'],
|
||||
2=>$rec['fk_product_type'],
|
||||
3=>$this->db->escape($rec['label']),
|
||||
4=>$rec['incdec']
|
||||
);
|
||||
//$prods[$this->db->escape($rec['label'])]= array(0=>$rec['id'],1=>$rec['qty'],2=>$rec['fk_product_type']);
|
||||
//$prods[$this->db->escape($rec['label'])]= array(0=>$rec['id'],1=>$rec['qty']);
|
||||
$listofchilds=$this->getChildsArbo($rec['id']);
|
||||
|
||||
@ -109,6 +109,19 @@ $cancel <> $langs->trans("Cancel") &&
|
||||
exit;
|
||||
}
|
||||
}
|
||||
else if($action==='save_composed_product') {
|
||||
|
||||
$TProduct = GETPOST('TProduct', 'array');
|
||||
if(!empty($TProduct)) {
|
||||
|
||||
foreach ($TProduct as $id_product => $row) {
|
||||
$product->update_sousproduit($id, $id_product,$row['qty'], isset($row['incdec']) ? 1 : 0 );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
if ($cancel == $langs->trans("Cancel"))
|
||||
{
|
||||
@ -175,97 +188,6 @@ dol_fiche_head($head, 'subproduct', $titre, 0, $picto);
|
||||
|
||||
if ($id > 0 || ! empty($ref))
|
||||
{
|
||||
/* if ($result)
|
||||
{
|
||||
if ($action <> 'edit' && $action <> 'search' && $action <> 're-edit')
|
||||
{
|
||||
// mode visu
|
||||
|
||||
print '<table class="border" width="100%">';
|
||||
|
||||
print "<tr>";
|
||||
|
||||
$nblignes=6;
|
||||
if ($product->isproduct() && ! empty($conf->stock->enabled)) $nblignes++;
|
||||
if ($product->isservice()) $nblignes++;
|
||||
|
||||
// Reference
|
||||
print '<td width="25%">'.$langs->trans("Ref").'</td><td>';
|
||||
print $form->showrefnav($product,'ref','',1,'ref');
|
||||
print '</td></tr>';
|
||||
|
||||
// Libelle
|
||||
print '<tr><td>'.$langs->trans("Label").'</td><td>'.$product->libelle.'</td>';
|
||||
print '</tr>';
|
||||
|
||||
// Number of subproducts
|
||||
$prodsfather = $product->getFather(); // Parent Products
|
||||
$product->get_sousproduits_arbo();
|
||||
$prods_arbo=$product->get_arbo_each_prod();
|
||||
$nbofsubproducts=count($prods_arbo);
|
||||
print '<tr><td>'.$langs->trans("AssociatedProductsNumber").'</td><td>';
|
||||
print $form->textwithpicto($nbofsubproducts, $langs->trans('IfZeroItIsNotAVirtualProduct'));
|
||||
print '</td>';
|
||||
|
||||
dol_fiche_end();
|
||||
|
||||
|
||||
// List of products into this virtual product
|
||||
if (count($prods_arbo) > 0)
|
||||
{
|
||||
print '<tr><td colspan="2">';
|
||||
print '<b>'.$langs->trans("ProductAssociationList").'</b><br>';
|
||||
print '<table class="nobordernopadding">';
|
||||
foreach($prods_arbo as $value)
|
||||
{
|
||||
$productstatic->id=$value['id'];
|
||||
$productstatic->type=$value['type'];
|
||||
$productstatic->ref=$value['fullpath'];
|
||||
if (! empty($conf->stock->enabled)) $productstatic->load_stock();
|
||||
//var_dump($value);
|
||||
//print '<pre>'.$productstatic->ref.'</pre>';
|
||||
//print $productstatic->getNomUrl(1).'<br>';
|
||||
//print $value[0]; // This contains a tr line.
|
||||
print '<tr>';
|
||||
//print '<td>'.$productstatic->getNomUrl(1,'composition').' ('.$value['nb'].($value['nb_total'] > $value['nb']?'->'.$value['nb_total']:'').')    </td>';
|
||||
print '<td>'.$productstatic->getNomUrl(1,'composition').' ('.$value['nb'].')    </td>';
|
||||
if (! empty($conf->stock->enabled)) print '<td>'.$langs->trans("Stock").' : <b>'.$productstatic->stock_reel.'</b></td>';
|
||||
print '</tr>';
|
||||
}
|
||||
print '</table>';
|
||||
print '</td></tr>';
|
||||
}
|
||||
|
||||
// Number of parent virtual products
|
||||
print '<tr><td>'.$langs->trans("ParentProductsNumber").'</td><td>';
|
||||
print $form->textwithpicto(count($prodsfather), $langs->trans('IfZeroItIsNotUsedByVirtualProduct'));
|
||||
print '</td>';
|
||||
|
||||
if (count($prodsfather) > 0)
|
||||
{
|
||||
print '<tr><td colspan="2">';
|
||||
print '<b>'.$langs->trans("ProductParentList").'</b><br>';
|
||||
print '<table class="nobordernopadding">';
|
||||
foreach($prodsfather as $value)
|
||||
{
|
||||
$idprod= $value["id"];
|
||||
$productstatic->id=$idprod;// $value["id"];
|
||||
$productstatic->type=$value["fk_product_type"];
|
||||
$productstatic->ref=$value['label'];
|
||||
print '<tr>';
|
||||
print '<td>'.$productstatic->getNomUrl(1,'composition').'</td>';;
|
||||
print '</tr>';
|
||||
}
|
||||
print '</table>';
|
||||
print '</td></tr>';
|
||||
}
|
||||
|
||||
print "</table>\n";
|
||||
|
||||
dol_fiche_end();
|
||||
}
|
||||
}
|
||||
*/
|
||||
/*
|
||||
* Fiche en mode edition
|
||||
*/
|
||||
@ -333,20 +255,43 @@ if ($id > 0 || ! empty($ref))
|
||||
$atleastonenotdefined=0;
|
||||
print '<tr><td colspan="2">';
|
||||
print $langs->trans("ProductAssociationList").'<br>';
|
||||
print '<table class="nobordernopadding centpercent">';
|
||||
|
||||
print '<form name="formComposedProduct" action="'.$_SERVER['PHP_SELF'].'" method="post" ">';
|
||||
print '<input type="hidden" name="action" value="save_composed_product" />';
|
||||
print '<input type="hidden" name="id" value="'.$id.'" />';
|
||||
|
||||
print '<table class="centpercent nobordernopadding">';
|
||||
|
||||
print '<tr class="liste_titre"><td>'.$langs->trans('ComposedProduct').'</td><td>'.$langs->trans('Qty').'</td><td>'.$langs->trans('ComposedProductDecreaseStock').'</td><td>'.$langs->trans('MinSupplierPrice').'</td><td>'.$langs->trans('Price').'</td><td>'.$langs->trans('Stock').'</td></tr>';
|
||||
|
||||
foreach($prods_arbo as $value)
|
||||
{
|
||||
$productstatic->id=$value['id'];
|
||||
$productstatic->type=$value['type'];
|
||||
//print '<pre>'.$productstatic->ref.'</pre>';
|
||||
//print $productstatic->getNomUrl(1).'<br>';
|
||||
//var_dump($value);
|
||||
print '<tr>';
|
||||
|
||||
$class=($class=='impair')?'pair':'impair';
|
||||
|
||||
print '<tr class="'.$class.'">';
|
||||
if ($value['level'] <= 1)
|
||||
{
|
||||
$notdefined=0;
|
||||
$productstatic->ref=$value['fullpath'];
|
||||
print '<td>'.$productstatic->getNomUrl(1,'composition').' ('.$value['nb'].')</td>';
|
||||
$nb_of_subproduct = $value['nb'];
|
||||
|
||||
print '<td>'.$productstatic->getNomUrl(1,'composition').'</td>';
|
||||
|
||||
if($user->rights->produit->creer || $user->rights->service->creer) {
|
||||
print '<td><input type="text" value="'.$nb_of_subproduct.'" name="TProduct['.$productstatic->id.'][qty]" size="4" /></td>';
|
||||
print '<td><input type="checkbox" name="TProduct['.$productstatic->id.'][incdec]" value="1" '.($value['incdec']==1?'checked="checked"':'' ).' /></td>';
|
||||
|
||||
}
|
||||
else{
|
||||
print '<td>'.$nb_of_subproduct.'</td>';
|
||||
print '<td>'.($value['incdec']==1?'x':'' ).'</td>';
|
||||
}
|
||||
|
||||
|
||||
|
||||
print '<td align="right">';
|
||||
if ($product_fourn->find_min_price_product_fournisseur($productstatic->id) > 0)
|
||||
{
|
||||
@ -367,26 +312,35 @@ if ($id > 0 || ! empty($ref))
|
||||
{
|
||||
print ' ';
|
||||
}
|
||||
print $productstatic->getNomUrl(1,'composition').' ('.$value['nb'].')</td>';
|
||||
print '<td><td>';
|
||||
print '<td><td>';
|
||||
print $productstatic->getNomUrl(1,'composition').'</td>';
|
||||
print '<td>'.$value['nb'].'</td>';
|
||||
print '<td> </td>';
|
||||
print '<td> <td>';
|
||||
print '<td> <td>';
|
||||
if (! empty($conf->stock->enabled)) print '<td align="right"></td>'; // Real stock
|
||||
}
|
||||
print '</tr>';
|
||||
}
|
||||
print '<tr>';
|
||||
print '<td colspan="2">'.$langs->trans("TotalBuyingPriceMin").': ';
|
||||
print '<tr class="liste_total">';
|
||||
print '<td colspan="3" align="right">'.$langs->trans("TotalBuyingPriceMin").': ';
|
||||
if ($atleastonenotdefined) print $langs->trans("Unknown").' ('.$langs->trans("SomeSubProductHaveNoPrices").')';
|
||||
print '</td>';
|
||||
print '<td align="right">'.($atleastonenotdefined?'':price($total,'','',0,0,-1,$conf->currency)).'</td>';
|
||||
print '<td align="right" class="liste_total">'.($atleastonenotdefined?'':price($total,'','',0,0,-1,$conf->currency)).'</td>';
|
||||
if (! empty($conf->stock->enabled)) print '<td class="liste_total" align="right"> </td>';
|
||||
print '</tr>';
|
||||
print '</table>';
|
||||
|
||||
if($user->rights->produit->creer || $user->rights->service->creer) {
|
||||
print '<div class="tabsAction"><input type="submit" value="'.$langs->trans('Save').'" /></div>';
|
||||
}
|
||||
|
||||
print '</form>';
|
||||
|
||||
print '</td></tr>';
|
||||
}
|
||||
|
||||
// Number of parent virtual products
|
||||
print '<tr><td>'.$langs->trans("ParentProductsNumber").'</td><td>';
|
||||
print '<tr class="pair"><td>'.$langs->trans("ParentProductsNumber").'</td><td>';
|
||||
print $form->textwithpicto(count($prodsfather), $langs->trans('IfZeroItIsNotUsedByVirtualProduct'));
|
||||
print '</td>';
|
||||
|
||||
|
||||
@ -292,7 +292,7 @@ class MouvementStock extends CommonObject
|
||||
}
|
||||
|
||||
// Add movement for sub products (recursive call)
|
||||
if (! $error && ! empty($conf->global->PRODUIT_SOUSPRODUITS))
|
||||
if (! $error && ! empty($conf->global->PRODUIT_SOUSPRODUITS) && empty($conf->global->INDEPENDANT_SUBPRODUCT_STOCK))
|
||||
{
|
||||
$error = $this->_createSubProduct($user, $fk_product, $entrepot_id, $qty, $type, 0, $label, $inventorycode); // we use 0 as price, because pmp is not changed for subproduct
|
||||
}
|
||||
@ -341,8 +341,7 @@ class MouvementStock extends CommonObject
|
||||
$sql = "SELECT fk_product_pere, fk_product_fils, qty";
|
||||
$sql.= " FROM ".MAIN_DB_PREFIX."product_association";
|
||||
$sql.= " WHERE fk_product_pere = ".$idProduct;
|
||||
// TODO Select only subproduct with incdec tag
|
||||
//$sql.= " AND incdec = 1";
|
||||
$sql.= " AND incdec = 1";
|
||||
|
||||
dol_syslog(get_class($this)."::_createSubProduct", LOG_DEBUG);
|
||||
$resql=$this->db->query($sql);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user