diff --git a/htdocs/commande/class/commande.class.php b/htdocs/commande/class/commande.class.php
index 6eb61c0f983..3f6c9d956a5 100644
--- a/htdocs/commande/class/commande.class.php
+++ b/htdocs/commande/class/commande.class.php
@@ -2014,7 +2014,7 @@ class Commande extends CommonOrder
$sql .= ' l.total_ht, l.total_ttc, l.total_tva, l.total_localtax1, l.total_localtax2, l.date_start, l.date_end,';
$sql .= ' l.fk_unit,';
$sql .= ' l.fk_multicurrency, l.multicurrency_code, l.multicurrency_subprice, l.multicurrency_total_ht, l.multicurrency_total_tva, l.multicurrency_total_ttc,';
- $sql .= ' p.ref as product_ref, p.description as product_desc, p.fk_product_type, p.label as product_label, p.tobatch as product_tobatch,';
+ $sql .= ' p.ref as product_ref, p.description as product_desc, p.fk_product_type, p.label as product_label, p.tosell as product_tosell, p.tobuy as product_tobuy, p.tobatch as product_tobatch,';
$sql .= ' p.weight, p.weight_units, p.volume, p.volume_units';
$sql .= ' FROM '.MAIN_DB_PREFIX.'commandedet as l';
$sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON (p.rowid = l.fk_product)';
@@ -2077,6 +2077,8 @@ class Commande extends CommonOrder
$line->libelle = $objp->product_label;
$line->product_label = $objp->product_label;
$line->product_desc = $objp->product_desc;
+ $line->product_tosell = $objp->product_tosell;
+ $line->product_tobuy = $objp->product_tobuy;
$line->product_tobatch = $objp->product_tobatch;
$line->fk_product_type = $objp->fk_product_type; // Produit ou service
$line->fk_unit = $objp->fk_unit;
diff --git a/htdocs/core/class/commonobject.class.php b/htdocs/core/class/commonobject.class.php
index 3020c53f83d..7c3b368ae14 100644
--- a/htdocs/core/class/commonobject.class.php
+++ b/htdocs/core/class/commonobject.class.php
@@ -62,6 +62,11 @@ abstract class CommonObject
*/
public $error;
+ /**
+ * @var string Error string that is hidden but can be used to store complementatry technical code.
+ */
+ public $errorhidden;
+
/**
* @var string[] Array of error strings
*/
diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php
index 85b930ae654..76ed140870f 100644
--- a/htdocs/core/lib/functions.lib.php
+++ b/htdocs/core/lib/functions.lib.php
@@ -3190,7 +3190,7 @@ function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $
'1downarrow', '1uparrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected',
'accountancy', 'account', 'accountline', 'action', 'add', 'address', 'bank_account', 'barcode', 'bank', 'bill', 'bookmark', 'bom', 'building',
'cash-register', 'category', 'check', 'clock', 'close_title', 'company', 'contact', 'contract', 'cubes',
- 'delete', 'dolly', 'dollyrevert', 'donation', 'edit', 'ellipsis-h', 'email', 'external-link-alt', 'external-link-square-alt',
+ 'delete', 'dolly', 'dollyrevert', 'donation', 'edit', 'ellipsis-h', 'email', 'eraser', 'external-link-alt', 'external-link-square-alt',
'filter', 'file-code', 'file-export', 'file-import', 'file-upload', 'folder', 'folder-open', 'globe', 'globe-americas', 'grip', 'grip_title', 'group',
'help', 'holiday',
'intervention', 'label', 'language', 'list', 'listlight', 'lot',
diff --git a/htdocs/core/lib/product.lib.php b/htdocs/core/lib/product.lib.php
index 09232fbd012..909e51dc6d7 100644
--- a/htdocs/core/lib/product.lib.php
+++ b/htdocs/core/lib/product.lib.php
@@ -90,16 +90,6 @@ function product_prepare_head($object)
$h++;
}
- $head[$h][0] = DOL_URL_ROOT."/product/stats/card.php?id=".$object->id;
- $head[$h][1] = $langs->trans('Statistics');
- $head[$h][2] = 'stats';
- $h++;
-
- $head[$h][0] = DOL_URL_ROOT."/product/stats/facture.php?showmessage=1&id=".$object->id;
- $head[$h][1] = $langs->trans('Referers');
- $head[$h][2] = 'referers';
- $h++;
-
if (!empty($conf->variants->enabled) && ($object->isProduct() || $object->isService())) {
global $db;
@@ -149,6 +139,16 @@ function product_prepare_head($object)
}
}
+ $head[$h][0] = DOL_URL_ROOT."/product/stats/facture.php?showmessage=1&id=".$object->id;
+ $head[$h][1] = $langs->trans('Referers');
+ $head[$h][2] = 'referers';
+ $h++;
+
+ $head[$h][0] = DOL_URL_ROOT."/product/stats/card.php?id=".$object->id;
+ $head[$h][1] = $langs->trans('Statistics');
+ $head[$h][2] = 'stats';
+ $h++;
+
// Show more tabs from modules
// Entries must be declared in modules descriptor with line
// $this->tabs = array('entity:+tabname:Title:@mymodule:/mymodule/mypage.php?id=__ID__'); to add new tab
diff --git a/htdocs/expedition/card.php b/htdocs/expedition/card.php
index 5fa0ebe26ea..2cef2c39320 100644
--- a/htdocs/expedition/card.php
+++ b/htdocs/expedition/card.php
@@ -1047,7 +1047,7 @@ if ($action == 'create')
$i++;
}
print '});
- jQuery("#autoreset").click(function() {';
+ jQuery("#autoreset").click(function() { console.log("Reset values to 0"); ';
$i = 0;
while ($i < $numAsked)
{
@@ -1075,12 +1075,12 @@ if ($action == 'create')
print '
'.$langs->trans("QtyToShip");
if (empty($conf->productbatch->enabled))
{
- print ' '.$langs->trans("Fill").'';
+ print ' '.$langs->trans("Fill").'';
print ' / ';
} else {
print ' ';
}
- print ''.$langs->trans("Reset").'';
+ print ''.img_picto($langs->trans("Reset"), 'eraser').'';
print ' | ';
if (!empty($conf->stock->enabled))
{
@@ -1114,7 +1114,7 @@ if ($action == 'create')
if (!empty($line->date_start)) $type = 1;
if (!empty($line->date_end)) $type = 1;
- print ''."\n";
+ print ''."\n";
print ''."\n";
// Product label
@@ -1125,13 +1125,16 @@ if ($action == 'create')
//var_dump($product->stock_warehouse[1]);
print '| ';
- print ''; // ancre pour retourner sur la ligne
+ print ''; // ancre pour retourner sur la ligne
// Show product and description
$product_static->type = $line->fk_product_type;
$product_static->id = $line->fk_product;
$product_static->ref = $line->ref;
+ $product_static->status = $line->product_tosell;
+ $product_static->status_buy = $line->product_tobuy;
$product_static->status_batch = $line->product_tobatch;
+
$text = $product_static->getNomUrl(1);
$text .= ' - '.(!empty($line->label) ? $line->label : $line->product_label);
$description = ($conf->global->PRODUIT_DESC_IN_FORM ? '' : dol_htmlentitiesbr($line->desc));
@@ -2048,7 +2051,7 @@ if ($action == 'create')
$sql .= ", ed.rowid as shipmentline_id, ed.qty as qty_shipped, ed.fk_expedition as expedition_id, ed.fk_origin_line, ed.fk_entrepot";
$sql .= ", e.rowid as shipment_id, e.ref as shipment_ref, e.date_creation, e.date_valid, e.date_delivery, e.date_expedition";
//if ($conf->delivery_note->enabled) $sql .= ", l.rowid as livraison_id, l.ref as livraison_ref, l.date_delivery, ld.qty as qty_received";
- $sql .= ', p.label as product_label, p.ref, p.fk_product_type, p.rowid as prodid, p.tobatch as product_tobatch';
+ $sql .= ', p.label as product_label, p.ref, p.fk_product_type, p.rowid as prodid, p.tosell as product_tosell, p.tobuy as product_tobuy, p.tobatch as product_tobatch';
$sql .= ', p.description as product_desc';
$sql .= " FROM ".MAIN_DB_PREFIX."expeditiondet as ed";
$sql .= ", ".MAIN_DB_PREFIX."expedition as e";
@@ -2075,7 +2078,10 @@ if ($action == 'create')
if ($obj)
{
// $obj->rowid is rowid in $origin."det" table
- $alreadysent[$obj->rowid][$obj->shipmentline_id] = array('shipment_ref'=>$obj->shipment_ref, 'shipment_id'=>$obj->shipment_id, 'warehouse'=>$obj->fk_entrepot, 'qty_shipped'=>$obj->qty_shipped, 'date_valid'=>$db->jdate($obj->date_valid), 'date_delivery'=>$db->jdate($obj->date_delivery));
+ $alreadysent[$obj->rowid][$obj->shipmentline_id] = array(
+ 'shipment_ref'=>$obj->shipment_ref, 'shipment_id'=>$obj->shipment_id, 'warehouse'=>$obj->fk_entrepot, 'qty_shipped'=>$obj->qty_shipped,
+ 'product_tosell'=>$obj->product_tosell, 'product_tobuy'=>$obj->product_tobuy, 'product_tobatch'=>$obj->product_tobatch,
+ 'date_valid'=>$db->jdate($obj->date_valid), 'date_delivery'=>$db->jdate($obj->date_delivery));
}
$i++;
}
@@ -2084,6 +2090,7 @@ if ($action == 'create')
}
print ' |
';
+
// Loop on each product to send/sent
for ($i = 0; $i < $num_prod; $i++)
{
@@ -2119,6 +2126,8 @@ if ($action == 'create')
$product_static->type = $lines[$i]->fk_product_type;
$product_static->id = $lines[$i]->fk_product;
$product_static->ref = $lines[$i]->ref;
+ $product_static->status = $lines[$i]->product_tosell;
+ $product_static->status_buy = $lines[$i]->product_tobuy;
$product_static->status_batch = $lines[$i]->product_tobatch;
$product_static->weight = $lines[$i]->weight;
diff --git a/htdocs/expedition/class/expedition.class.php b/htdocs/expedition/class/expedition.class.php
index abc59080912..6c2faa0624d 100644
--- a/htdocs/expedition/class/expedition.class.php
+++ b/htdocs/expedition/class/expedition.class.php
@@ -619,10 +619,8 @@ class Expedition extends CommonObject
// Tracking url
$this->getUrlTrackingStatus($obj->tracking_number);
- /*
- * Thirdparty
- */
- $result = $this->fetch_thirdparty();
+ // Thirdparty
+ $result = $this->fetch_thirdparty(); // TODO Remove this
// Retrieve extrafields
$this->fetch_optionals();
@@ -919,6 +917,7 @@ class Expedition extends CommonObject
$line->entrepot_id = $entrepot_id;
$line->origin_line_id = $id;
+ $line->fk_origin_line = $id;
$line->qty = $qty;
$orderline = new OrderLine($this->db);
@@ -931,31 +930,37 @@ class Expedition extends CommonObject
{
$fk_product = $orderline->fk_product;
- if (!($entrepot_id > 0) && empty($conf->global->STOCK_WAREHOUSE_NOT_REQUIRED_FOR_SHIPMENTS))
- {
+ if (!($entrepot_id > 0) && empty($conf->global->STOCK_WAREHOUSE_NOT_REQUIRED_FOR_SHIPMENTS)) {
$langs->load("errors");
$this->error = $langs->trans("ErrorWarehouseRequiredIntoShipmentLine");
return -1;
}
- if ($conf->global->STOCK_MUST_BE_ENOUGH_FOR_SHIPMENT)
- {
- // Check must be done for stock of product into warehouse if $entrepot_id defined
+ if ($conf->global->STOCK_MUST_BE_ENOUGH_FOR_SHIPMENT) {
$product = new Product($this->db);
- $result = $product->fetch($fk_product);
+ $product->fetch($fk_product);
+ // Check must be done for stock of product into warehouse if $entrepot_id defined
if ($entrepot_id > 0) {
$product->load_stock('warehouseopen');
$product_stock = $product->stock_warehouse[$entrepot_id]->real;
- } else $product_stock = $product->stock_reel;
+ } else {
+ $product_stock = $product->stock_reel;
+ }
$product_type = $product->type;
- if ($product_type == 0 && $product_stock < $qty)
- {
- $langs->load("errors");
- $this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnShipment', $product->ref);
- $this->db->rollback();
- return -3;
+ if ($product_type == 0 || !empty($conf->global->STOCK_SUPPORTS_SERVICES)) {
+ $isavirtualproduct = ($product->hasFatherOrChild(1) > 0);
+ // The product is qualified for a check of quantity (must be enough in stock to be added into shipment).
+ if (!$isavirtualproduct || empty($conf->global->PRODUIT_SOUSPRODUITS) || ($isavirtualproduct && empty($conf->global->STOCK_EXCLUDE_VIRTUAL_PRODUCTS))) { // If STOCK_EXCLUDE_VIRTUAL_PRODUCTS is set, we do not manage stock for kits/virtual products.
+ if ($product_stock < $qty) {
+ $langs->load("errors");
+ $this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnShipment', $product->ref);
+ $this->errorhidden = 'ErrorStockIsNotEnoughToAddProductOnShipment';
+ $this->db->rollback();
+ return -3;
+ }
+ }
}
}
}
@@ -1576,7 +1581,7 @@ class Expedition extends CommonObject
$sql .= ", cd.fk_multicurrency, cd.multicurrency_code, cd.multicurrency_subprice, cd.multicurrency_total_ht, cd.multicurrency_total_tva, cd.multicurrency_total_ttc, cd.rang";
$sql .= ", ed.rowid as line_id, ed.qty as qty_shipped, ed.fk_origin_line, ed.fk_entrepot";
$sql .= ", p.ref as product_ref, p.label as product_label, p.fk_product_type";
- $sql .= ", p.weight, p.weight_units, p.length, p.length_units, p.surface, p.surface_units, p.volume, p.volume_units, p.tobatch as product_tobatch";
+ $sql .= ", p.weight, p.weight_units, p.length, p.length_units, p.surface, p.surface_units, p.volume, p.volume_units, p.tosell as product_tosell, p.tobuy as product_tobuy, p.tobatch as product_tobatch";
$sql .= " FROM ".MAIN_DB_PREFIX."expeditiondet as ed, ".MAIN_DB_PREFIX."commandedet as cd";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON p.rowid = cd.fk_product";
$sql .= " WHERE ed.fk_expedition = ".$this->id;
@@ -1638,6 +1643,8 @@ class Expedition extends CommonObject
$line->product_ref = $obj->product_ref;
$line->product_label = $obj->product_label;
$line->libelle = $obj->product_label; // TODO deprecated
+ $line->product_tosell = $obj->product_tosell;
+ $line->product_tobuy = $obj->product_tobuy;
$line->product_tobatch = $obj->product_tobatch;
$line->label = $obj->custom_label;
$line->description = $obj->description;
diff --git a/htdocs/product/class/product.class.php b/htdocs/product/class/product.class.php
index 59d34f728b1..292690020b9 100644
--- a/htdocs/product/class/product.class.php
+++ b/htdocs/product/class/product.class.php
@@ -3807,11 +3807,11 @@ class Product extends CommonObject
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
- * Verifie si c'est un sous-produit
+ * Check if it is a sub-product into a kit
*
- * @param int $fk_parent Id du produit auquel le produit est lie
- * @param int $fk_child Id du produit lie
- * @return int < 0 si erreur, > 0 si ok
+ * @param int $fk_parent Id of parent kit product
+ * @param int $fk_child Id of child product
+ * @return int <0 if KO, >0 if OK
*/
public function is_sousproduit($fk_parent, $fk_child)
{
@@ -3827,6 +3827,7 @@ class Product extends CommonObject
if ($num > 0) {
$obj = $this->db->fetch_object($result);
+
$this->is_sousproduit_qty = $obj->qty;
$this->is_sousproduit_incdec = $obj->incdec;
@@ -4221,17 +4222,26 @@ class Product extends CommonObject
}
/**
- * Return all parent products for current product (first level only)
+ * Count all parent and children products for current product (first level only)
*
- * @return int Nb of father + child
+ * @param int $mode 0=Both parent and child, -1=Parents only, 1=Children only
+ * @return int Nb of father + child
+ * @see getFather(), get_sousproduits_arbo()
*/
- public function hasFatherOrChild()
+ public function hasFatherOrChild($mode = 0)
{
$nb = 0;
$sql = "SELECT COUNT(pa.rowid) as nb";
$sql .= " FROM ".MAIN_DB_PREFIX."product_association as pa";
- $sql .= " WHERE pa.fk_product_fils = ".$this->id." OR pa.fk_product_pere = ".$this->id;
+ if ($mode == 0) {
+ $sql .= " WHERE pa.fk_product_fils = ".$this->id." OR pa.fk_product_pere = ".$this->id;
+ } elseif ($mode == -1) {
+ $sql .= " WHERE pa.fk_product_fils = ".$this->id; // We are a child, so we found lines that link to parents (can have several parents)
+ } elseif ($mode == 1) {
+ $sql .= " WHERE pa.fk_product_pere = ".$this->id; // We are a parent, so we found lines that link to children (can have several children)
+ }
+
$resql = $this->db->query($sql);
if ($resql) {
$obj = $this->db->fetch_object($resql);
@@ -4297,6 +4307,7 @@ class Product extends CommonObject
* Return all parent products for current product (first level only)
*
* @return array Array of product
+ * @see hasFatherOrChild()
*/
public function getFather()
{
diff --git a/htdocs/product/stats/commande.php b/htdocs/product/stats/commande.php
index 4bedbd3d2ba..f89305b4d95 100644
--- a/htdocs/product/stats/commande.php
+++ b/htdocs/product/stats/commande.php
@@ -170,6 +170,7 @@ if ($id > 0 || !empty($ref))
if (!empty($search_year)) $option .= '&search_year='.urlencode($search_year);
print '