NEW shipment kits with dispatcher v2 (#33750)
* NEW shipment kits with dispatcher v2 * Show stock of virtual product on select warehouse when dispatching a shipment --------- Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
This commit is contained in:
parent
de763b4b43
commit
c85cc3061b
17 changed files with 1675 additions and 582 deletions
|
|
@ -456,6 +456,13 @@ print '<td>';
|
|||
print ajax_constantonoff('SHIPPING_DISPLAY_STOCK_ENTRY_DATE');
|
||||
print '</td></tr>';
|
||||
|
||||
print '<tr class="oddeven">';
|
||||
print '<td>'.$langs->trans('SHIPPING_SELL_EAT_BY_DATE_PRE_SELECT_EARLIEST');
|
||||
print '</td>';
|
||||
print '<td>';
|
||||
print ajax_constantonoff('SHIPPING_SELL_EAT_BY_DATE_PRE_SELECT_EARLIEST');
|
||||
print '</td></tr>';
|
||||
|
||||
$substitutionarray = pdf_getSubstitutionArray($langs, null, null, 2);
|
||||
$substitutionarray['__(AnyTranslationKey)__'] = $langs->trans("Translation");
|
||||
$htmltext = '<i>'.$langs->trans("AvailableVariables").':<br>';
|
||||
|
|
|
|||
|
|
@ -228,6 +228,15 @@ $formproduct = new FormProduct($db);
|
|||
|
||||
|
||||
$disableStockCalculateOn = array();
|
||||
if (getDolGlobalInt('PRODUIT_SOUSPRODUITS')) {
|
||||
$langs->load('products');
|
||||
$disableStockCalculateOn[] = 'BILL';
|
||||
$disableStockCalculateOn[] = 'VALIDATE_ORDER';
|
||||
$disableStockCalculateOn[] = 'SUPPLIER_BILL';
|
||||
$disableStockCalculateOn[] = 'SUPPLIER_VALIDATE_ORDER';
|
||||
$disableStockCalculateOn[] = 'SHIPMENT_CLOSE';
|
||||
print info_admin($langs->trans('WhenProductVirtualOnOptionAreForced'));
|
||||
}
|
||||
if (isModEnabled('productbatch')) {
|
||||
// If module lot/serial enabled, we force the inc/dec mode to STOCK_CALCULATE_ON_SHIPMENT_CLOSE and STOCK_CALCULATE_ON_RECEPTION_CLOSE
|
||||
$langs->load("productbatch");
|
||||
|
|
|
|||
|
|
@ -1646,13 +1646,38 @@ class Commande extends CommonOrder
|
|||
$result = $product->fetch($fk_product);
|
||||
$product_type = $product->type;
|
||||
|
||||
if (getDolGlobalString('STOCK_MUST_BE_ENOUGH_FOR_ORDER') && $product_type == 0 && $product->stock_reel < $qty) {
|
||||
$langs->load("errors");
|
||||
$this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnOrder', $product->ref);
|
||||
$this->errors[] = $this->error;
|
||||
dol_syslog(get_class($this)."::addline error=Product ".$product->ref.": ".$this->error, LOG_ERR);
|
||||
$this->db->rollback();
|
||||
return self::STOCK_NOT_ENOUGH_FOR_ORDER;
|
||||
if (getDolGlobalString('STOCK_MUST_BE_ENOUGH_FOR_ORDER') && $product_type == 0) {
|
||||
// get real stock
|
||||
$productChildrenNb = 0;
|
||||
if (getDolGlobalInt('PRODUIT_SOUSPRODUITS')) {
|
||||
$productChildrenNb = $product->hasFatherOrChild(1);
|
||||
}
|
||||
if ($productChildrenNb > 0) {
|
||||
// compute real stock from each subcomponent
|
||||
$product_stock = null;
|
||||
$product->loadStockForVirtualProduct('warehouseopen', $qty);
|
||||
foreach ($product->stock_warehouse as $componentStockWarehouse) {
|
||||
if ($product_stock === null) {
|
||||
$product_stock = $componentStockWarehouse->real;
|
||||
} else {
|
||||
$product_stock = min($product_stock, $componentStockWarehouse->real);
|
||||
}
|
||||
}
|
||||
if ($product_stock === null) {
|
||||
$product_stock = 0;
|
||||
}
|
||||
} else {
|
||||
$product_stock = $product->stock_reel;
|
||||
}
|
||||
|
||||
if ($product_stock < $qty) {
|
||||
$langs->load("errors");
|
||||
$this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnOrder', $product->ref);
|
||||
$this->errors[] = $this->error;
|
||||
dol_syslog(get_class($this)."::addline error=Product ".$product->ref.": ".$this->error, LOG_ERR);
|
||||
$this->db->rollback();
|
||||
return self::STOCK_NOT_ENOUGH_FOR_ORDER;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Calcul du total TTC et de la TVA pour la ligne a partir de
|
||||
|
|
@ -3187,15 +3212,40 @@ class Commande extends CommonOrder
|
|||
$result = $product->fetch($line->fk_product);
|
||||
$product_type = $product->type;
|
||||
|
||||
if (getDolGlobalString('STOCK_MUST_BE_ENOUGH_FOR_ORDER') && $product_type == 0 && $product->stock_reel < $qty) {
|
||||
$langs->load("errors");
|
||||
$this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnOrder', $product->ref);
|
||||
$this->errors[] = $this->error;
|
||||
if (getDolGlobalString('STOCK_MUST_BE_ENOUGH_FOR_ORDER') && $product_type == 0) {
|
||||
// get real stock
|
||||
$productChildrenNb = 0;
|
||||
if (getDolGlobalInt('PRODUIT_SOUSPRODUITS')) {
|
||||
$productChildrenNb = $product->hasFatherOrChild(1);
|
||||
}
|
||||
if ($productChildrenNb > 0) {
|
||||
// compute real stock from each subcomponent
|
||||
$product_stock = null;
|
||||
$product->loadStockForVirtualProduct('warehouseopen', $qty);
|
||||
foreach ($product->stock_warehouse as $componentStockWarehouse) {
|
||||
if ($product_stock === null) {
|
||||
$product_stock = $componentStockWarehouse->real;
|
||||
} else {
|
||||
$product_stock = min($product_stock, $componentStockWarehouse->real);
|
||||
}
|
||||
}
|
||||
if ($product_stock === null) {
|
||||
$product_stock = 0;
|
||||
}
|
||||
} else {
|
||||
$product_stock = $product->stock_reel;
|
||||
}
|
||||
|
||||
dol_syslog(get_class($this)."::addline error=Product ".$product->ref.": ".$this->error, LOG_ERR);
|
||||
if ($product_stock < $qty) {
|
||||
$langs->load("errors");
|
||||
$this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnOrder', $product->ref);
|
||||
$this->errors[] = $this->error;
|
||||
|
||||
$this->db->rollback();
|
||||
return self::STOCK_NOT_ENOUGH_FOR_ORDER;
|
||||
dol_syslog(get_class($this)."::addline error=Product ".$product->ref.": ".$this->error, LOG_ERR);
|
||||
|
||||
$this->db->rollback();
|
||||
return self::STOCK_NOT_ENOUGH_FOR_ORDER;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4256,11 +4256,36 @@ class Facture extends CommonInvoice
|
|||
$result = $product->fetch($fk_product);
|
||||
$product_type = $product->type;
|
||||
|
||||
if (getDolGlobalString('STOCK_MUST_BE_ENOUGH_FOR_INVOICE') && $product_type == 0 && $product->stock_reel < $qty) {
|
||||
$langs->load("errors");
|
||||
$this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnInvoice', $product->ref);
|
||||
$this->db->rollback();
|
||||
return -3;
|
||||
if (getDolGlobalString('STOCK_MUST_BE_ENOUGH_FOR_INVOICE') && $product_type == 0) {
|
||||
// get real stock
|
||||
$productChildrenNb = 0;
|
||||
if (getDolGlobalInt('PRODUIT_SOUSPRODUITS')) {
|
||||
$productChildrenNb = $product->hasFatherOrChild(1);
|
||||
}
|
||||
if ($productChildrenNb > 0) {
|
||||
// compute real stock from each subcomponent
|
||||
$product_stock = null;
|
||||
$product->loadStockForVirtualProduct('warehouseopen', $qty);
|
||||
foreach ($product->stock_warehouse as $componentStockWarehouse) {
|
||||
if ($product_stock === null) {
|
||||
$product_stock = $componentStockWarehouse->real;
|
||||
} else {
|
||||
$product_stock = min($product_stock, $componentStockWarehouse->real);
|
||||
}
|
||||
}
|
||||
if ($product_stock === null) {
|
||||
$product_stock = 0;
|
||||
}
|
||||
} else {
|
||||
$product_stock = $product->stock_reel;
|
||||
}
|
||||
|
||||
if ($product_stock < $qty) {
|
||||
$langs->load("errors");
|
||||
$this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnInvoice', $product->ref);
|
||||
$this->db->rollback();
|
||||
return -3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4574,11 +4599,36 @@ class Facture extends CommonInvoice
|
|||
$result = $product->fetch($line->fk_product);
|
||||
$product_type = $product->type;
|
||||
|
||||
if (getDolGlobalString('STOCK_MUST_BE_ENOUGH_FOR_INVOICE') && $product_type == 0 && $product->stock_reel < $qty) {
|
||||
$langs->load("errors");
|
||||
$this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnInvoice', $product->ref);
|
||||
$this->db->rollback();
|
||||
return -3;
|
||||
if (getDolGlobalString('STOCK_MUST_BE_ENOUGH_FOR_INVOICE') && $product_type == 0) {
|
||||
// get real stock
|
||||
$productChildrenNb = 0;
|
||||
if (getDolGlobalInt('PRODUIT_SOUSPRODUITS')) {
|
||||
$productChildrenNb = $product->hasFatherOrChild(1);
|
||||
}
|
||||
if ($productChildrenNb > 0) {
|
||||
// compute real stock from each subcomponent
|
||||
$product_stock = null;
|
||||
$product->loadStockForVirtualProduct('warehouseopen', $qty);
|
||||
foreach ($product->stock_warehouse as $componentStockWarehouse) {
|
||||
if ($product_stock === null) {
|
||||
$product_stock = $componentStockWarehouse->real;
|
||||
} else {
|
||||
$product_stock = min($product_stock, $componentStockWarehouse->real);
|
||||
}
|
||||
}
|
||||
if ($product_stock === null) {
|
||||
$product_stock = 0;
|
||||
}
|
||||
} else {
|
||||
$product_stock = $product->stock_reel;
|
||||
}
|
||||
|
||||
if ($product_stock < $qty) {
|
||||
$langs->load("errors");
|
||||
$this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnInvoice', $product->ref);
|
||||
$this->db->rollback();
|
||||
return -3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
149
htdocs/expedition/ajax/interface.php
Normal file
149
htdocs/expedition/ajax/interface.php
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
<?php
|
||||
/* Copyright (C) 2024 Laurent Destailleur (eldy) <eldy@users.sourceforge.net>
|
||||
* Copyright (C) 2024 Lionel Vessiller <lvessiller@open-dsi.fr>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file htdocs/expedition/ajax/interface.php
|
||||
* \brief Ajax search component for Shipment.
|
||||
*/
|
||||
|
||||
if (!defined('NOREQUIRESOC')) {
|
||||
define('NOREQUIRESOC', '1');
|
||||
}
|
||||
if (!defined('NOCSRFCHECK')) {
|
||||
define('NOCSRFCHECK', '1');
|
||||
}
|
||||
if (!defined('NOTOKENRENEWAL')) {
|
||||
define('NOTOKENRENEWAL', '1');
|
||||
}
|
||||
if (!defined('NOREQUIREMENU')) {
|
||||
define('NOREQUIREMENU', '1');
|
||||
}
|
||||
if (!defined('NOREQUIREHTML')) {
|
||||
define('NOREQUIREHTML', '1');
|
||||
}
|
||||
if (!defined('NOREQUIREAJAX')) {
|
||||
define('NOREQUIREAJAX', '1');
|
||||
}
|
||||
|
||||
require '../../main.inc.php'; // Load $user and permissions
|
||||
/**
|
||||
* @var DoliDB $db
|
||||
* @var Translate $langs
|
||||
* @var User $user
|
||||
*/
|
||||
|
||||
$warehouse_id = GETPOSTINT('warehouse_id');
|
||||
$batch = GETPOST('batch', 'alphanohtml');
|
||||
$product_id = GETPOSTINT('product_id');
|
||||
$action = GETPOST('action', 'alphanohtml');
|
||||
|
||||
$result = restrictedArea($user, 'expedition');
|
||||
|
||||
$permissiontowrite = $user->hasRight('expedition', 'write');
|
||||
|
||||
$is_eat_by_enabled = !getDolGlobalInt('PRODUCT_DISABLE_EATBY');
|
||||
$is_sell_by_enabled = !getDolGlobalInt('PRODUCT_DISABLE_SELLBY');
|
||||
|
||||
|
||||
/*
|
||||
* View
|
||||
*/
|
||||
|
||||
top_httphead("application/json");
|
||||
|
||||
if ($action == 'updateselectbatchbywarehouse' && $permissiontowrite) {
|
||||
$resArr = array();
|
||||
|
||||
$sql = "SELECT pb.batch, pb.rowid, ps.fk_entrepot, pb.qty, e.ref as label, ps.fk_product";
|
||||
if ($is_eat_by_enabled) {
|
||||
$sql .= ", pl.eatby";
|
||||
}
|
||||
if ($is_sell_by_enabled) {
|
||||
$sql .= ", pl.sellby";
|
||||
}
|
||||
$sql .= " FROM ".$db->prefix()."product_batch as pb";
|
||||
$sql .= " LEFT JOIN ".$db->prefix()."product_stock as ps on ps.rowid = pb.fk_product_stock";
|
||||
$sql .= " LEFT JOIN ".$db->prefix()."entrepot as e on e.rowid = ps.fk_entrepot AND e.entity IN (".getEntity('stock').")";
|
||||
if ($is_eat_by_enabled || $is_sell_by_enabled) {
|
||||
$sql .= " LEFT JOIN ".$db->prefix()."product_lot as pl on ps.fk_product = pl.fk_product AND pb.batch = pl.batch";
|
||||
}
|
||||
$sql .= " WHERE ps.fk_product = ".((int) $product_id);
|
||||
if ($warehouse_id > 0) {
|
||||
$sql .= " AND fk_entrepot = '".((int) $warehouse_id)."'";
|
||||
}
|
||||
$sql .= " ORDER BY e.ref, pb.batch";
|
||||
|
||||
$resql = $db->query($sql);
|
||||
|
||||
if ($resql) {
|
||||
while ($obj = $db->fetch_object($resql)) {
|
||||
$eat_by_date_formatted = '';
|
||||
if ($is_eat_by_enabled && !empty($obj->eatby)) {
|
||||
$eat_by_date_formatted = dol_print_date($db->jdate($obj->eatby), 'day');
|
||||
}
|
||||
$sell_by_date_formatted = '';
|
||||
if ($is_sell_by_enabled && !empty($obj->sellby)) {
|
||||
$sell_by_date_formatted = dol_print_date($db->jdate($obj->sellby), 'day');
|
||||
}
|
||||
|
||||
// set qty
|
||||
if (!isset($resArr[$obj->batch])) {
|
||||
$resArr[$obj->batch] = array(
|
||||
'qty' => (float) $obj->qty,
|
||||
);
|
||||
} else {
|
||||
$resArr[$obj->batch]['qty'] += $obj->qty;
|
||||
}
|
||||
|
||||
// set eat-by date
|
||||
if (!isset($resArr[$obj->batch]['eatbydate'])) {
|
||||
$resArr[$obj->batch]['eatbydate'] = $eat_by_date_formatted;
|
||||
}
|
||||
|
||||
// set sell-by date
|
||||
if (!isset($resArr[$obj->batch]['sellbydate'])) {
|
||||
$resArr[$obj->batch]['sellbydate'] = $sell_by_date_formatted;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode($resArr);
|
||||
} elseif ($action == 'updateselectwarehousebybatch' && $permissiontowrite) {
|
||||
$res = 0;
|
||||
|
||||
$sql = "SELECT pb.batch, pb.rowid, ps.fk_entrepot, e.ref, pb.qty";
|
||||
$sql .= " FROM ".$db->prefix()."product_batch as pb";
|
||||
$sql .= " JOIN ".$db->prefix()."product_stock as ps on ps.rowid = pb.fk_product_stock";
|
||||
$sql .= " JOIN ".$db->prefix()."entrepot as e on e.rowid = ps.fk_entrepot AND e.entity IN (".getEntity('stock').")";
|
||||
$sql .= " WHERE ps.fk_product = ".((int) $product_id);
|
||||
if ($batch) {
|
||||
$sql .= " AND pb.batch = '".$db->escape($batch)."'";
|
||||
}
|
||||
$sql .= " ORDER BY e.ref, pb.batch";
|
||||
|
||||
$resql = $db->query($sql);
|
||||
|
||||
if ($resql) {
|
||||
if ($db->num_rows($resql) == 1) {
|
||||
$obj = $db->fetch_object($resql);
|
||||
$res = $obj->fk_entrepot;
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode($res);
|
||||
}
|
||||
|
|
@ -439,7 +439,14 @@ if (empty($reshook)) {
|
|||
} else {
|
||||
// batch mode
|
||||
if ($batch_line[$i]['qty'] > 0 || ($batch_line[$i]['qty'] == 0 && getDolGlobalString('SHIPMENT_GETS_ALL_ORDER_PRODUCTS'))) {
|
||||
$ret = $object->addline_batch($batch_line[$i], $array_options[$i]);
|
||||
$origin_line_id = (int) $batch_line[$i]['ix_l'];
|
||||
$origin_line = new OrderLine($db);
|
||||
$res = $origin_line->fetch($origin_line_id);
|
||||
if ($res <= 0) {
|
||||
$error++;
|
||||
setEventMessages($origin_line->error, $origin_line->errors, 'errors');
|
||||
}
|
||||
$ret = $object->addline_batch($batch_line[$i], $array_options[$i], $origin_line);
|
||||
if ($ret < 0) {
|
||||
setEventMessages($object->error, $object->errors, 'errors');
|
||||
$error++;
|
||||
|
|
@ -1252,13 +1259,22 @@ if ($action == 'create') {
|
|||
print '<!-- line for order line '.$line->id.' -->'."\n";
|
||||
print '<tr class="oddeven" id="row-'.$line->id.'">'."\n";
|
||||
|
||||
$qtyProdCom = $line->qty;
|
||||
$productChildrenNb = 0;
|
||||
// Product label
|
||||
if ($line->fk_product > 0) { // If predefined product
|
||||
$res = $product->fetch($line->fk_product);
|
||||
if ($res < 0) {
|
||||
dol_print_error($db, $product->error, $product->errors);
|
||||
}
|
||||
$product->load_stock('warehouseopen'); // Load all $product->stock_warehouse[idwarehouse]->detail_batch
|
||||
if (getDolGlobalInt('PRODUIT_SOUSPRODUITS')) {
|
||||
$productChildrenNb = $product->hasFatherOrChild(1);
|
||||
}
|
||||
if ($productChildrenNb > 0) {
|
||||
$product->loadStockForVirtualProduct('warehouseopen', $qtyProdCom);
|
||||
} else {
|
||||
$product->load_stock('warehouseopen'); // Load all $product->stock_warehouse[idwarehouse]->detail_batch
|
||||
}
|
||||
//var_dump($product->stock_warehouse[1]);
|
||||
|
||||
print '<td>';
|
||||
|
|
@ -1319,7 +1335,6 @@ if ($action == 'create') {
|
|||
print '<td class="center">'.$line->qty;
|
||||
print '<input name="qtyasked'.$indiceAsked.'" id="qtyasked'.$indiceAsked.'" type="hidden" value="'.$line->qty.'">';
|
||||
print ''.$unit_order.'</td>';
|
||||
$qtyProdCom = $line->qty;
|
||||
|
||||
// Qty already shipped
|
||||
print '<td class="center">';
|
||||
|
|
@ -1391,10 +1406,14 @@ if ($action == 'create') {
|
|||
if (getDolGlobalInt('STOCK_DISALLOW_NEGATIVE_TRANSFER')) {
|
||||
$stockMin = 0;
|
||||
}
|
||||
if ($product->stockable_product == Product::ENABLED_STOCK) {
|
||||
print $formproduct->selectWarehouses($tmpentrepot_id, 'entl'.$indiceAsked, '', 1, 0, $line->fk_product, '', 1, 0, array(), 'minwidth200', array(), 1, $stockMin, 'stock DESC, e.ref');
|
||||
if ($productChildrenNb > 0) {
|
||||
print $formproduct->selectWarehouses($tmpentrepot_id, 'entl'.$indiceAsked, '', 1, 0, 0, '', 0, 0, array(), 'minwidth200', array(), 1, $stockMin, 'stock DESC, e.ref');
|
||||
} else {
|
||||
print img_warning().' '.$langs->trans('StockDisabled');
|
||||
if ($product->stockable_product == Product::ENABLED_STOCK) {
|
||||
print $formproduct->selectWarehouses($tmpentrepot_id, 'entl'.$indiceAsked, '', 1, 0, $line->fk_product, '', 1, 0, array(), 'minwidth200', array(), 1, $stockMin, 'stock DESC, e.ref');
|
||||
} else {
|
||||
print img_warning().' '.$langs->trans('StockDisabled');
|
||||
}
|
||||
}
|
||||
|
||||
if ($tmpentrepot_id > 0 && $tmpentrepot_id == $warehouse_id) {
|
||||
|
|
@ -1636,10 +1655,12 @@ if ($action == 'create') {
|
|||
if (isModEnabled('stock')) {
|
||||
print '<td class="left">';
|
||||
if ($line->product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
|
||||
if ($product->stockable_product == Product::ENABLED_STOCK) {
|
||||
if ($product->stockable_product == Product::ENABLED_STOCK || $productChildrenNb > 0) {
|
||||
print $tmpwarehouseObject->getNomUrl(0).' ';
|
||||
print '<!-- Show details of stock -->';
|
||||
print '('.$stock.')';
|
||||
if ($productChildrenNb <= 0) {
|
||||
print '<!-- Show details of stock -->';
|
||||
print '('.$stock.')';
|
||||
}
|
||||
} else {
|
||||
print img_warning().' '.$langs->trans('StockDisabled');
|
||||
}
|
||||
|
|
@ -2686,6 +2707,34 @@ if ($action == 'create') {
|
|||
}
|
||||
}
|
||||
print $form->textwithtooltip(img_picto('', 'object_stock').' '.$langs->trans("DetailWarehouseNumber"), $detail);
|
||||
} elseif (count($lines[$i]->detail_children) > 1) {
|
||||
$detail = '';
|
||||
foreach ($lines[$i]->detail_children as $child_product_id => $child_stock_list) {
|
||||
foreach ($child_stock_list as $warehouse_id => $total_qty) {
|
||||
// get product from cache
|
||||
$child_product_label = '';
|
||||
if (!isset($conf->cache['product'][$child_product_id])) {
|
||||
$child_product = new Product($db);
|
||||
$child_product->fetch($child_product_id);
|
||||
$conf->cache['product'][$child_product_id] = $child_product;
|
||||
} else {
|
||||
$child_product = $conf->cache['product'][$child_product_id];
|
||||
}
|
||||
$child_product_label = $child_product->ref . ' ' . $child_product->label;
|
||||
|
||||
// get warehouse from cache
|
||||
if (!isset($conf->cache['warehouse'][$warehouse_id])) {
|
||||
$child_warehouse = new Entrepot($db);
|
||||
$child_warehouse->fetch($warehouse_id);
|
||||
$conf->cache['warehouse'][$warehouse_id] = $child_warehouse;
|
||||
} else {
|
||||
$child_warehouse = $conf->cache['warehouse'][$warehouse_id];
|
||||
}
|
||||
|
||||
$detail .= $langs->trans('DetailChildrenFormat', $child_product_label, $child_warehouse->label, price2num($total_qty, 'MS')).'<br>';
|
||||
}
|
||||
}
|
||||
print $form->textwithtooltip(img_picto('', 'object_stock').' '.$langs->trans('DetailWarehouseNumber'), $detail);
|
||||
}
|
||||
print '</td>';
|
||||
}
|
||||
|
|
@ -2746,9 +2795,25 @@ if ($action == 'create') {
|
|||
print '<input type="submit" class="button button-cancel" id="cancellinebutton" name="cancel" value="'.$langs->trans("Cancel").'"><br>';
|
||||
print '</td>';
|
||||
} elseif ($object->status == Expedition::STATUS_DRAFT) {
|
||||
$edit_url = $_SERVER["PHP_SELF"].'?id='.$object->id.'&action=editline&token='.newToken().'&lineid='.$lines[$i]->id;
|
||||
if (getDolGlobalInt('PRODUIT_SOUSPRODUITS')) {
|
||||
$product_id = $lines[$i]->fk_product;
|
||||
if (!isset($conf->cache['product'][$product_id])) {
|
||||
$product = new Product($db);
|
||||
$product->fetch($product_id);
|
||||
$conf->cache['product'][$product_id] = $product;
|
||||
} else {
|
||||
$product = $conf->cache['product'][$product_id];
|
||||
}
|
||||
|
||||
if ($product->hasFatherOrChild(1)) {
|
||||
$edit_url = dol_buildpath('/expedition/dispatch.php?id='.$object->id, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// edit-delete buttons
|
||||
print '<td class="linecoledit center">';
|
||||
print '<a class="editfielda reposition" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=editline&token='.newToken().'&lineid='.$lines[$i]->id.'">'.img_edit().'</a>';
|
||||
print '<a class="editfielda reposition" href="'.$edit_url.'">'.img_edit().'</a>';
|
||||
print '</td>';
|
||||
print '<td class="linecoldelete" width="10">';
|
||||
print '<a class="reposition" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=deleteline&token='.newToken().'&lineid='.$lines[$i]->id.'">'.img_delete().'</a>';
|
||||
|
|
|
|||
|
|
@ -258,7 +258,7 @@ class Expedition extends CommonObject
|
|||
public $commande;
|
||||
|
||||
/**
|
||||
* @var ExpeditionLigne[] array of shipping lines
|
||||
* @var array<int, ExpeditionLigne> array of shipping lines
|
||||
*/
|
||||
public $lines = array();
|
||||
|
||||
|
|
@ -496,15 +496,140 @@ class Expedition extends CommonObject
|
|||
if ($this->db->query($sql)) {
|
||||
// Insert of lines
|
||||
$num = count($this->lines);
|
||||
for ($i = 0; $i < $num; $i++) {
|
||||
if (empty($this->lines[$i]->product_type) || getDolGlobalString('STOCK_SUPPORTS_SERVICES') || getDolGlobalString('SHIPMENT_SUPPORTS_SERVICES')) {
|
||||
if (!isset($this->lines[$i]->detail_batch)) { // no batch management
|
||||
if ($this->create_line($this->lines[$i]->entrepot_id, $this->lines[$i]->origin_line_id, $this->lines[$i]->qty, $this->lines[$i]->rang, $this->lines[$i]->array_options) <= 0) {
|
||||
$error++;
|
||||
$kits_list = array();
|
||||
if (getDolGlobalInt('PRODUIT_SOUSPRODUITS')) {
|
||||
for ($i = 0; $i < $num; $i++) {
|
||||
if (empty($this->lines[$i]->product_type) || getDolGlobalString('STOCK_SUPPORTS_SERVICES') || getDolGlobalString('SHIPMENT_SUPPORTS_SERVICES')) {
|
||||
// virtual products
|
||||
$line = $this->lines[$i];
|
||||
if ($line->fk_product > 0) {
|
||||
if (!isset($kits_list[$line->fk_product])) {
|
||||
if (!is_object($line->product)) {
|
||||
$line_product = new Product($this->db);
|
||||
$result = $line_product->fetch($line->fk_product, '', '', '', 1, 1, 1);
|
||||
if ($result <= 0) {
|
||||
$error++;
|
||||
}
|
||||
} else {
|
||||
$line_product = $line->product;
|
||||
}
|
||||
|
||||
// get all children of virtual product
|
||||
$line_product->get_sousproduits_arbo();
|
||||
$prods_arbo = $line_product->get_arbo_each_prod($line->qty);
|
||||
if (count($prods_arbo) > 0) {
|
||||
$kits_list[$line->fk_product] = array(
|
||||
'arbo' => $prods_arbo,
|
||||
'total_qty' => $line->qty,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$kits_list[$line->fk_product]['total_qty'] += $line->qty;
|
||||
}
|
||||
}
|
||||
} else { // with batch management
|
||||
if ($this->create_line_batch($this->lines[$i], $this->lines[$i]->array_options) <= 0) {
|
||||
$error++;
|
||||
}
|
||||
}
|
||||
}
|
||||
$kits_id_cached = array();
|
||||
$sub_kits_id_cached = array();
|
||||
for ($i = 0; $i < $num; $i++) {
|
||||
$line = $this->lines[$i];
|
||||
if (empty($line->product_type) || getDolGlobalString('STOCK_SUPPORTS_SERVICES') || getDolGlobalString('SHIPMENT_SUPPORTS_SERVICES')) {
|
||||
$line_id = 0;
|
||||
if (!isset($kits_id_cached[$line->fk_product])) {
|
||||
if (!isset($line->detail_batch) || isset($kits_list[$line->fk_product])) { // no batch management or is kit
|
||||
$qty = isset($kits_list[$line->fk_product]) ? $kits_list[$line->fk_product]['total_qty'] : $line->qty;
|
||||
$warehouse_id = isset($kits_list[$line->fk_product]) ? 0 : $line->entrepot_id;
|
||||
$line_id = $this->create_line($warehouse_id, $line->origin_line_id, $qty, $line->rang, $line->array_options, 0, $line->fk_product);
|
||||
if ($line_id <= 0) {
|
||||
$error++;
|
||||
}
|
||||
if (isset($kits_list[$line->fk_product])) $kits_id_cached[$line->fk_product] = $line_id;
|
||||
} else { // with batch management
|
||||
if ($this->create_line_batch($line, $line->array_options) <= 0) {
|
||||
$error++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$line_id = $kits_id_cached[$line->fk_product];
|
||||
}
|
||||
|
||||
// virtual products
|
||||
if (isset($kits_list[$line->fk_product])) {
|
||||
$prods_arbo = $kits_list[$line->fk_product]['arbo'];
|
||||
$total_qty = $kits_list[$line->fk_product]['total_qty'];
|
||||
|
||||
// get all children of virtual product
|
||||
$parent_line_id = $line_id; // parent line created
|
||||
$level_last = 1;
|
||||
$product_child_id = 0;
|
||||
foreach ($prods_arbo as $index => $product_child_arr) {
|
||||
// 'id' => Id product
|
||||
// 'id_parent' => Id parent product
|
||||
// 'ref' => Ref product
|
||||
// 'nb' => Nb of units that compose parent product
|
||||
// 'nb_total' => // Nb of units for all nb of product
|
||||
// 'stock' => Stock
|
||||
// 'stock_alert' => Stock alert
|
||||
// 'label' => Label
|
||||
// 'fullpath' => // Full path label
|
||||
// 'type' =>
|
||||
// 'desiredstock' => Desired stock
|
||||
// 'level' => Level
|
||||
// 'incdec' => Need to be incremented or decremented
|
||||
// 'entity' => Entity
|
||||
$product_child_level = (int) $product_child_arr['level'];
|
||||
$product_child_incdec = !empty($product_child_arr['incdec']);
|
||||
|
||||
// detect new level
|
||||
if ($product_child_level != $level_last) {
|
||||
$parent_line_id = $line_id; // last line id
|
||||
$parent_product_id = $product_child_id; // last line id
|
||||
if (isset($kits_id_cached[$parent_product_id])) {
|
||||
$parent_line_id = $kits_id_cached[$parent_product_id];
|
||||
} else {
|
||||
$kits_id_cached[$parent_product_id] = $parent_line_id;
|
||||
}
|
||||
}
|
||||
|
||||
// determine if it's a kit : check next level
|
||||
$is_kit = false;
|
||||
$next_level = $product_child_level;
|
||||
$next_index = $index + 1;
|
||||
if (isset($prods_arbo[$next_index])) {
|
||||
$next_level = (int) $prods_arbo[$next_index]['level'];
|
||||
}
|
||||
if ($next_level > $product_child_level) {
|
||||
$is_kit = true;
|
||||
}
|
||||
|
||||
// determine quantity of sub-product
|
||||
$product_child_id = (int) $product_child_arr['id'];
|
||||
$product_child_qty = (float) $product_child_arr['nb_total']; // by default
|
||||
$warehouse_id = $line->entrepot_id; // by default
|
||||
if ($is_kit || !$product_child_incdec) {
|
||||
if (!$product_child_incdec) {
|
||||
$product_child_qty = 0;
|
||||
}
|
||||
$warehouse_id = 0; // no warehouse used for a kit or if stock is not managed (empty incdec)
|
||||
}
|
||||
|
||||
// create line for a child of virtual product
|
||||
if (!isset($sub_kits_id_cached[$product_child_id]) || $warehouse_id > 0) {
|
||||
$line_id = $this->create_line($warehouse_id, 0, $product_child_qty, $line->rang, $line->array_options, $parent_line_id, $product_child_id);
|
||||
if ($line_id <= 0) {
|
||||
$error++;
|
||||
dol_syslog(__METHOD__ . ' : ' . $this->errorsToString(), LOG_ERR);
|
||||
break;
|
||||
}
|
||||
|
||||
// if kit or not manage stock (empty incdec)
|
||||
if (empty($warehouse_id)) {
|
||||
$sub_kits_id_cached[$product_child_id] = $line_id;
|
||||
}
|
||||
}
|
||||
|
||||
$level_last = $product_child_level;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -589,9 +714,11 @@ class Expedition extends CommonObject
|
|||
* @param float $qty Quantity
|
||||
* @param int $rang Rang
|
||||
* @param array<string,mixed> $array_options extrafields array
|
||||
* @param int $parent_line_id Id of parent line for virtual products
|
||||
* @param int $product_id Id of product (child of virtual product)
|
||||
* @return int Return integer <0 if KO, line_id if OK
|
||||
*/
|
||||
public function create_line($entrepot_id, $origin_line_id, $qty, $rang = 0, $array_options = [])
|
||||
public function create_line($entrepot_id, $origin_line_id, $qty, $rang = 0, $array_options = [], $parent_line_id = 0, $product_id = 0)
|
||||
{
|
||||
//phpcs:enable
|
||||
global $user;
|
||||
|
|
@ -601,10 +728,18 @@ class Expedition extends CommonObject
|
|||
$expeditionline->entrepot_id = $entrepot_id;
|
||||
$expeditionline->fk_elementdet = $origin_line_id;
|
||||
$expeditionline->element_type = $this->origin;
|
||||
$expeditionline->fk_parent = $parent_line_id;
|
||||
$expeditionline->fk_product = $product_id;
|
||||
$expeditionline->qty = $qty;
|
||||
$expeditionline->rang = $rang;
|
||||
$expeditionline->array_options = $array_options;
|
||||
|
||||
if (!($expeditionline->fk_product > 0)) {
|
||||
$order_line = new OrderLine($this->db);
|
||||
$order_line->fetch($expeditionline->fk_elementdet);
|
||||
$expeditionline->fk_product = $order_line->fk_product;
|
||||
}
|
||||
|
||||
if (($lineId = $expeditionline->insert($user)) < 0) {
|
||||
$this->errors[] = $expeditionline->error;
|
||||
}
|
||||
|
|
@ -995,9 +1130,11 @@ class Expedition extends CommonObject
|
|||
* @param int $id Id of source line (order line)
|
||||
* @param float $qty Quantity
|
||||
* @param array<string,mixed> $array_options extrafields array
|
||||
* @param int $fk_product Id of product
|
||||
* @param int $fk_parent Id of parent line
|
||||
* @return int Return integer <0 if KO, >0 if OK
|
||||
*/
|
||||
public function addline($entrepot_id, $id, $qty, $array_options = [])
|
||||
public function addline($entrepot_id, $id, $qty, $array_options = [], $fk_product = 0, $fk_parent = 0)
|
||||
{
|
||||
global $conf, $langs;
|
||||
|
||||
|
|
@ -1008,6 +1145,8 @@ class Expedition extends CommonObject
|
|||
$line->origin_line_id = $id;
|
||||
$line->fk_elementdet = $id;
|
||||
$line->element_type = 'order';
|
||||
$line->fk_parent = $fk_parent;
|
||||
$line->fk_product = $fk_product;
|
||||
$line->qty = $qty;
|
||||
|
||||
$orderline = new OrderLine($this->db);
|
||||
|
|
@ -1016,6 +1155,9 @@ class Expedition extends CommonObject
|
|||
// Copy the rang of the order line to the expedition line
|
||||
$line->rang = $orderline->rang;
|
||||
$line->product_type = $orderline->product_type;
|
||||
if (!($line->fk_product > 0)) {
|
||||
$line->fk_product = $orderline->fk_product;
|
||||
}
|
||||
|
||||
if (isModEnabled('stock') && !empty($orderline->fk_product)) {
|
||||
$product = new Product($this->db);
|
||||
|
|
@ -1028,20 +1170,45 @@ class Expedition extends CommonObject
|
|||
}
|
||||
|
||||
if (getDolGlobalString('STOCK_MUST_BE_ENOUGH_FOR_SHIPMENT')) {
|
||||
// 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;
|
||||
$productChildrenNb = 0;
|
||||
if (getDolGlobalInt('PRODUIT_SOUSPRODUITS')) {
|
||||
$productChildrenNb = $product->hasFatherOrChild(1);
|
||||
}
|
||||
if ($productChildrenNb > 0) {
|
||||
$product_stock = null;
|
||||
$product->loadStockForVirtualProduct('warehouseopen', $line->qty);
|
||||
if ($entrepot_id > 0) {
|
||||
if (isset($product->stock_warehouse[$entrepot_id])) {
|
||||
$product_stock = $product->stock_warehouse[$entrepot_id]->real;
|
||||
}
|
||||
} else {
|
||||
foreach ($product->stock_warehouse as $componentStockWarehouse) {
|
||||
if ($product_stock === null) {
|
||||
$product_stock = $componentStockWarehouse->real;
|
||||
} else {
|
||||
$product_stock = min($product_stock, $componentStockWarehouse->real);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($product_stock === null) {
|
||||
$product_stock = 0;
|
||||
}
|
||||
} else {
|
||||
$product_stock = $product->stock_reel;
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
$product_type = $product->type;
|
||||
if ($product_type == 0 || getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
|
||||
$isavirtualproduct = ($product->hasFatherOrChild(1) > 0);
|
||||
$isavirtualproduct = ($productChildrenNb > 0);
|
||||
// The product is qualified for a check of quantity (must be enough in stock to be added into shipment).
|
||||
if (!$isavirtualproduct || !getDolGlobalString('PRODUIT_SOUSPRODUITS') || ($isavirtualproduct && !getDolGlobalString('STOCK_EXCLUDE_VIRTUAL_PRODUCTS'))) { // If STOCK_EXCLUDE_VIRTUAL_PRODUCTS is set, we do not manage stock for kits/virtual products.
|
||||
if ($product_stock < $qty && $product->stockable_product == Product::ENABLED_STOCK) {
|
||||
if ($product->stockable_product == Product::ENABLED_STOCK && $product_stock < $qty) {
|
||||
$langs->load("errors");
|
||||
$this->error = $langs->trans('ErrorStockIsNotEnoughToAddProductOnShipment', $product->ref);
|
||||
$this->errorhidden = 'ErrorStockIsNotEnoughToAddProductOnShipment';
|
||||
|
|
@ -1056,8 +1223,8 @@ class Expedition extends CommonObject
|
|||
|
||||
// If product need a batch number, we should not have called this function but addline_batch instead.
|
||||
// If this happen, we may have a bug in card.php page
|
||||
if (isModEnabled('productbatch') && !empty($orderline->fk_product) && !empty($orderline->product_tobatch)) {
|
||||
$this->error = 'ADDLINE_WAS_CALLED_INSTEAD_OF_ADDLINEBATCH '.$orderline->id.' '.$orderline->fk_product; //
|
||||
if (isModEnabled('productbatch') && !empty($line->fk_product) && !empty($orderline->product_tobatch)) {
|
||||
$this->error = 'ADDLINE_WAS_CALLED_INSTEAD_OF_ADDLINEBATCH '.$orderline->id.' '.$line->fk_product; //
|
||||
return -4;
|
||||
}
|
||||
|
||||
|
|
@ -1077,9 +1244,10 @@ class Expedition extends CommonObject
|
|||
*
|
||||
* @param array{detail:array<array{id_batch:int,q:int|float}>,qty:int|float,ix_l:int} $dbatch Array of value (key 'detail' -> Array, key 'qty' total quantity for line, key ix_l : original line index)
|
||||
* @param array<string,mixed> $array_options extrafields array
|
||||
* @param Object $origin_line Origin line (only from OrderLine at this moment)
|
||||
* @return int Return integer <0 if KO, >0 if OK
|
||||
*/
|
||||
public function addline_batch($dbatch, $array_options = [])
|
||||
public function addline_batch($dbatch, $array_options = [], $origin_line = null)
|
||||
{
|
||||
// phpcs:enable
|
||||
global $conf, $langs;
|
||||
|
|
@ -1131,6 +1299,12 @@ class Expedition extends CommonObject
|
|||
$line->fk_elementdet = $dbatch['ix_l'];
|
||||
$line->qty = $dbatch['qty'];
|
||||
$line->detail_batch = $tab;
|
||||
if (!($line->rang > 0)) {
|
||||
$line->rang = $origin_line->rang;
|
||||
}
|
||||
if (!($line->fk_product > 0)) {
|
||||
$line->fk_product = $origin_line->fk_product;
|
||||
}
|
||||
|
||||
// extrafields
|
||||
if (!getDolGlobalString('MAIN_EXTRAFIELDS_DISABLED') && is_array($array_options) && count($array_options) > 0) { // For avoid conflicts if trigger used
|
||||
|
|
@ -1331,19 +1505,27 @@ class Expedition extends CommonObject
|
|||
}
|
||||
|
||||
// Stock control
|
||||
if (!$error && isModEnabled('stock') &&
|
||||
$can_update_stock = isModEnabled('stock') &&
|
||||
((getDolGlobalString('STOCK_CALCULATE_ON_SHIPMENT') && $this->status > self::STATUS_DRAFT) ||
|
||||
(getDolGlobalString('STOCK_CALCULATE_ON_SHIPMENT_CLOSE') && $this->status == self::STATUS_CLOSED && $also_update_stock))) {
|
||||
(getDolGlobalString('STOCK_CALCULATE_ON_SHIPMENT_CLOSE') && $this->status == self::STATUS_CLOSED && $also_update_stock));
|
||||
if (!$error) {
|
||||
require_once DOL_DOCUMENT_ROOT."/product/stock/class/mouvementstock.class.php";
|
||||
|
||||
$langs->load("agenda");
|
||||
|
||||
// Loop on each product line to add a stock movement and delete features
|
||||
$sql = "SELECT cd.fk_product, cd.subprice, ed.qty, ed.fk_entrepot, ed.rowid as expeditiondet_id";
|
||||
$sql .= " FROM ".$this->db->prefix()."commandedet as cd,";
|
||||
$sql .= " ".$this->db->prefix()."expeditiondet as ed";
|
||||
// Loop on each product line to add a stock movement (contain sub-products)
|
||||
$sql = "SELECT ";
|
||||
$sql .= " ed.fk_product";
|
||||
$sql .= ", ed.qty, ed.fk_entrepot, ed.rowid as expeditiondet_id";
|
||||
$sql .= ", SUM(".$this->db->ifsql("pa.rowid IS NOT NULL", "1", "0").") as iskit";
|
||||
$sql .= ", ".$this->db->ifsql("pai.incdec IS NULL", "1", "pai.incdec")." as incdec";
|
||||
$sql .= " FROM ".$this->db->prefix()."expeditiondet as ed";
|
||||
$sql .= " LEFT JOIN ".$this->db->prefix()."product_association as pa ON pa.fk_product_pere = ed.fk_product";
|
||||
$sql .= " LEFT JOIN ".$this->db->prefix()."expeditiondet as edp ON edp.rowid = ed.fk_parent";
|
||||
$sql .= " LEFT JOIN ".$this->db->prefix()."product_association as pai ON pai.fk_product_pere = edp.fk_product AND pai.fk_product_fils = ed.fk_product";
|
||||
$sql .= " WHERE ed.fk_expedition = ".((int) $this->id);
|
||||
$sql .= " AND cd.rowid = ed.fk_elementdet";
|
||||
$sql .= " GROUP BY ed.fk_product, ed.qty, ed.fk_entrepot, ed.rowid, pai.incdec";
|
||||
$sql .= $this->db->order("ed.rowid", "DESC");
|
||||
|
||||
dol_syslog(get_class($this)."::delete select details", LOG_DEBUG);
|
||||
$resql = $this->db->query($sql);
|
||||
|
|
@ -1355,45 +1537,68 @@ class Expedition extends CommonObject
|
|||
for ($i = 0; $i < $cpt; $i++) {
|
||||
dol_syslog(get_class($this)."::delete movement index ".$i);
|
||||
$obj = $this->db->fetch_object($resql);
|
||||
$line_id = (int) $obj->expeditiondet_id;
|
||||
|
||||
$mouvS = new MouvementStock($this->db);
|
||||
// we do not log origin because it will be deleted
|
||||
$mouvS->origin = '';
|
||||
// get lot/serial
|
||||
$lotArray = null;
|
||||
if (isModEnabled('productbatch')) {
|
||||
$lotArray = $shipmentlinebatch->fetchAll($obj->expeditiondet_id);
|
||||
if (!is_array($lotArray)) {
|
||||
$error++;
|
||||
$this->errors[] = "Error ".$this->db->lasterror();
|
||||
if ($can_update_stock && empty($obj->iskit) && !empty($obj->incdec)) {
|
||||
$mouvS = new MouvementStock($this->db);
|
||||
// we do not log origin because it will be deleted
|
||||
$mouvS->origin = '';
|
||||
// get lot/serial
|
||||
$lotArray = null;
|
||||
if (isModEnabled('productbatch')) {
|
||||
$lotArray = $shipmentlinebatch->fetchAll($obj->expeditiondet_id);
|
||||
if (!is_array($lotArray)) {
|
||||
$error++;
|
||||
$this->errors[] = "Error ".$this->db->lasterror();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($lotArray)) {
|
||||
// no lot/serial
|
||||
// We increment stock of product (and sub-products)
|
||||
// We use warehouse selected for each line
|
||||
$result = $mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $obj->qty, 0, $langs->trans("ShipmentCanceledInDolibarr", $this->ref)); // Price is set to 0, because we don't want to see WAP changed
|
||||
if ($result < 0) {
|
||||
$error++;
|
||||
$this->errors = array_merge($this->errors, $mouvS->errors);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// We increment stock of batches
|
||||
// We use warehouse selected for each line
|
||||
foreach ($lotArray as $lot) {
|
||||
$result = $mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $lot->qty, 0, $langs->trans("ShipmentCanceledInDolibarr", $this->ref), $lot->eatby, $lot->sellby, (string) $lot->batch); // Price is set to 0, because we don't want to see WAP changed
|
||||
if (empty($lotArray)) {
|
||||
// no lot/serial
|
||||
// We increment stock of product (and sub-products)
|
||||
// We use warehouse selected for each line
|
||||
$result = $mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $obj->qty, 0, $langs->trans("ShipmentCanceledInDolibarr", $this->ref), '', '', '', '', 0, '', 0, 1); // Price is set to 0, because we don't want to see WAP changed
|
||||
if ($result < 0) {
|
||||
$error++;
|
||||
$this->errors = array_merge($this->errors, $mouvS->errors);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// We increment stock of batches
|
||||
// We use warehouse selected for each line
|
||||
foreach ($lotArray as $lot) {
|
||||
$result = $mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $lot->qty, 0, $langs->trans("ShipmentCanceledInDolibarr", $this->ref), $lot->eatby, $lot->sellby, (string) $lot->batch, '', 0, '', 0, 1); // Price is set to 0, because we don't want to see WAP changed
|
||||
if ($result < 0) {
|
||||
$error++;
|
||||
$this->errors = array_merge($this->errors, $mouvS->errors);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($error) {
|
||||
break; // break for loop in case of error
|
||||
}
|
||||
}
|
||||
if ($error) {
|
||||
break; // break for loop in case of error
|
||||
}
|
||||
|
||||
if (!$error) {
|
||||
// delete all children and batches of this shipment line
|
||||
$shipment_line = new ExpeditionLigne($this->db);
|
||||
$res = $shipment_line->fetch($line_id);
|
||||
if ($res > 0) {
|
||||
$result = $shipment_line->delete($user);
|
||||
if ($result < 0) {
|
||||
$error++;
|
||||
$this->errors[] = "Error ".$shipment_line->errorsToString();
|
||||
}
|
||||
} else {
|
||||
$error++;
|
||||
$this->errors[] = "Error ".$shipment_line->errorsToString();
|
||||
}
|
||||
}
|
||||
|
||||
if ($error) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$error++;
|
||||
|
|
@ -1401,87 +1606,67 @@ class Expedition extends CommonObject
|
|||
}
|
||||
}
|
||||
|
||||
// delete batch expedition line
|
||||
if (!$error && isModEnabled('productbatch')) {
|
||||
$shipmentlinebatch = new ExpeditionLineBatch($this->db);
|
||||
if ($shipmentlinebatch->deleteFromShipment($this->id) < 0) {
|
||||
$error++;
|
||||
$this->errors[] = "Error ".$this->db->lasterror();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!$error) {
|
||||
$sql = "DELETE FROM ".$this->db->prefix()."expeditiondet";
|
||||
$sql .= " WHERE fk_expedition = ".((int) $this->id);
|
||||
// Delete linked object
|
||||
$res = $this->deleteObjectLinked();
|
||||
if ($res < 0) {
|
||||
$error++;
|
||||
}
|
||||
|
||||
if ($this->db->query($sql)) {
|
||||
// Delete linked object
|
||||
$res = $this->deleteObjectLinked();
|
||||
if ($res < 0) {
|
||||
$error++;
|
||||
}
|
||||
// No delete expedition
|
||||
if (!$error) {
|
||||
$sql = "SELECT rowid FROM ".$this->db->prefix()."expedition";
|
||||
$sql .= " WHERE rowid = ".((int) $this->id);
|
||||
|
||||
// No delete expedition
|
||||
if (!$error) {
|
||||
$sql = "SELECT rowid FROM ".$this->db->prefix()."expedition";
|
||||
$sql .= " WHERE rowid = ".((int) $this->id);
|
||||
if ($this->db->query($sql)) {
|
||||
if (!empty($this->origin) && $this->origin_id > 0) {
|
||||
$this->fetch_origin();
|
||||
$origin_object = $this->origin_object;
|
||||
'@phan-var-force Facture|Commande $origin_object';
|
||||
if ($origin_object->status == Commande::STATUS_SHIPMENTONPROCESS) { // If order source of shipment is "shipment in progress"
|
||||
// Check if there is no more shipment. If not, we can move back status of order to "validated" instead of "shipment in progress"
|
||||
$origin_object->loadExpeditions();
|
||||
//var_dump($this->$origin->expeditions);exit;
|
||||
if (count($origin_object->expeditions) <= 0) {
|
||||
$origin_object->setStatut(Commande::STATUS_VALIDATED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->db->query($sql)) {
|
||||
if (!empty($this->origin) && $this->origin_id > 0) {
|
||||
$this->fetch_origin();
|
||||
$origin_object = $this->origin_object;
|
||||
'@phan-var-force Facture|Commande $origin_object';
|
||||
if ($origin_object->status == Commande::STATUS_SHIPMENTONPROCESS) { // If order source of shipment is "shipment in progress"
|
||||
// Check if there is no more shipment. If not, we can move back status of order to "validated" instead of "shipment in progress"
|
||||
$origin_object->loadExpeditions();
|
||||
//var_dump($this->$origin->expeditions);exit;
|
||||
if (count($origin_object->expeditions) <= 0) {
|
||||
$origin_object->setStatut(Commande::STATUS_VALIDATED);
|
||||
if (!$error) {
|
||||
$this->db->commit();
|
||||
|
||||
// We delete PDFs
|
||||
$ref = dol_sanitizeFileName($this->ref);
|
||||
if (!empty($conf->expedition->dir_output)) {
|
||||
$dir = $conf->expedition->dir_output.'/sending/'.$ref;
|
||||
$file = $dir.'/'.$ref.'.pdf';
|
||||
if (file_exists($file)) {
|
||||
if (!dol_delete_file($file)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (file_exists($dir)) {
|
||||
if (!dol_delete_dir_recursive($dir)) {
|
||||
$this->error = $langs->trans("ErrorCanNotDeleteDir", $dir);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$error) {
|
||||
$this->db->commit();
|
||||
|
||||
// We delete PDFs
|
||||
$ref = dol_sanitizeFileName($this->ref);
|
||||
if (!empty($conf->expedition->dir_output)) {
|
||||
$dir = $conf->expedition->dir_output.'/sending/'.$ref;
|
||||
$file = $dir.'/'.$ref.'.pdf';
|
||||
if (file_exists($file)) {
|
||||
if (!dol_delete_file($file)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (file_exists($dir)) {
|
||||
if (!dol_delete_dir_recursive($dir)) {
|
||||
$this->error = $langs->trans("ErrorCanNotDeleteDir", $dir);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
} else {
|
||||
$this->db->rollback();
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
} else {
|
||||
$this->error = $this->db->lasterror()." - sql=$sql";
|
||||
$this->db->rollback();
|
||||
return -3;
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
$this->error = $this->db->lasterror()." - sql=$sql";
|
||||
$this->db->rollback();
|
||||
return -2;
|
||||
}//*/
|
||||
return -3;
|
||||
}
|
||||
} else {
|
||||
$this->error = $this->db->lasterror()." - sql=$sql";
|
||||
$this->db->rollback();
|
||||
return -1;
|
||||
return -2;
|
||||
}
|
||||
} else {
|
||||
$this->db->rollback();
|
||||
|
|
@ -1530,9 +1715,10 @@ class Expedition extends CommonObject
|
|||
}
|
||||
|
||||
// Stock control
|
||||
if (!$error && isModEnabled('stock') &&
|
||||
$can_update_stock = isModEnabled('stock') &&
|
||||
((getDolGlobalString('STOCK_CALCULATE_ON_SHIPMENT') && $this->status > self::STATUS_DRAFT) ||
|
||||
(getDolGlobalString('STOCK_CALCULATE_ON_SHIPMENT_CLOSE') && $this->status == self::STATUS_CLOSED && $also_update_stock))) {
|
||||
(getDolGlobalString('STOCK_CALCULATE_ON_SHIPMENT_CLOSE') && $this->status == self::STATUS_CLOSED && $also_update_stock));
|
||||
if (!$error) {
|
||||
require_once DOL_DOCUMENT_ROOT."/product/stock/class/mouvementstock.class.php";
|
||||
|
||||
$langs->load("agenda");
|
||||
|
|
@ -1540,12 +1726,19 @@ class Expedition extends CommonObject
|
|||
// we try deletion of batch line even if module batch not enabled in case of the module were enabled and disabled previously
|
||||
$shipmentlinebatch = new ExpeditionLineBatch($this->db);
|
||||
|
||||
// Loop on each product line to add a stock movement
|
||||
$sql = "SELECT cd.fk_product, cd.subprice, ed.qty, ed.fk_entrepot, ed.rowid as expeditiondet_id";
|
||||
$sql .= " FROM ".$this->db->prefix()."commandedet as cd,";
|
||||
$sql .= " ".$this->db->prefix()."expeditiondet as ed";
|
||||
// Loop on each product line to add a stock movement (contain sub-products)
|
||||
$sql = "SELECT ";
|
||||
$sql .= " ed.fk_product";
|
||||
$sql .= ", ed.qty, ed.fk_entrepot, ed.rowid as expeditiondet_id";
|
||||
$sql .= ", SUM(".$this->db->ifsql("pa.rowid IS NOT NULL", "1", "0").") as iskit";
|
||||
$sql .= ", ".$this->db->ifsql("pai.incdec IS NULL", "1", "pai.incdec")." as incdec";
|
||||
$sql .= " FROM ".$this->db->prefix()."expeditiondet as ed";
|
||||
$sql .= " LEFT JOIN ".$this->db->prefix()."product_association as pa ON pa.fk_product_pere = ed.fk_product";
|
||||
$sql .= " LEFT JOIN ".$this->db->prefix()."expeditiondet as edp ON edp.rowid = ed.fk_parent";
|
||||
$sql .= " LEFT JOIN ".$this->db->prefix()."product_association as pai ON pai.fk_product_pere = edp.fk_product AND pai.fk_product_fils = ed.fk_product";
|
||||
$sql .= " WHERE ed.fk_expedition = ".((int) $this->id);
|
||||
$sql .= " AND cd.rowid = ed.fk_elementdet";
|
||||
$sql .= " GROUP BY ed.fk_product, ed.qty, ed.fk_entrepot, ed.rowid, pai.incdec";
|
||||
$sql .= $this->db->order("ed.rowid", "DESC");
|
||||
|
||||
dol_syslog(get_class($this)."::delete select details", LOG_DEBUG);
|
||||
$resql = $this->db->query($sql);
|
||||
|
|
@ -1554,41 +1747,64 @@ class Expedition extends CommonObject
|
|||
for ($i = 0; $i < $cpt; $i++) {
|
||||
dol_syslog(get_class($this)."::delete movement index ".$i);
|
||||
$obj = $this->db->fetch_object($resql);
|
||||
$line_id = (int) $obj->expeditiondet_id;
|
||||
|
||||
$mouvS = new MouvementStock($this->db);
|
||||
// we do not log origin because it will be deleted
|
||||
$mouvS->origin = '';
|
||||
// get lot/serial
|
||||
$lotArray = $shipmentlinebatch->fetchAll($obj->expeditiondet_id);
|
||||
if (!is_array($lotArray)) {
|
||||
$error++;
|
||||
$this->errors[] = "Error ".$this->db->lasterror();
|
||||
}
|
||||
if (empty($lotArray)) {
|
||||
// no lot/serial
|
||||
// We increment stock of product (and sub-products)
|
||||
// We use warehouse selected for each line
|
||||
$result = $mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $obj->qty, 0, $langs->trans("ShipmentDeletedInDolibarr", $this->ref)); // Price is set to 0, because we don't want to see WAP changed
|
||||
if ($result < 0) {
|
||||
if ($can_update_stock && empty($obj->iskit) && !empty($obj->incdec)) {
|
||||
$mouvS = new MouvementStock($this->db);
|
||||
// we do not log origin because it will be deleted
|
||||
$mouvS->origin = '';
|
||||
// get lot/serial
|
||||
$lotArray = $shipmentlinebatch->fetchAll($line_id);
|
||||
if (!is_array($lotArray)) {
|
||||
$error++;
|
||||
$this->errors = array_merge($this->errors, $mouvS->errors);
|
||||
break;
|
||||
$this->errors[] = "Error ".$this->db->lasterror();
|
||||
}
|
||||
} else {
|
||||
// We increment stock of batches
|
||||
// We use warehouse selected for each line
|
||||
foreach ($lotArray as $lot) {
|
||||
$result = $mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $lot->qty, 0, $langs->trans("ShipmentDeletedInDolibarr", $this->ref), $lot->eatby, $lot->sellby, (string) $lot->batch); // Price is set to 0, because we don't want to see WAP changed
|
||||
if (empty($lotArray)) {
|
||||
// no lot/serial
|
||||
// We increment stock of product (disable for sub-products : already in shipment lines)
|
||||
// We use warehouse selected for each line
|
||||
$result = $mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $obj->qty, 0, $langs->trans("ShipmentDeletedInDolibarr", $this->ref), '', '', '', '', 0, '', 0, 1); // Price is set to 0, because we don't want to see WAP changed
|
||||
if ($result < 0) {
|
||||
$error++;
|
||||
$this->errors = array_merge($this->errors, $mouvS->errors);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// We increment stock of batches
|
||||
// We use warehouse selected for each line
|
||||
foreach ($lotArray as $lot) {
|
||||
$result = $mouvS->reception($user, $obj->fk_product, $obj->fk_entrepot, $lot->qty, 0, $langs->trans("ShipmentDeletedInDolibarr", $this->ref), $lot->eatby, $lot->sellby, (string) $lot->batch, '', 0, '', 0, 1); // Price is set to 0, because we don't want to see WAP changed
|
||||
if ($result < 0) {
|
||||
$error++;
|
||||
$this->errors = array_merge($this->errors, $mouvS->errors);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($error) {
|
||||
break; // break for loop in case of error
|
||||
}
|
||||
}
|
||||
if ($error) {
|
||||
break; // break for loop in case of error
|
||||
}
|
||||
|
||||
if (!$error) {
|
||||
// delete all children and batches of this shipment line
|
||||
$shipment_line = new ExpeditionLigne($this->db);
|
||||
$res = $shipment_line->fetch($line_id);
|
||||
if ($res > 0) {
|
||||
$result = $shipment_line->delete($user);
|
||||
if ($result < 0) {
|
||||
$error++;
|
||||
$this->errors[] = "Error ".$shipment_line->errorsToString();
|
||||
}
|
||||
} else {
|
||||
$error++;
|
||||
$this->errors[] = "Error ".$shipment_line->errorsToString();
|
||||
}
|
||||
}
|
||||
|
||||
if ($error) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$error++;
|
||||
|
|
@ -1596,106 +1812,83 @@ class Expedition extends CommonObject
|
|||
}
|
||||
}
|
||||
|
||||
// delete batch expedition line
|
||||
if (!$error) {
|
||||
$shipmentlinebatch = new ExpeditionLineBatch($this->db);
|
||||
if ($shipmentlinebatch->deleteFromShipment($this->id) < 0) {
|
||||
// Delete linked object
|
||||
$res = $this->deleteObjectLinked();
|
||||
if ($res < 0) {
|
||||
$error++;
|
||||
$this->errors[] = "Error ".$this->db->lasterror();
|
||||
}
|
||||
}
|
||||
|
||||
if (!$error) {
|
||||
$main = $this->db->prefix().'expeditiondet';
|
||||
$ef = $main."_extrafields";
|
||||
$sqlef = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM $main WHERE fk_expedition = ".((int) $this->id).")";
|
||||
// delete extrafields
|
||||
$res = $this->deleteExtraFields();
|
||||
if ($res < 0) {
|
||||
$error++;
|
||||
}
|
||||
|
||||
$sql = "DELETE FROM ".$this->db->prefix()."expeditiondet";
|
||||
$sql .= " WHERE fk_expedition = ".((int) $this->id);
|
||||
|
||||
if ($this->db->query($sqlef) && $this->db->query($sql)) {
|
||||
// Delete linked object
|
||||
$res = $this->deleteObjectLinked();
|
||||
if (!$error) {
|
||||
// Delete linked contacts
|
||||
$res = $this->delete_linked_contact();
|
||||
if ($res < 0) {
|
||||
$error++;
|
||||
}
|
||||
}
|
||||
if (!$error) {
|
||||
$sql = "DELETE FROM ".$this->db->prefix()."expedition";
|
||||
$sql .= " WHERE rowid = ".((int) $this->id);
|
||||
|
||||
// delete extrafields
|
||||
$res = $this->deleteExtraFields();
|
||||
if ($res < 0) {
|
||||
$error++;
|
||||
}
|
||||
|
||||
if (!$error) {
|
||||
// Delete linked contacts
|
||||
$res = $this->delete_linked_contact();
|
||||
if ($res < 0) {
|
||||
$error++;
|
||||
if ($this->db->query($sql)) {
|
||||
if (!empty($this->origin) && $this->origin_id > 0) {
|
||||
$this->fetch_origin();
|
||||
$origin_object = $this->origin_object;
|
||||
'@phan-var-force Facture|Commande $origin_object';
|
||||
if ($origin_object->status == Commande::STATUS_SHIPMENTONPROCESS) { // If order source of shipment is "shipment in progress"
|
||||
// Check if there is no more shipment. If not, we can move back status of order to "validated" instead of "shipment in progress"
|
||||
$origin_object->loadExpeditions();
|
||||
//var_dump($this->$origin->expeditions);exit;
|
||||
if (count($origin_object->expeditions) <= 0) {
|
||||
$origin_object->setStatut(Commande::STATUS_VALIDATED);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$error) {
|
||||
$sql = "DELETE FROM ".$this->db->prefix()."expedition";
|
||||
$sql .= " WHERE rowid = ".((int) $this->id);
|
||||
|
||||
if ($this->db->query($sql)) {
|
||||
if (!empty($this->origin) && $this->origin_id > 0) {
|
||||
$this->fetch_origin();
|
||||
$origin_object = $this->origin_object;
|
||||
'@phan-var-force Facture|Commande $origin_object';
|
||||
if ($origin_object->status == Commande::STATUS_SHIPMENTONPROCESS) { // If order source of shipment is "shipment in progress"
|
||||
// Check if there is no more shipment. If not, we can move back status of order to "validated" instead of "shipment in progress"
|
||||
$origin_object->loadExpeditions();
|
||||
//var_dump($this->$origin->expeditions);exit;
|
||||
if (count($origin_object->expeditions) <= 0) {
|
||||
$origin_object->setStatut(Commande::STATUS_VALIDATED);
|
||||
if (!$error) {
|
||||
$this->db->commit();
|
||||
|
||||
// Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
|
||||
$this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
|
||||
$this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
|
||||
|
||||
// We delete PDFs
|
||||
$ref = dol_sanitizeFileName($this->ref);
|
||||
if (!empty($conf->expedition->dir_output)) {
|
||||
$dir = $conf->expedition->dir_output . '/sending/' . $ref;
|
||||
$file = $dir . '/' . $ref . '.pdf';
|
||||
if (file_exists($file)) {
|
||||
if (!dol_delete_file($file)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (file_exists($dir)) {
|
||||
if (!dol_delete_dir_recursive($dir)) {
|
||||
$this->error = $langs->trans("ErrorCanNotDeleteDir", $dir);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$error) {
|
||||
$this->db->commit();
|
||||
|
||||
// Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
|
||||
$this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
|
||||
$this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
|
||||
|
||||
// We delete PDFs
|
||||
$ref = dol_sanitizeFileName($this->ref);
|
||||
if (!empty($conf->expedition->dir_output)) {
|
||||
$dir = $conf->expedition->dir_output.'/sending/'.$ref;
|
||||
$file = $dir.'/'.$ref.'.pdf';
|
||||
if (file_exists($file)) {
|
||||
if (!dol_delete_file($file)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (file_exists($dir)) {
|
||||
if (!dol_delete_dir_recursive($dir)) {
|
||||
$this->error = $langs->trans("ErrorCanNotDeleteDir", $dir);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
} else {
|
||||
$this->db->rollback();
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
} else {
|
||||
$this->error = $this->db->lasterror()." - sql=$sql";
|
||||
$this->db->rollback();
|
||||
return -3;
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
$this->error = $this->db->lasterror()." - sql=$sql";
|
||||
$this->db->rollback();
|
||||
return -2;
|
||||
return -3;
|
||||
}
|
||||
} else {
|
||||
$this->error = $this->db->lasterror()." - sql=$sql";
|
||||
$this->db->rollback();
|
||||
return -1;
|
||||
return -2;
|
||||
}
|
||||
} else {
|
||||
$this->db->rollback();
|
||||
|
|
@ -1890,6 +2083,33 @@ class Expedition extends CommonObject
|
|||
}
|
||||
}
|
||||
|
||||
// virtual product : find all children stock (group by product id and warehouse id)
|
||||
if (getDolGlobalInt('PRODUIT_SOUSPRODUITS')) {
|
||||
$detail_children = array(); // detail by product : array of [warehouse_id => total_qty]
|
||||
$line_child_list = array();
|
||||
$res = $line->findAllChild($line->id, $line_child_list, 1);
|
||||
if ($res > 0) {
|
||||
foreach ($line_child_list as $child_line) {
|
||||
foreach ($child_line as $child_obj) {
|
||||
$child_product_id = (int) $child_obj->fk_product;
|
||||
$child_warehouse_id = (int) $child_obj->fk_warehouse;
|
||||
|
||||
if ($child_warehouse_id > 0) {
|
||||
// child quantities group by warehouses
|
||||
if (!isset($detail_children[$child_product_id])) {
|
||||
$detail_children[$child_product_id] = array();
|
||||
}
|
||||
if (!isset($detail_children[$child_product_id][$child_warehouse_id])) {
|
||||
$detail_children[$child_product_id][$child_warehouse_id] = 0;
|
||||
}
|
||||
$detail_children[$child_product_id][$child_warehouse_id] += $child_obj->qty;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$line->detail_children = $detail_children;
|
||||
}
|
||||
|
||||
$line->fetch_optionals();
|
||||
|
||||
if ($originline != $obj->fk_elementdet) {
|
||||
|
|
@ -2479,17 +2699,18 @@ class Expedition extends CommonObject
|
|||
$langs->load("agenda");
|
||||
|
||||
// Loop on each product line to add a stock movement
|
||||
$sql = "SELECT cd.fk_product, cd.subprice,";
|
||||
$sql .= " ed.rowid, ed.qty, ed.fk_entrepot,";
|
||||
$sql .= " e.ref,";
|
||||
$sql .= " edb.rowid as edbrowid, edb.eatby, edb.sellby, edb.batch, edb.qty as edbqty, edb.fk_origin_stock,";
|
||||
$sql .= " cd.rowid as cdid, ed.rowid as edid";
|
||||
$sql .= " FROM " . $this->db->prefix() . "commandedet as cd,";
|
||||
$sql .= " " . $this->db->prefix() . "expeditiondet as ed";
|
||||
$sql = "SELECT";
|
||||
$sql .= " ed.rowid as edid, ed.fk_product, ed.qty, ed.fk_entrepot";
|
||||
$sql .= ", cd.rowid as cdid";
|
||||
$sql .= ", cd.subprice";
|
||||
$sql .= ", edb.rowid as edbrowid, edb.eatby, edb.sellby, edb.batch, edb.qty as edbqty, edb.fk_origin_stock";
|
||||
$sql .= ", e.ref";
|
||||
$sql .= " FROM " . $this->db->prefix() . "expeditiondet as ed";
|
||||
$sql .= " LEFT JOIN " . $this->db->prefix() . "commandedet as cd ON cd.rowid = ed.fk_elementdet";
|
||||
$sql .= " LEFT JOIN " . $this->db->prefix() . "expeditiondet_batch as edb on edb.fk_expeditiondet = ed.rowid";
|
||||
$sql .= " INNER JOIN " . $this->db->prefix() . "expedition as e ON ed.fk_expedition = e.rowid";
|
||||
$sql .= " WHERE ed.fk_expedition = " . ((int) $this->id);
|
||||
$sql .= " AND cd.rowid = ed.fk_elementdet";
|
||||
//$sql .= " AND cd.rowid = ed.fk_elementdet";
|
||||
|
||||
dol_syslog(get_class($this) . "::valid select details", LOG_DEBUG);
|
||||
$resql = $this->db->query($sql);
|
||||
|
|
@ -2505,7 +2726,7 @@ class Expedition extends CommonObject
|
|||
if ($qty <= 0 || ($qty < 0 && !getDolGlobalInt('SHIPMENT_ALLOW_NEGATIVE_QTY'))) {
|
||||
continue;
|
||||
}
|
||||
dol_syslog(get_class($this) . "::valid movement index " . $i . " ed.rowid=" . $obj->rowid . " edb.rowid=" . $obj->edbrowid);
|
||||
dol_syslog(get_class($this) . "::valid movement index " . $i . " ed.rowid=" . $obj->edid . " edb.rowid=" . $obj->edbrowid);
|
||||
|
||||
$mouvS = new MouvementStock($this->db);
|
||||
$mouvS->origin = &$this;
|
||||
|
|
|
|||
|
|
@ -91,6 +91,11 @@ class ExpeditionLigne extends CommonObjectLine
|
|||
*/
|
||||
public $origin_line_id;
|
||||
|
||||
/**
|
||||
* @var int Id of parent line for children of virtual product
|
||||
*/
|
||||
public $fk_parent;
|
||||
|
||||
/**
|
||||
* @var string Type of object the fk_element refers to. Example: 'order'.
|
||||
*/
|
||||
|
|
@ -137,6 +142,12 @@ class ExpeditionLigne extends CommonObjectLine
|
|||
*/
|
||||
public $detail_batch;
|
||||
|
||||
/**
|
||||
* Virtual products : array of total of quantities group product id and warehouse id ([id_product][id_warehouse] -> qty (int|float))
|
||||
* @var array<int, array<int, int|float>>
|
||||
*/
|
||||
public $detail_children;
|
||||
|
||||
/** detail of warehouses and qty
|
||||
* We can use this to know warehouse when there is no lot.
|
||||
* @var stdClass[]
|
||||
|
|
@ -361,7 +372,10 @@ class ExpeditionLigne extends CommonObjectLine
|
|||
$error = 0;
|
||||
|
||||
// Check parameters
|
||||
if (empty($this->fk_expedition) || empty($this->fk_elementdet) || !is_numeric($this->qty)) {
|
||||
if (empty($this->fk_expedition)
|
||||
|| empty($this->fk_product) // product id is mandatory
|
||||
|| (empty($this->fk_elementdet) && empty($this->fk_parent)) // at least origin line id of parent line id is set
|
||||
|| !is_numeric($this->qty)) {
|
||||
$this->error = 'ErrorMandatoryParametersNotProvided';
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -383,13 +397,17 @@ class ExpeditionLigne extends CommonObjectLine
|
|||
$sql .= "fk_expedition";
|
||||
$sql .= ", fk_entrepot";
|
||||
$sql .= ", fk_elementdet";
|
||||
$sql .= ", fk_parent";
|
||||
$sql .= ", fk_product";
|
||||
$sql .= ", element_type";
|
||||
$sql .= ", qty";
|
||||
$sql .= ", rang";
|
||||
$sql .= ") VALUES (";
|
||||
$sql .= $this->fk_expedition;
|
||||
$sql .= ", ".(empty($this->entrepot_id) ? 'NULL' : $this->entrepot_id);
|
||||
$sql .= ", ".((int) $this->fk_elementdet);
|
||||
$sql .= ", ".(empty($this->fk_elementdet) ? 'NULL' : $this->fk_elementdet);
|
||||
$sql .= ", ".(empty($this->fk_parent) ? 'NULL' : $this->fk_parent);
|
||||
$sql .= ", ".(empty($this->fk_product) ? 'NULL' : $this->fk_product);
|
||||
$sql .= ", '".(empty($this->element_type) ? 'order' : $this->db->escape($this->element_type))."'";
|
||||
$sql .= ", ".price2num($this->qty, 'MS');
|
||||
$sql .= ", ".((int) $ranktouse);
|
||||
|
|
@ -418,7 +436,7 @@ class ExpeditionLigne extends CommonObjectLine
|
|||
|
||||
if ($error) {
|
||||
foreach ($this->errors as $errmsg) {
|
||||
dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
|
||||
dol_syslog(__METHOD__.' '.$errmsg, LOG_ERR);
|
||||
$this->error .= ($this->error ? ', '.$errmsg : $errmsg);
|
||||
}
|
||||
}
|
||||
|
|
@ -435,6 +453,69 @@ class ExpeditionLigne extends CommonObjectLine
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all children
|
||||
*
|
||||
* @param int $line_id Line id
|
||||
* @param stdClass[] $list List of sub-lines for a virtual product line (array of object with attributes : rowid, fk_product, fk_parent, qty, fk_warehouse, batch, eatby, sellby, iskit, incdec)
|
||||
* @param int $mode [=0] array of lines ids, 1 array of line object for dispatcher
|
||||
* @return int Return integer <0 if KO else >0 if OK
|
||||
*/
|
||||
public function findAllChild($line_id, &$list = array(), $mode = 0)
|
||||
{
|
||||
if ($line_id > 0) {
|
||||
// find all child
|
||||
$sql = "SELECT ed.rowid as child_line_id";
|
||||
if ($mode == 1) {
|
||||
$sql .= ", ed.fk_product";
|
||||
$sql .= ", ed.fk_parent";
|
||||
$sql .= ", " . $this->db->ifsql('eb.rowid IS NULL', 'ed.qty', 'eb.qty') . " as qty";
|
||||
$sql .= ", " . $this->db->ifsql('eb.rowid IS NULL', 'ed.fk_entrepot', 'eb.fk_warehouse') . " as fk_warehouse";
|
||||
$sql .= ", eb.batch, eb.eatby, eb.sellby";
|
||||
}
|
||||
$sql .= " FROM " . $this->db->prefix() . $this->table_element . " as ed";
|
||||
$sql .= " LEFT JOIN " . $this->db->prefix() . "expeditiondet_batch as eb ON eb.fk_expeditiondet = " . ((int) $line_id);
|
||||
$sql .= " WHERE ed.fk_parent = " . ((int) $line_id);
|
||||
$sql .= $this->db->order('ed.fk_product,ed.rowid', 'ASC,ASC');
|
||||
|
||||
$resql = $this->db->query($sql);
|
||||
if ($resql) {
|
||||
while ($obj = $this->db->fetch_object($resql)) {
|
||||
$child_line_id = (int) $obj->child_line_id;
|
||||
if (!isset($list[$line_id])) {
|
||||
$list[$line_id] = array();
|
||||
}
|
||||
|
||||
if ($mode == 0) {
|
||||
$list[$line_id][] = $child_line_id;
|
||||
} elseif ($mode == 1) {
|
||||
$line_obj = new stdClass();
|
||||
$line_obj->rowid = $child_line_id;
|
||||
$line_obj->fk_product = $obj->fk_product;
|
||||
$line_obj->fk_parent = $obj->fk_parent;
|
||||
$line_obj->qty = $obj->qty;
|
||||
$line_obj->fk_warehouse = $obj->fk_warehouse;
|
||||
$line_obj->batch = $obj->batch;
|
||||
$line_obj->eatby = $obj->eatby;
|
||||
$line_obj->sellby = $obj->sellby;
|
||||
$line_obj->iskit = 0;
|
||||
$line_obj->incdec = 0;
|
||||
$list[$line_id][] = $line_obj;
|
||||
}
|
||||
|
||||
$this->findAllChild($child_line_id, $list, $mode);
|
||||
}
|
||||
$this->db->free($resql);
|
||||
} else {
|
||||
$this->error = $this->db->lasterror();
|
||||
$this->errors[] = $this->error;
|
||||
dol_syslog(__METHOD__.' '.$this->error, LOG_ERR);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete shipment line.
|
||||
*
|
||||
|
|
@ -448,41 +529,82 @@ class ExpeditionLigne extends CommonObjectLine
|
|||
|
||||
$this->db->begin();
|
||||
|
||||
// delete batch expedition line
|
||||
if (isModEnabled('productbatch')) {
|
||||
$sql = "DELETE FROM ".MAIN_DB_PREFIX."expeditiondet_batch";
|
||||
$sql .= " WHERE fk_expeditiondet = ".((int) $this->id);
|
||||
// virtual products : delete all children and batch
|
||||
if (getDolGlobalInt('PRODUIT_SOUSPRODUITS') && !($this->fk_parent > 0)) {
|
||||
// find all children
|
||||
$line_id_list = array();
|
||||
$result = $this->findAllChild($this->id, $line_id_list);
|
||||
if ($result) {
|
||||
$child_line_id_list = array_reverse($line_id_list, true);
|
||||
foreach ($child_line_id_list as $child_line_id_arr) {
|
||||
foreach ($child_line_id_arr as $child_line_id) {
|
||||
// delete batch expedition line
|
||||
if (isModEnabled('productbatch')) {
|
||||
$sql = "DELETE FROM " . $this->db->prefix() . "expeditiondet_batch";
|
||||
$sql .= " WHERE fk_expeditiondet = " . ((int) $child_line_id);
|
||||
if (!$this->db->query($sql)) {
|
||||
$error++;
|
||||
$this->errors[] = $this->db->lasterror() . " - sql=$sql";
|
||||
}
|
||||
}
|
||||
|
||||
if (!$this->db->query($sql)) {
|
||||
$this->errors[] = $this->db->lasterror()." - sql=$sql";
|
||||
$sql = "DELETE FROM " . $this->db->prefix() . "expeditiondet";
|
||||
$sql .= " WHERE rowid = " . ((int) $child_line_id);
|
||||
if (!$this->db->query($sql)) {
|
||||
$error++;
|
||||
$this->errors[] = $this->db->lasterror() . " - sql=$sql";
|
||||
}
|
||||
|
||||
if ($error) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($error) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$error++;
|
||||
}
|
||||
}
|
||||
|
||||
$sql = "DELETE FROM ".MAIN_DB_PREFIX."expeditiondet";
|
||||
$sql .= " WHERE rowid = ".((int) $this->id);
|
||||
if (!$error) {
|
||||
// delete batch expedition line
|
||||
if (isModEnabled('productbatch')) {
|
||||
$sql = "DELETE FROM ".$this->db->prefix()."expeditiondet_batch";
|
||||
$sql .= " WHERE fk_expeditiondet = ".((int) $this->id);
|
||||
|
||||
if (!$error && $this->db->query($sql)) {
|
||||
// Remove extrafields
|
||||
if (!$error) {
|
||||
$result = $this->deleteExtraFields();
|
||||
if ($result < 0) {
|
||||
$this->errors[] = $this->error;
|
||||
if (!$this->db->query($sql)) {
|
||||
$this->errors[] = $this->db->lasterror()." - sql=$sql";
|
||||
$error++;
|
||||
}
|
||||
}
|
||||
if (!$error && !$notrigger) {
|
||||
// Call trigger
|
||||
$result = $this->call_trigger('LINESHIPPING_DELETE', $user);
|
||||
if ($result < 0) {
|
||||
$this->errors[] = $this->error;
|
||||
$error++;
|
||||
|
||||
$sql = "DELETE FROM ".$this->db->prefix()."expeditiondet";
|
||||
$sql .= " WHERE rowid = ".((int) $this->id);
|
||||
|
||||
if (!$error && $this->db->query($sql)) {
|
||||
// Remove extrafields
|
||||
if (!$error) {
|
||||
$result = $this->deleteExtraFields();
|
||||
if ($result < 0) {
|
||||
$this->errors[] = $this->error;
|
||||
$error++;
|
||||
}
|
||||
}
|
||||
// End call triggers
|
||||
if (!$error && !$notrigger) {
|
||||
// Call trigger
|
||||
$result = $this->call_trigger('LINESHIPPING_DELETE', $user);
|
||||
if ($result < 0) {
|
||||
$this->errors[] = $this->error;
|
||||
$error++;
|
||||
}
|
||||
// End call triggers
|
||||
}
|
||||
} else {
|
||||
$this->errors[] = $this->db->lasterror()." - sql=$sql";
|
||||
$error++;
|
||||
}
|
||||
} else {
|
||||
$this->errors[] = $this->db->lasterror()." - sql=$sql";
|
||||
$error++;
|
||||
}
|
||||
|
||||
if (!$error) {
|
||||
|
|
|
|||
|
|
@ -143,35 +143,36 @@ if (empty($reshook)) {
|
|||
foreach ($_POST as $key => $value) {
|
||||
// without batch module enabled
|
||||
$reg = array();
|
||||
if (preg_match('/^product_.*([0-9]+)_([0-9]+)$/i', $key, $reg)) {
|
||||
if (preg_match('/^(?:product|productbatch)([0-9]+)_([0-9]+)_([0-9]+)$/i', $key, $reg)) {
|
||||
$pos++;
|
||||
$modebatch = null;
|
||||
if (preg_match('/^product_([0-9]+)_([0-9]+)$/i', $key, $reg)) {
|
||||
if (preg_match('/^product([0-9]+)_([0-9]+)_([0-9]+)$/i', $key, $reg)) {
|
||||
$modebatch = "barcode";
|
||||
} elseif (preg_match('/^product_batch_([0-9]+)_([0-9]+)$/i', $key, $reg)) { // With batchmode enabled
|
||||
} elseif (preg_match('/^productbatch([0-9]+)_([0-9]+)_([0-9]+)$/i', $key, $reg)) { // With batchmode enabled
|
||||
$modebatch = "batch";
|
||||
}
|
||||
|
||||
$numline = $pos;
|
||||
$dispatch_line_suffix = $reg[1].'_'.$reg[2].'_'.$reg[3];
|
||||
if ($modebatch == "barcode") {
|
||||
$prod = "product_".$reg[1].'_'.$reg[2];
|
||||
$prod = "product".$dispatch_line_suffix;
|
||||
} else {
|
||||
$prod = 'product_batch_'.$reg[1].'_'.$reg[2];
|
||||
$prod = 'productbatch'.$dispatch_line_suffix;
|
||||
}
|
||||
$qty = "qty_".$reg[1].'_'.$reg[2];
|
||||
$ent = "entrepot_".$reg[1].'_'.$reg[2];
|
||||
$fk_commandedet = "fk_commandedet_".$reg[1].'_'.$reg[2];
|
||||
$idline = GETPOSTINT("idline_".$reg[1].'_'.$reg[2]);
|
||||
$qty = "qty".$dispatch_line_suffix;
|
||||
$ent = "entrepot".$dispatch_line_suffix;
|
||||
$fk_commandedet = "fk_commandedet".$dispatch_line_suffix;
|
||||
$idline = GETPOSTINT("idline".$dispatch_line_suffix);
|
||||
$warehouse_id = GETPOSTINT($ent);
|
||||
$prod_id = GETPOSTINT($prod);
|
||||
//$pu = "pu_".$reg[1].'_'.$reg[2]; // This is unit price including discount
|
||||
//$pu = "pu".$dispatch_line_suffix; // This is unit price including discount
|
||||
$lot = '';
|
||||
$dDLUO = '';
|
||||
$dDLC = '';
|
||||
if ($modebatch == "batch") { //TODO: Make impossible to input non existing batch code
|
||||
$lot = GETPOST('lot_number_'.$reg[1].'_'.$reg[2]);
|
||||
$dDLUO = dol_mktime(12, 0, 0, GETPOSTINT('dluo_'.$reg[1].'_'.$reg[2].'month'), GETPOSTINT('dluo_'.$reg[1].'_'.$reg[2].'day'), GETPOSTINT('dluo_'.$reg[1].'_'.$reg[2].'year'));
|
||||
$dDLC = dol_mktime(12, 0, 0, GETPOSTINT('dlc_'.$reg[1].'_'.$reg[2].'month'), GETPOSTINT('dlc_'.$reg[1].'_'.$reg[2].'day'), GETPOSTINT('dlc_'.$reg[1].'_'.$reg[2].'year'));
|
||||
$lot = GETPOST('lot_number'.$dispatch_line_suffix);
|
||||
$dDLUO = dol_mktime(12, 0, 0, GETPOSTINT('dluo'.$dispatch_line_suffix.'month'), GETPOSTINT('dluo'.$dispatch_line_suffix.'day'), GETPOSTINT('dluo'.$dispatch_line_suffix.'year'));
|
||||
$dDLC = dol_mktime(12, 0, 0, GETPOSTINT('dlc'.$dispatch_line_suffix.'month'), GETPOSTINT('dlc'.$dispatch_line_suffix.'day'), GETPOSTINT('dlc'.$dispatch_line_suffix.'year'));
|
||||
}
|
||||
|
||||
$newqty = GETPOSTFLOAT($qty, 'MS');
|
||||
|
|
@ -241,13 +242,12 @@ if (empty($reshook)) {
|
|||
|
||||
if (!$error && $modebatch == "batch") {
|
||||
if ($newqty > 0) {
|
||||
$suffixkeyfordate = preg_replace('/^product_batch/', '', $key);
|
||||
$sellby = dol_mktime(0, 0, 0, GETPOSTINT('dlc'.$suffixkeyfordate.'month'), GETPOSTINT('dlc'.$suffixkeyfordate.'day'), GETPOSTINT('dlc'.$suffixkeyfordate.'year'), '');
|
||||
$eatby = dol_mktime(0, 0, 0, GETPOSTINT('dluo'.$suffixkeyfordate.'month'), GETPOSTINT('dluo'.$suffixkeyfordate.'day'), GETPOSTINT('dluo'.$suffixkeyfordate.'year'));
|
||||
$suffixkeyfordate = preg_replace('/^productbatch/', '', $key);
|
||||
$sellby = dol_mktime(12, 0, 0, GETPOSTINT('dlc'.$suffixkeyfordate.'month'), GETPOSTINT('dlc'.$suffixkeyfordate.'day'), GETPOSTINT('dlc'.$suffixkeyfordate.'year'), '');
|
||||
$eatby = dol_mktime(12, 0, 0, GETPOSTINT('dluo'.$suffixkeyfordate.'month'), GETPOSTINT('dluo'.$suffixkeyfordate.'day'), GETPOSTINT('dluo'.$suffixkeyfordate.'year'));
|
||||
|
||||
$sqlsearchdet = "SELECT rowid FROM ".$db->prefix().$expeditionlinebatch->table_element;
|
||||
$sqlsearchdet .= " WHERE fk_expeditiondet = ".((int) $idline);
|
||||
$sqlsearchdet .= " AND batch = '".$db->escape($lot)."'";
|
||||
$resqlsearchdet = $db->query($sqlsearchdet);
|
||||
|
||||
$objsearchdet = null;
|
||||
|
|
@ -259,10 +259,11 @@ if (empty($reshook)) {
|
|||
|
||||
if ($objsearchdet) {
|
||||
$sql = "UPDATE ".$db->prefix().$expeditionlinebatch->table_element." SET";
|
||||
$sql .= " eatby = ".($eatby ? "'".$db->idate($eatby)."'" : "null");
|
||||
$sql .= " , sellby = ".($sellby ? "'".$db->idate($sellby)."'" : "null");
|
||||
$sql .= " , qty = ".((float) $newqty);
|
||||
$sql .= " , fk_warehouse = ".((int) $warehouse_id);
|
||||
$sql .= " batch = '".$db->escape($lot)."'";
|
||||
$sql .= ", eatby = ".($eatby ? "'".$db->idate($eatby)."'" : "null");
|
||||
$sql .= ", sellby = ".($sellby ? "'".$db->idate($sellby)."'" : "null");
|
||||
$sql .= ", qty = ".((float) $newqty);
|
||||
$sql .= ", fk_warehouse = ".((int) $warehouse_id);
|
||||
$sql .= " WHERE rowid = ".((int) $objsearchdet->rowid);
|
||||
} else {
|
||||
$sql = "INSERT INTO ".$db->prefix().$expeditionlinebatch->table_element." (";
|
||||
|
|
@ -271,7 +272,7 @@ if (empty($reshook)) {
|
|||
$sql .= " '".$db->escape($lot)."', ".((float) $newqty).", 0, ".((int) $warehouse_id).")";
|
||||
}
|
||||
} else {
|
||||
$sql = " DELETE FROM ".$db->prefix().$expeditionlinebatch->table_element;
|
||||
$sql = "DELETE FROM ".$db->prefix().$expeditionlinebatch->table_element;
|
||||
$sql .= " WHERE fk_expeditiondet = ".((int) $idline);
|
||||
$sql .= " AND batch = '".$db->escape($lot)."'";
|
||||
}
|
||||
|
|
@ -286,7 +287,11 @@ if (empty($reshook)) {
|
|||
} else {
|
||||
$expeditiondispatch->fk_expedition = $object->id;
|
||||
$expeditiondispatch->entrepot_id = GETPOSTINT($ent);
|
||||
$expeditiondispatch->fk_elementdet = GETPOSTINT($fk_commandedet);
|
||||
$expeditiondispatch->fk_parent = GETPOSTINT('fk_parent'.$dispatch_line_suffix);
|
||||
$expeditiondispatch->fk_product = $prod_id;
|
||||
if (!($expeditiondispatch->fk_parent > 0)) {
|
||||
$expeditiondispatch->fk_elementdet = GETPOSTINT($fk_commandedet);
|
||||
}
|
||||
$expeditiondispatch->qty = $newqty;
|
||||
|
||||
if ($newqty > 0) {
|
||||
|
|
@ -297,8 +302,8 @@ if (empty($reshook)) {
|
|||
}
|
||||
|
||||
if ($modebatch == "batch" && !$error) {
|
||||
$expeditionlinebatch->sellby = $dDLUO;
|
||||
$expeditionlinebatch->eatby = $dDLC;
|
||||
$expeditionlinebatch->sellby = $dDLC; // DLC is sellByDate
|
||||
$expeditionlinebatch->eatby = $dDLUO; // DLUO is eatByDate
|
||||
$expeditionlinebatch->batch = $lot;
|
||||
$expeditionlinebatch->qty = $newqty;
|
||||
$expeditionlinebatch->fk_origin_stock = 0;
|
||||
|
|
@ -793,7 +798,7 @@ if ($object->id > 0 || !empty($object->ref)) {
|
|||
$sql = "SELECT ed.rowid";
|
||||
$sql .= ", cd.fk_product";
|
||||
$sql .= ", ".$db->ifsql('eb.rowid IS NULL', 'ed.qty', 'eb.qty')." as qty";
|
||||
$sql .= ", ed.fk_entrepot";
|
||||
$sql .= ", ".$db->ifsql('eb.rowid IS NULL OR eb.fk_warehouse IS NULL', 'ed.fk_entrepot', 'eb.fk_warehouse')." as fk_warehouse";
|
||||
$sql .= ", eb.batch, eb.eatby, eb.sellby";
|
||||
$sql .= " FROM ".$db->prefix()."expeditiondet as ed";
|
||||
$sql .= " LEFT JOIN ".$db->prefix()."expeditiondet_batch as eb on ed.rowid = eb.fk_expeditiondet";
|
||||
|
|
@ -806,167 +811,324 @@ if ($object->id > 0 || !empty($object->ref)) {
|
|||
$j = 0;
|
||||
if ($resultsql) {
|
||||
$numd = $db->num_rows($resultsql);
|
||||
while ($obj_exp = $db->fetch_object($resultsql)) {
|
||||
$suffix = "_" . $j . "_" . $i;
|
||||
|
||||
while ($j < $numd) {
|
||||
$suffix = "_".$j."_".$i;
|
||||
$objd = $db->fetch_object($resultsql);
|
||||
$productChildrenNb = 0;
|
||||
$expedition_line_child_list = array();
|
||||
if (getDolGlobalInt('PRODUIT_SOUSPRODUITS')) {
|
||||
// virtual product : find all children
|
||||
$productChildrenNb = $tmpproduct->hasFatherOrChild(1);
|
||||
if ($productChildrenNb > 0) {
|
||||
$line_id_list = array();
|
||||
|
||||
if ($is_mod_batch_enabled && (!empty($objd->batch) || (is_null($objd->batch) && $tmpproduct->status_batch > 0))) {
|
||||
$type = 'batch';
|
||||
// load all child as object line
|
||||
$expeditionLine = new ExpeditionLigne($db);
|
||||
$result = $expeditionLine->findAllChild($obj_exp->rowid, $line_id_list, 1);
|
||||
if ($result > 0) {
|
||||
$child_level = 1;
|
||||
foreach ($line_id_list as $line_id_arr) {
|
||||
foreach ($line_id_arr as $line_obj) {
|
||||
$child_product_id = (int) $line_obj->fk_product;
|
||||
if (empty($conf->cache['product'][$child_product_id])) {
|
||||
$child_product = new Product($db);
|
||||
$child_product->fetch($child_product_id);
|
||||
$conf->cache['product'][$child_product_id] = $child_product;
|
||||
} else {
|
||||
$child_product = $conf->cache['product'][$child_product_id];
|
||||
}
|
||||
|
||||
// Enable hooks to append additional columns
|
||||
$parameters = array(
|
||||
// allows hook to distinguish between the rows with information and the rows with dispatch form input
|
||||
'is_information_row' => true,
|
||||
'j' => $j,
|
||||
'suffix' => $suffix,
|
||||
'objd' => $objd,
|
||||
);
|
||||
$reshook = $hookmanager->executeHooks(
|
||||
'printFieldListValue',
|
||||
$parameters,
|
||||
$object,
|
||||
$action
|
||||
);
|
||||
if ($reshook < 0) {
|
||||
setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
|
||||
// sub-product is a batch and get selected batch from database or all batches for selected warehouse
|
||||
$batch_list = array();
|
||||
if ($is_mod_batch_enabled && $child_product->hasbatch()) {
|
||||
// search if batch is not exist in shipment lines
|
||||
$sql_line_batch_search = "SELECT eb.rowid, eb.qty, eb.batch, eb.sellby, eb.eatby";
|
||||
$sql_line_batch_search .= " FROM ".$db->prefix()."expeditiondet_batch as eb";
|
||||
$sql_line_batch_search .= " WHERE eb.fk_expeditiondet = ".((int) $line_obj->rowid);
|
||||
$res_line_batch_search = $db->query($sql_line_batch_search);
|
||||
if ($res_line_batch_search) {
|
||||
while ($obj_batch = $db->fetch_object($res_line_batch_search)) {
|
||||
// set the selected bath by default
|
||||
if ($obj_batch->batch != '') {
|
||||
$line_obj->batch = $obj_batch->batch;
|
||||
}
|
||||
$obj_batch->eatby = dol_print_date($obj_batch->eatby, 'day');
|
||||
$obj_batch->sellby = dol_print_date($obj_batch->sellby, 'day');
|
||||
$batch_list[] = $obj_batch;
|
||||
}
|
||||
$db->free($res_line_batch_search);
|
||||
}
|
||||
|
||||
// no batch found for this sub-product so retrieve all batch numbers for this sub-product id and warehouse id
|
||||
if (empty($batch_list)) {
|
||||
$batch_sort_field_arr = array();
|
||||
$batch_sort_order_arr = array();
|
||||
if ($is_sell_by_enabled) {
|
||||
$batch_sort_field_arr[] = 'pl.sellby'; // order by sell by (DLC)
|
||||
$batch_sort_order_arr[] = 'ASC';
|
||||
}
|
||||
if ($is_eat_by_enabled) {
|
||||
$batch_sort_field_arr[] = 'pl.eatby'; // order by eat by (DLUO)
|
||||
$batch_sort_order_arr[] = 'ASC';
|
||||
}
|
||||
$batch_sort_field_arr[] = 'pb.qty'; // order by qty
|
||||
$batch_sort_order_arr[] = 'ASC';
|
||||
$batch_sort_field_arr[] = 'pl.rowid'; // order by rowid
|
||||
$batch_sort_order_arr[] = 'ASC';
|
||||
$product_batch = new Productbatch($db);
|
||||
$product_batch_result = $product_batch->findAllForProduct($child_product_id, $line_obj->fk_warehouse, (getDolGlobalInt('STOCK_DISALLOW_NEGATIVE_TRANSFER') ? 0 : null), implode(',', $batch_sort_field_arr), implode(',', $batch_sort_order_arr));
|
||||
if (is_array($product_batch_result)) {
|
||||
foreach ($product_batch_result as $batch_current) {
|
||||
$batch_current->eatby = dol_print_date($batch_current->eatby, 'day');
|
||||
$batch_current->sellby = dol_print_date($batch_current->sellby, 'day');
|
||||
$batch_list[] = $batch_current;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$line_obj->batch_list = $batch_list;
|
||||
|
||||
// determine if line is virtual product and stock is managed
|
||||
$line_obj->iskit = 0;
|
||||
if ($child_product->stockable_product == Product::ENABLED_STOCK) {
|
||||
$can_manage_stock = 1;
|
||||
} else {
|
||||
$can_manage_stock = 0; // the value of "incdec" can't be modified
|
||||
}
|
||||
$line_obj->incdec = $can_manage_stock; // set value by default before this request
|
||||
$sql_child = "SELECT ";
|
||||
$sql_child .= " SUM(".$db->ifsql("pa.rowid IS NOT NULL", "1", "0").") as iskit";
|
||||
$sql_child .= ", ".$db->ifsql("pai.incdec IS NULL", "1", "pai.incdec")." as incdec";
|
||||
$sql_child .= " FROM ".$db->prefix()."expeditiondet as ed";
|
||||
$sql_child .= " LEFT JOIN ".$db->prefix()."expeditiondet as edp ON edp.rowid = ".((int) $line_obj->fk_parent);
|
||||
$sql_child .= " LEFT JOIN ".$db->prefix()."product_association as pa ON pa.fk_product_pere = ".((int) $child_product_id);
|
||||
$sql_child .= " LEFT JOIN ".$db->prefix()."product_association as pai ON pai.fk_product_pere = edp.fk_product AND pai.fk_product_fils = ".((int) $child_product_id);
|
||||
$sql_child .= " WHERE ed.rowid = ".((int) $line_obj->rowid);
|
||||
$sql_child .= " GROUP BY pa.rowid, pai.incdec";
|
||||
$resql_child = $db->query($sql_child);
|
||||
if ($resql_child) {
|
||||
if ($child_obj = $db->fetch_object($resql_child)) {
|
||||
$line_obj->iskit = (int) $child_obj->iskit;
|
||||
if ($can_manage_stock) {
|
||||
$line_obj->incdec = (int) $child_obj->incdec; // reset value to 0 or 1 if stock can be managed
|
||||
}
|
||||
}
|
||||
$db->free($resql_child);
|
||||
}
|
||||
$line_obj->html_label = str_repeat(" ", $child_level) . "→" . $child_product->getNomUrl(1);
|
||||
$expedition_line_child_list[] = $line_obj;
|
||||
}
|
||||
$child_level++;
|
||||
}
|
||||
}
|
||||
}
|
||||
print $hookmanager->resPrint;
|
||||
|
||||
print '</tr>';
|
||||
|
||||
print '<!-- line for batch '.$numline.' -->';
|
||||
print '<tr class="oddeven autoresettr" name="'.$type.$suffix.'" data-remove="clear">';
|
||||
print '<td>';
|
||||
print '<input id="fk_commandedet'.$suffix.'" name="fk_commandedet'.$suffix.'" type="hidden" value="'.$objp->rowid.'">';
|
||||
print '<input id="idline'.$suffix.'" name="idline'.$suffix.'" type="hidden" value="'.$objd->rowid.'">';
|
||||
print '<input name="product_batch'.$suffix.'" type="hidden" value="'.$objd->fk_product.'">';
|
||||
|
||||
print '<!-- This is a U.P. (may include discount or not depending on STOCK_EXCLUDE_DISCOUNT_FOR_PMP. will be used for PMP calculation) -->';
|
||||
print '<input class="maxwidth75" name="pu'.$suffix.'" type="hidden" value="'.price2num($up_ht_disc, 'MU').'">';
|
||||
|
||||
print '</td>';
|
||||
|
||||
print '<td>';
|
||||
print '<input type="text" class="inputlotnumber quatrevingtquinzepercent" id="lot_number'.$suffix.'" name="lot_number'.$suffix.'" value="'.(GETPOSTISSET('lot_number'.$suffix) ? GETPOST('lot_number'.$suffix) : $objd->batch).'">';
|
||||
//print '<input type="hidden" id="lot_number'.$suffix.'" name="lot_number'.$suffix.'" value="'.$objd->batch.'">';
|
||||
print '</td>';
|
||||
if ($is_sell_by_enabled) {
|
||||
print '<td class="nowraponall">';
|
||||
$dlcdatesuffix = !empty($objd->sellby) ? dol_stringtotime($objd->sellby) : dol_mktime(0, 0, 0, GETPOSTINT('dlc'.$suffix.'month'), GETPOSTINT('dlc'.$suffix.'day'), GETPOSTINT('dlc'.$suffix.'year'));
|
||||
print $form->selectDate($dlcdatesuffix, 'dlc'.$suffix, 0, 0, 1, '');
|
||||
print '</td>';
|
||||
}
|
||||
if ($is_eat_by_enabled) {
|
||||
print '<td class="nowraponall">';
|
||||
$dluodatesuffix = !empty($objd->eatby) ? dol_stringtotime($objd->eatby) : dol_mktime(0, 0, 0, GETPOSTINT('dluo'.$suffix.'month'), GETPOSTINT('dluo'.$suffix.'day'), GETPOSTINT('dluo'.$suffix.'year'));
|
||||
print $form->selectDate($dluodatesuffix, 'dluo'.$suffix, 0, 0, 1, '');
|
||||
print '</td>';
|
||||
}
|
||||
print '<td colspan="2"> </td>'; // Supplier ref + Qty ordered + qty already dispatched
|
||||
} else {
|
||||
$type = 'dispatch';
|
||||
$colspan = 6;
|
||||
$colspan = $is_sell_by_enabled ? $colspan : --$colspan;
|
||||
$colspan = $is_eat_by_enabled ? $colspan : --$colspan;
|
||||
|
||||
// Enable hooks to append additional columns
|
||||
$parameters = array(
|
||||
// allows hook to distinguish between the rows with information and the rows with dispatch form input
|
||||
'is_information_row' => true,
|
||||
'j' => $j,
|
||||
'suffix' => $suffix,
|
||||
'objd' => $objd,
|
||||
);
|
||||
$reshook = $hookmanager->executeHooks(
|
||||
'printFieldListValue',
|
||||
$parameters,
|
||||
$object,
|
||||
$action
|
||||
);
|
||||
if ($reshook < 0) {
|
||||
setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
|
||||
}
|
||||
print $hookmanager->resPrint;
|
||||
|
||||
print '</tr>';
|
||||
|
||||
print '<!-- line no batch '.$numline.' -->';
|
||||
print '<tr class="oddeven autoresettr" name="'.$type.$suffix.'" data-remove="clear">';
|
||||
print '<td colspan="'.$colspan.'">';
|
||||
print '<input id="fk_commandedet'.$suffix.'" name="fk_commandedet'.$suffix.'" type="hidden" value="'.$objp->rowid.'">';
|
||||
print '<input id="idline'.$suffix.'" name="idline'.$suffix.'" type="hidden" value="'.$objd->rowid.'">';
|
||||
print '<input name="product'.$suffix.'" type="hidden" value="'.$objd->fk_product.'">';
|
||||
print '<!-- This is a up (may include discount or not depending on STOCK_EXCLUDE_DISCOUNT_FOR_PMP. will be used for PMP calculation) -->';
|
||||
print '<input class="maxwidth75" name="pu'.$suffix.'" type="hidden" value="'.price2num($up_ht_disc, 'MU').'">';
|
||||
print '</td>';
|
||||
}
|
||||
// Qty to dispatch
|
||||
print '<td class="right nowraponall">';
|
||||
print '<a href="" id="reset'.$suffix.'" class="resetline">'.img_picto($langs->trans("Reset"), 'eraser', 'class="pictofixedwidth opacitymedium"').'</a>';
|
||||
$suggestedvalue = (GETPOSTISSET('qty'.$suffix) ? GETPOSTFLOAT('qty'.$suffix) : $objd->qty);
|
||||
//var_dump($suggestedvalue);exit;
|
||||
print '<input id="qty'.$suffix.'" onchange="onChangeDispatchLineQty($(this))" name="qty'.$suffix.'" data-type="'.$type.'" data-index="'.$i.'" class="width50 right qtydispatchinput" value="'.$suggestedvalue.'" data-expected="'.$objd->qty.'">';
|
||||
print '</td>';
|
||||
print '<td>';
|
||||
if ($is_mod_batch_enabled && $objp->tobatch > 0) {
|
||||
$type = 'batch';
|
||||
print img_picto($langs->trans('AddStockLocationLine'), 'split.png', 'class="splitbutton" '.($numd != $j + 1 ? 'style="display:none"' : '').' onClick="addDispatchLine('.$i.', \''.$type.'\')"');
|
||||
} else {
|
||||
$type = 'dispatch';
|
||||
print img_picto($langs->trans('AddStockLocationLine'), 'split.png', 'class="splitbutton" '.($numd != $j + 1 ? 'style="display:none"' : '').' onClick="addDispatchLine('.$i.', \''.$type.'\')"');
|
||||
}
|
||||
|
||||
print '</td>';
|
||||
|
||||
// Warehouse
|
||||
print '<td class="right">';
|
||||
if ($objp->stockable_product == Product::ENABLED_STOCK) {
|
||||
if (count($listwarehouses) > 1) {
|
||||
print $formproduct->selectWarehouses(GETPOST("entrepot".$suffix) ? GETPOST("entrepot".$suffix) : $objd->fk_entrepot, "entrepot".$suffix, '', 1, 0, $objp->fk_product, '', 1, 0, array(), 'csswarehouse'.$suffix);
|
||||
} elseif (count($listwarehouses) == 1) {
|
||||
print $formproduct->selectWarehouses(GETPOST("entrepot".$suffix) ? GETPOST("entrepot".$suffix) : $objd->fk_entrepot, "entrepot".$suffix, '', 0, 0, $objp->fk_product, '', 1, 0, array(), 'csswarehouse'.$suffix);
|
||||
if (empty($expedition_line_child_list)) {
|
||||
$obj_exp->iskit = 0; // is not virtual product
|
||||
// manage stock if enabled for product
|
||||
if ($objp->stockable_product == Product::ENABLED_STOCK) {
|
||||
$obj_exp->incdec = 1;
|
||||
} else {
|
||||
$langs->load("errors");
|
||||
print $langs->trans("ErrorNoWarehouseDefined");
|
||||
$obj_exp->incdec = 0;
|
||||
}
|
||||
} else {
|
||||
// on force l'entrepot pour passer le test d'ajout de ligne dans expedition.class.php
|
||||
print '<input id="entrepot'.$suffix.'" name="entrepot'.$suffix.'" type="hidden" value="'.$objd->fk_entrepot.'">';
|
||||
print img_warning().' '.$langs->trans('StockDisabled');
|
||||
$expedition_line_child_list[] = $obj_exp;
|
||||
}
|
||||
print "</td>\n";
|
||||
|
||||
// Enable hooks to append additional columns
|
||||
$parameters = array(
|
||||
'is_information_row' => false, // this is a dispatch form row
|
||||
'i' => $i,
|
||||
'suffix' => $suffix,
|
||||
'objp' => $objp,
|
||||
);
|
||||
$reshook = $hookmanager->executeHooks(
|
||||
'printFieldListValue',
|
||||
$parameters,
|
||||
$object,
|
||||
$action
|
||||
);
|
||||
if ($reshook < 0) {
|
||||
setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
|
||||
$child_suffix = $suffix;
|
||||
foreach ($expedition_line_child_list as $objd) {
|
||||
$child_line_id = $objd->rowid;
|
||||
|
||||
$can_update_stock = empty($objd->iskit) && !empty($objd->incdec);
|
||||
$suffix = $child_line_id.$child_suffix;
|
||||
|
||||
// set default batch values for this dispatched line (lot/serial number of virtual product)
|
||||
$dispatch_line_batch_current = null;
|
||||
if (!empty($objd->batch_list)) {
|
||||
$dispatch_line_batch_count = count($objd->batch_list);
|
||||
// if only one batch found, this batch is pre-selected
|
||||
if ($dispatch_line_batch_count == 1) {
|
||||
$dispatch_line_batch_current = current($objd->batch_list);
|
||||
}
|
||||
}
|
||||
if (is_object($dispatch_line_batch_current)) {
|
||||
$objd->batch = $dispatch_line_batch_current->batch;
|
||||
$objd->eatby = $dispatch_line_batch_current->eatby;
|
||||
$objd->sellby = $dispatch_line_batch_current->sellby;
|
||||
}
|
||||
|
||||
if ($is_mod_batch_enabled
|
||||
&& (
|
||||
!empty($objd->batch)
|
||||
|| (is_null($objd->batch) && $tmpproduct->status_batch > 0)
|
||||
|| !empty($objd->batch_list)
|
||||
)
|
||||
) {
|
||||
$type = 'batch';
|
||||
|
||||
// Enable hooks to append additional columns
|
||||
$parameters = array(
|
||||
// allows hook to distinguish between the rows with information and the rows with dispatch form input
|
||||
'is_information_row' => true,
|
||||
'j' => $j,
|
||||
'suffix' => $suffix,
|
||||
'objd' => $objd,
|
||||
);
|
||||
$reshook = $hookmanager->executeHooks(
|
||||
'printFieldListValue',
|
||||
$parameters,
|
||||
$object,
|
||||
$action
|
||||
);
|
||||
if ($reshook < 0) {
|
||||
setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
|
||||
}
|
||||
print $hookmanager->resPrint;
|
||||
|
||||
print '</tr>';
|
||||
|
||||
print '<!-- line for batch ' . $numline . ' -->';
|
||||
print '<tr class="oddeven autoresettr" name="' . $type . '-' . $suffix . '" data-remove="clear">';
|
||||
print '<td>';
|
||||
print '<input id="fk_commandedet' . $suffix . '" name="fk_commandedet' . $suffix . '" type="hidden" value="' . $objp->rowid . '">';
|
||||
print '<input id="idline' . $suffix . '" name="idline' . $suffix . '" type="hidden" value="' . $objd->rowid . '">';
|
||||
print '<input id="fk_parent' . $suffix . '" name="fk_parent' . $suffix . '" type="hidden" value="' . $objd->fk_parent . '">';
|
||||
print '<input name="productbatch' . $suffix . '" type="hidden" value="' . $objd->fk_product . '">';
|
||||
|
||||
print '<!-- This is a U.P. (may include discount or not depending on STOCK_EXCLUDE_DISCOUNT_FOR_PMP. will be used for PMP calculation) -->';
|
||||
print '<input class="maxwidth75" name="pu' . $suffix . '" type="hidden" value="' . price2num($up_ht_disc, 'MU') . '">';
|
||||
if (!empty($objd->html_label)) {
|
||||
print $objd->html_label;
|
||||
}
|
||||
print '</td>';
|
||||
|
||||
print '<td>';
|
||||
print '<input type="text" class="inputlotnumber quatrevingtquinzepercent csslotnumber" name="lot_number'.$suffix.'" value="'.(GETPOSTISSET('lot_number'.$suffix) ? GETPOST('lot_number'.$suffix) : $objd->batch).'" list="lot_number'.$suffix.'">';
|
||||
print $formproduct->selectLotDataList('lot_number'.$suffix, 0, $objd->fk_product, GETPOST("entrepot".$suffix) ? GETPOST("entrepot".$suffix) : $objd->fk_warehouse, array());
|
||||
print '</td>';
|
||||
|
||||
if ($is_sell_by_enabled) {
|
||||
print '<td class="nowraponall">';
|
||||
$dlcdatesuffix = !empty($objd->sellby) ? dol_stringtotime($objd->sellby) : dol_mktime(0, 0, 0, GETPOSTINT('dlc'.$suffix.'month'), GETPOSTINT('dlc'.$suffix.'day'), GETPOSTINT('dlc'.$suffix.'year'));
|
||||
print $form->selectDate($dlcdatesuffix, 'dlc'.$suffix, 0, 0, 1, '');
|
||||
print '</td>';
|
||||
}
|
||||
if ($is_eat_by_enabled) {
|
||||
print '<td class="nowraponall">';
|
||||
$dluodatesuffix = !empty($objd->eatby) ? dol_stringtotime($objd->eatby) : dol_mktime(0, 0, 0, GETPOSTINT('dluo'.$suffix.'month'), GETPOSTINT('dluo'.$suffix.'day'), GETPOSTINT('dluo'.$suffix.'year'));
|
||||
print $form->selectDate($dluodatesuffix, 'dluo'.$suffix, 0, 0, 1, '');
|
||||
print '</td>';
|
||||
}
|
||||
print '<td colspan="2"> </td>'; // Supplier ref + Qty ordered + qty already dispatched
|
||||
} else {
|
||||
$type = 'dispatch';
|
||||
$colspan = 6;
|
||||
$colspan = $is_sell_by_enabled ? $colspan : --$colspan;
|
||||
$colspan = $is_eat_by_enabled ? $colspan : --$colspan;
|
||||
|
||||
// Enable hooks to append additional columns
|
||||
$parameters = array(
|
||||
// allows hook to distinguish between the rows with information and the rows with dispatch form input
|
||||
'is_information_row' => true,
|
||||
'j' => $j,
|
||||
'suffix' => $suffix,
|
||||
'objd' => $objd,
|
||||
);
|
||||
$reshook = $hookmanager->executeHooks(
|
||||
'printFieldListValue',
|
||||
$parameters,
|
||||
$object,
|
||||
$action
|
||||
);
|
||||
if ($reshook < 0) {
|
||||
setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
|
||||
}
|
||||
print $hookmanager->resPrint;
|
||||
|
||||
print '</tr>';
|
||||
|
||||
print '<!-- line no batch '.$numline.' -->';
|
||||
print '<tr class="oddeven autoresettr" name="'.$type.'-'.$suffix.'" data-remove="clear">';
|
||||
print '<td colspan="'.$colspan.'">';
|
||||
print '<input id="fk_commandedet'.$suffix.'" name="fk_commandedet'.$suffix.'" type="hidden" value="'.$objp->rowid.'">';
|
||||
print '<input id="idline'.$suffix.'" name="idline'.$suffix.'" type="hidden" value="'.$objd->rowid.'">';
|
||||
print '<input id="fk_parent'.$suffix.'" name="fk_parent'.$suffix.'" type="hidden" value="'.$objd->fk_parent.'">';
|
||||
print '<input name="product'.$suffix.'" type="hidden" value="'.$objd->fk_product.'">';
|
||||
print '<!-- This is a up (may include discount or not depending on STOCK_EXCLUDE_DISCOUNT_FOR_PMP. will be used for PMP calculation) -->';
|
||||
print '<input class="maxwidth75" name="pu'.$suffix.'" type="hidden" value="'.price2num($up_ht_disc, 'MU').'">';
|
||||
if (!empty($objd->html_label)) {
|
||||
print $objd->html_label;
|
||||
}
|
||||
print '</td>';
|
||||
}
|
||||
// Qty to dispatch
|
||||
print '<td class="right nowraponall">';
|
||||
$suggestedvalue = (GETPOSTISSET('qty'.$suffix) ? GETPOSTFLOAT('qty'.$suffix) : $objd->qty);
|
||||
//var_dump($suggestedvalue);exit;
|
||||
if ($can_update_stock) {
|
||||
print '<a href="" id="reset'.$suffix.'" class="resetline">'.img_picto($langs->trans("Reset"), 'eraser', 'class="pictofixedwidth opacitymedium"').'</a>';
|
||||
print '<input id="qty'.$suffix.'" onchange="onChangeDispatchLineQty($(this))" name="qty'.$suffix.'" data-type="'.$type.'" data-index="'.$i.'" class="width50 right qtydispatchinput" value="'.$suggestedvalue.'" data-expected="'.$objd->qty.'">';
|
||||
} else {
|
||||
print '<input type="hidden" id="qty'.$suffix.'" name="qty'.$suffix.'" value="">';
|
||||
}
|
||||
print '</td>';
|
||||
print '<td>';
|
||||
if ($can_update_stock) {
|
||||
print img_picto($langs->trans('AddStockLocationLine'), 'split.png', 'class="splitbutton" onClick="addDispatchLine('.$i.', \''.$type.'-'.$child_line_id.'\')"');
|
||||
}
|
||||
print '</td>';
|
||||
|
||||
// Warehouse
|
||||
print '<td class="right">';
|
||||
if ($can_update_stock) {
|
||||
if (count($listwarehouses) > 1) {
|
||||
print $formproduct->selectWarehouses(GETPOST("entrepot".$suffix) ? GETPOST("entrepot".$suffix) : $objd->fk_warehouse, "entrepot".$suffix, '', 1, 0, $objd->fk_product, '', 1, 0, array(), 'csswarehouse'.$suffix);
|
||||
} elseif (count($listwarehouses) == 1) {
|
||||
print $formproduct->selectWarehouses(GETPOST("entrepot".$suffix) ? GETPOST("entrepot".$suffix) : $objd->fk_warehouse, "entrepot".$suffix, '', 0, 0, $objd->fk_product, '', 1, 0, array(), 'csswarehouse'.$suffix);
|
||||
} else {
|
||||
$langs->load("errors");
|
||||
print $langs->trans("ErrorNoWarehouseDefined");
|
||||
}
|
||||
} else {
|
||||
// on force l'entrepot pour passer le test d'ajout de ligne dans expedition.class.php
|
||||
print '<input id="entrepot'.$suffix.'" name="entrepot'.$suffix.'" type="hidden" value="'.$objd->fk_warehouse.'">';
|
||||
print img_warning().' '.$langs->trans('StockDisabled');
|
||||
}
|
||||
print "</td>\n";
|
||||
|
||||
// Enable hooks to append additional columns
|
||||
$parameters = array(
|
||||
'is_information_row' => false, // this is a dispatch form row
|
||||
'i' => $i,
|
||||
'suffix' => $suffix,
|
||||
'objp' => $objp,
|
||||
'objd' => $objd,
|
||||
);
|
||||
$reshook = $hookmanager->executeHooks(
|
||||
'printFieldListValue',
|
||||
$parameters,
|
||||
$object,
|
||||
$action
|
||||
);
|
||||
if ($reshook < 0) {
|
||||
setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
|
||||
}
|
||||
print $hookmanager->resPrint;
|
||||
|
||||
print "</tr>\n";
|
||||
}
|
||||
print $hookmanager->resPrint;
|
||||
|
||||
print "</tr>\n";
|
||||
$j++;
|
||||
|
||||
$numline++;
|
||||
}
|
||||
$suffix = "_".$j."_".$i;
|
||||
|
||||
//$suffix = "_".$j."_".$i;
|
||||
} else {
|
||||
$errorMsg = 'Shipment dispatch SQL error : '.$db->lasterror();
|
||||
setEventMessage($errorMsg, 'errors');
|
||||
dol_syslog($errorMsg, LOG_ERR);
|
||||
}
|
||||
|
||||
/*
|
||||
if ($j == 0) {
|
||||
if ($is_mod_batch_enabled && !empty($objp->tobatch)) {
|
||||
$type = 'batch';
|
||||
|
|
@ -997,7 +1159,7 @@ if ($object->id > 0 || !empty($object->ref)) {
|
|||
print '<td>';
|
||||
print '<input id="fk_commandedet'.$suffix.'" name="fk_commandedet'.$suffix.'" type="hidden" value="'.$objp->rowid.'">';
|
||||
print '<input id="idline'.$suffix.'" name="idline'.$suffix.'" type="hidden" value="-1">';
|
||||
print '<input name="product_batch'.$suffix.'" type="hidden" value="'.$objp->fk_product.'">';
|
||||
print '<input name="productbatch'.$suffix.'" type="hidden" value="'.$objp->fk_product.'">';
|
||||
|
||||
print '<!-- This is a up (may include discount or not depending on STOCK_EXCLUDE_DISCOUNT_FOR_PMP. will be used for PMP calculation) -->';
|
||||
print '<input class="maxwidth75" name="pu'.$suffix.'" type="hidden" value="'.price2num($up_ht_disc, 'MU').'">';
|
||||
|
|
@ -1110,10 +1272,128 @@ if ($object->id > 0 || !empty($object->ref)) {
|
|||
print $hookmanager->resPrint;
|
||||
print "</tr>\n";
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
|
||||
// reload batch select and warehouse select on change (Ajax)
|
||||
$out_js_line_list = array();
|
||||
$out_js_line = 'function updateselectbatchbywarehouse() {';
|
||||
$out_js_line .= ' jQuery(document).on("change", "select[name*=\"entrepot\"]", function() {';
|
||||
$out_js_line .= ' var selectwarehouse = jQuery(this);';
|
||||
$out_js_line .= ' var selectbatch_name = selectwarehouse.attr("name").replace("entrepot", "lot_number");';
|
||||
$out_js_line .= ' var selectbatch = jQuery("datalist[id*=\""+selectbatch_name+"\"]");';
|
||||
$out_js_line .= ' var selectedbatch = selectbatch.val();';
|
||||
$out_js_line .= ' var product_element_name = selectwarehouse.attr("name").replace("entrepot", "productbatch");';
|
||||
$out_js_line .= ' jQuery.ajax({';
|
||||
$out_js_line .= ' type: "POST",';
|
||||
$out_js_line .= ' url: "'.dol_escape_js(dol_buildpath('/expedition/ajax/interface.php', 1)).'",';
|
||||
$out_js_line .= ' data: {';
|
||||
$out_js_line .= ' action: "updateselectbatchbywarehouse",';
|
||||
$out_js_line .= ' warehouse_id: jQuery(this).val(),';
|
||||
$out_js_line .= ' token: "'.currentToken().'",';
|
||||
$out_js_line .= ' product_id: jQuery("input[name=\""+product_element_name+"\"]").val()';
|
||||
$out_js_line .= ' }';
|
||||
$out_js_line .= ' }).done(function(data) {';
|
||||
$out_js_line .= ' selectbatch.empty();';
|
||||
$out_js_line .= ' if (typeof data == "object") {';
|
||||
$out_js_line .= ' console.log("data is already type object, no need to parse it");';
|
||||
$out_js_line .= ' } else {';
|
||||
$out_js_line .= ' console.log("data is type "+(typeof data));';
|
||||
$out_js_line .= ' data = JSON.parse(data);';
|
||||
$out_js_line .= ' }';
|
||||
$out_js_line .= ' selectbatch.append(jQuery("<option>", {';
|
||||
$out_js_line .= ' value: "",';
|
||||
$out_js_line .= ' }));';
|
||||
$out_js_line .= ' jQuery.each(data, function(key, objBatch) {';
|
||||
$out_js_line .= ' var dataEatByDate = objBatch.eatbydate;';
|
||||
$out_js_line .= ' var dataSellByDate = objBatch.sellbydate;';
|
||||
$out_js_line .= ' var optionLabel = key+" (";';
|
||||
$out_js_line .= ' if (selectwarehouse.val() == -1) {';
|
||||
$out_js_line .= ' optionLabel += "'.dol_escape_js($langs->trans('TotalStock')).': "+objBatch.qty;';
|
||||
$out_js_line .= ' } else {';
|
||||
$out_js_line .= ' optionLabel += "'.dol_escape_js($langs->trans('Stock')).': "+objBatch.qty;';
|
||||
$out_js_line .= ' }';
|
||||
$out_js_line .= ' if (dataEatByDate != "") {';
|
||||
$out_js_line .= ' optionLabel += " - '.dol_escape_js($langs->trans('EatByDate')).': "+dataEatByDate;';
|
||||
$out_js_line .= ' }';
|
||||
$out_js_line .= ' if (dataSellByDate != "") {';
|
||||
$out_js_line .= ' optionLabel += " - '.dol_escape_js($langs->trans('SellByDate')).': "+dataSellByDate;';
|
||||
$out_js_line .= ' }';
|
||||
$out_js_line .= ' optionLabel += ")";';
|
||||
$out_js_line .= ' var option = "<option data-eatbydate=\""+dataEatByDate+"\" data-sellbydate=\""+dataSellByDate+"\" value=\""+key+"\"";';
|
||||
$out_js_line .= ' if (key === selectedbatch) {';
|
||||
$out_js_line .= ' option += " selected";';
|
||||
$out_js_line .= ' }';
|
||||
$out_js_line .= ' option += ">"+optionLabel+"</option>";';
|
||||
$out_js_line .= ' selectbatch.append(option);';
|
||||
$out_js_line .= ' });';
|
||||
$out_js_line .= ' });';
|
||||
$out_js_line .= ' });';
|
||||
$out_js_line .= '}';
|
||||
|
||||
$out_js_line .= 'function updateselectwarehousebybatch() {';
|
||||
$out_js_line .= ' jQuery(document).on("change", "input[name*=lot_number]", function() {';
|
||||
$out_js_line .= ' var selectbatch = jQuery(this);';
|
||||
$out_js_line .= ' var selectwarehouse_name = selectbatch.attr("name").replace("lot_number", "entrepot");';
|
||||
$out_js_line .= ' var selectwarehouse = jQuery("select[name*=\""+selectwarehouse_name+"\"]");';
|
||||
$out_js_line .= ' var selectedwarehouse = selectwarehouse.val();';
|
||||
$out_js_line .= ' var inputbatchdlc_name = selectbatch.attr("name").replace("lot_number", "dlc");';
|
||||
$out_js_line .= ' var inputbatchdlc = jQuery("input[name*=\""+inputbatchdlc_name+"\"]");';
|
||||
$out_js_line .= ' var inputbatchdluo_name = selectbatch.attr("name").replace("lot_number", "dluo");';
|
||||
$out_js_line .= ' var inputbatchdluo = jQuery("input[name*=\""+inputbatchdluo_name+"\"]");';
|
||||
$out_js_line .= ' var datalistselectedbatch = jQuery("#"+selectbatch.attr("name")+" option[value=\""+selectbatch.val()+"\"]");';
|
||||
$out_js_line .= ' var selectedbatch_dlc = datalistselectedbatch.data("sellbydate");';
|
||||
$out_js_line .= ' var selectedbatch_dluo = datalistselectedbatch.data("eatbydate");';
|
||||
$out_js_line .= ' if (typeof selectedbatch_dlc === "undefined") {';
|
||||
$out_js_line .= ' selectedbatch_dlc = "";';
|
||||
$out_js_line .= ' }';
|
||||
$out_js_line .= ' if (typeof selectedbatch_dluo === "undefined") {';
|
||||
$out_js_line .= ' selectedbatch_dluo = "";';
|
||||
$out_js_line .= ' }';
|
||||
$out_js_line .= ' inputbatchdlc.val(selectedbatch_dlc).trigger("change");';
|
||||
$out_js_line .= ' inputbatchdluo.val(selectedbatch_dluo).trigger("change");';
|
||||
$out_js_line .= ' if (selectedwarehouse != -1) {';
|
||||
$out_js_line .= ' return;';
|
||||
$out_js_line .= ' }';
|
||||
$out_js_line .= ' var product_element_name = selectbatch.attr("name").replace("lot_number", "productbatch");';
|
||||
$out_js_line .= ' jQuery.ajax({';
|
||||
$out_js_line .= ' type: "POST",';
|
||||
$out_js_line .= ' url: "'.dol_escape_js(dol_buildpath('/expedition/ajax/interface.php', 1)).'",';
|
||||
$out_js_line .= ' data: {';
|
||||
$out_js_line .= ' action: "updateselectwarehousebybatch",';
|
||||
$out_js_line .= ' batch: jQuery(this).val(),';
|
||||
$out_js_line .= ' token: "'.currentToken().'",';
|
||||
$out_js_line .= ' product_id: jQuery("input[name=\""+product_element_name+"\"]").val()';
|
||||
$out_js_line .= ' }';
|
||||
$out_js_line .= ' }).done(function(data) {';
|
||||
$out_js_line .= ' if (typeof data == "object") {';
|
||||
$out_js_line .= ' console.log("data is already type object, no need to parse it");';
|
||||
$out_js_line .= ' } else {';
|
||||
$out_js_line .= ' console.log("data is type "+(typeof data));';
|
||||
$out_js_line .= ' data = JSON.parse(data);';
|
||||
$out_js_line .= ' }';
|
||||
$out_js_line .= ' if (data != 0) {';
|
||||
$out_js_line .= ' selectwarehouse.val(data).change();';
|
||||
$out_js_line .= ' }';
|
||||
$out_js_line .= ' });';
|
||||
$out_js_line .= ' });';
|
||||
$out_js_line .= '}';
|
||||
$out_js_line_list[] = $out_js_line;
|
||||
|
||||
$out_js = '<script type="text/javascript" language="javascript">';
|
||||
$out_js .= 'jQuery(document).ready(function() {';
|
||||
// when a warehouse is selected, only the lot/serial numbers that are available in it are offered
|
||||
$out_js .= 'updateselectbatchbywarehouse();';
|
||||
// when a lot/serial number is selected and it is only available in one warehouse, the warehouse is automatically selected
|
||||
$out_js .= 'updateselectwarehousebybatch();';
|
||||
$out_js .= implode('', $out_js_line_list);
|
||||
$out_js .= '});';
|
||||
$out_js .= '</script>';
|
||||
print $out_js;
|
||||
|
||||
$db->free($resql);
|
||||
} else {
|
||||
dol_print_error($db);
|
||||
|
|
@ -1419,7 +1699,7 @@ if ($object->id > 0 || !empty($object->ref)) {
|
|||
$("select[name=fk_default_warehouse]").change(function() {
|
||||
console.log("warehouse is modified");
|
||||
var fk_default_warehouse = $("option:selected", this).val();
|
||||
$("select[name^=entrepot_]").val(fk_default_warehouse).change();
|
||||
$("select[name^=entrepot]").val(fk_default_warehouse).change();
|
||||
});
|
||||
|
||||
$("#autoreset").click(function() {
|
||||
|
|
@ -1430,7 +1710,12 @@ if ($object->id > 0 || !empty($object->ref)) {
|
|||
console.log("we process line "+id+" "+idtab);
|
||||
if ($(this).data("remove") == "clear") { /* data-remove=clear means that line qty must be cleared but line must not be removed */
|
||||
console.log("We clear the object to expected value")
|
||||
$("#qty_"+idtab[1]+"_"+idtab[2]).val("");
|
||||
var idlinetab = idtab[0].split("-");
|
||||
var idline = "";
|
||||
if (idlinetab.length > 0) {
|
||||
idline = idlinetab[1];
|
||||
}
|
||||
$("#qty"+idline+"_"+idtab[1]+"_"+idtab[2]).val("");
|
||||
/*
|
||||
qtyexpected = $("#qty_"+idtab[1]+"_"+idtab[2]).data("expected")
|
||||
console.log(qtyexpected);
|
||||
|
|
@ -1458,9 +1743,9 @@ if ($object->id > 0 || !empty($object->ref)) {
|
|||
$(".resetline").on("click", function(event) {
|
||||
event.preventDefault();
|
||||
id = $(this).attr("id");
|
||||
id = id.split("reset_");
|
||||
console.log("Reset trigger for id = qty_"+id[1]);
|
||||
$("#qty_"+id[1]).val("");
|
||||
id = id.split("reset");
|
||||
console.log("Reset trigger for id = qty"+id[1]);
|
||||
$("#qty"+id[1]).val("");
|
||||
});
|
||||
});
|
||||
</script>';
|
||||
|
|
|
|||
|
|
@ -70,18 +70,23 @@ function addDispatchLine(index, type, mode) {
|
|||
|
||||
console.log("expedition/js/lib_dispatch.js.php addDispatchLine Split line type="+type+" index="+index+" mode="+mode);
|
||||
|
||||
var lineId = '';
|
||||
var typeArr = type.split('-');
|
||||
if (typeArr.length > 0) {
|
||||
lineId = typeArr[1];
|
||||
}
|
||||
var $row0 = $("tr[name='"+type+'_0_'+index+"']");
|
||||
var $dpopt = $row0.find('.hasDatepicker').first().datepicker('option', 'all'); // get current datepicker options to apply the same to the cloned datepickers
|
||||
var $row = $row0.clone(true); // clone first batch line to jQuery object
|
||||
var nbrTrs = $("tr[name^='"+type+"_'][name$='_"+index+"']").length; // count nb of tr line with attribute name that starts with 'batch_' or 'dispatch_', and end with _index
|
||||
var qtyOrdered = parseFloat($("#qty_ordered_0_"+index).val()); // Qty ordered is same for all rows
|
||||
|
||||
var qty = parseFloat($("#qty_"+(nbrTrs - 1)+"_"+index).val());
|
||||
var qty = parseFloat($("#qty"+lineId+"_"+(nbrTrs - 1)+"_"+index).val());
|
||||
if (isNaN(qty)) {
|
||||
qty = '';
|
||||
}
|
||||
|
||||
console.log("expedition/js/lib_dispatch.js.php addDispatchLine Split line nbrTrs="+nbrTrs+" qtyOrdered="+qtyOrdered+" qty="+qty);
|
||||
console.log("expedition/js/lib_dispatch.js.php addDispatchLine Split line="+lineId+" nbrTrs="+nbrTrs+" qtyOrdered="+qtyOrdered+" qty="+qty);
|
||||
|
||||
var qtyDispatched;
|
||||
|
||||
|
|
@ -106,7 +111,7 @@ function addDispatchLine(index, type, mode) {
|
|||
if (newlineqty <= 0) {
|
||||
newlineqty = qty - 1;
|
||||
oldlineqty = 1;
|
||||
$("#qty_"+(nbrTrs - 1)+"_"+index).val(oldlineqty);
|
||||
$("#qty"+lineId+"_"+(nbrTrs - 1)+"_"+index).val(oldlineqty);
|
||||
}
|
||||
|
||||
//replace tr suffix nbr
|
||||
|
|
@ -123,66 +128,72 @@ function addDispatchLine(index, type, mode) {
|
|||
});
|
||||
}, 0);
|
||||
|
||||
//create new select2 to avoid duplicate id of cloned one
|
||||
$row.find("select[name='" + 'entrepot_' + nbrTrs + '_' + index + "']").select2();
|
||||
// create new select2 to avoid duplicate id of cloned one for warehouse
|
||||
$row.find("select[name='"+'entrepot'+lineId+'_'+nbrTrs+'_'+index+"']").select2();
|
||||
// create new select2 to avoid duplicate id of cloned one for lot / serial number
|
||||
$row.find("select[name='"+'lot_number'+lineId+'_'+nbrTrs+'_'+index+"']").select2();
|
||||
// TODO find solution to copy selected option to new select
|
||||
// TODO find solution to keep new tr's after page refresh
|
||||
//clear value
|
||||
$row.find("input[name^='qty']").val('');
|
||||
//change name of new row
|
||||
$row.attr('name', type + '_' + nbrTrs + '_' + index);
|
||||
$row.attr('name', type+'_'+nbrTrs+'_'+index);
|
||||
//insert new row before last row
|
||||
$("tr[name^='" + type + "_'][name$='_" + index + "']:last").after($row);
|
||||
$("tr[name^='"+type+"_'][name$='_"+index+"']:last").after($row);
|
||||
|
||||
//remove cloned select2 with duplicate id.
|
||||
$("#s2id_entrepot_" + nbrTrs + '_' + index).detach(); // old way to find duplicated select2 component
|
||||
$(".csswarehouse_" + nbrTrs + "_" + index + ":first-child").parent("span.selection").parent(".select2").detach();
|
||||
// remove cloned select2 with duplicate id for warehouse
|
||||
$("#s2id_entrepot"+lineId+"_"+nbrTrs+'_'+index).detach(); // old way to find duplicated select2 component
|
||||
$(".csswarehouse"+lineId+"_"+nbrTrs+"_"+index + ":first-child").parent("span.selection").parent(".select2").detach();
|
||||
|
||||
// remove cloned select2 with duplicate id for lot / serial number
|
||||
$("#s2id_lot_number"+lineId+"_"+nbrTrs+'_'+index).detach(); // old way to find duplicated select2 component
|
||||
$(".csslotnumber"+lineId+"_"+nbrTrs+"_"+index + ":first-child").parent("span.selection").parent(".select2").detach();
|
||||
|
||||
/* Suffix of lines are: _ trs.length _ index */
|
||||
$("#qty_"+nbrTrs+"_"+index).focus();
|
||||
$("#qty"+lineId+"_"+nbrTrs+"_"+index).focus();
|
||||
$("#qty_dispatched_0_"+index).val(oldlineqty);
|
||||
|
||||
//hide all buttons then show only the last one
|
||||
$("tr[name^='" + type + "_'][name$='_" + index + "'] .splitbutton").hide();
|
||||
$("tr[name^='" + type + "_'][name$='_" + index + "']:last .splitbutton").show();
|
||||
$("tr[name^='"+type+"_'][name$='_"+index+"'] .splitbutton").hide();
|
||||
$("tr[name^='"+type+"_'][name$='_"+index+"']:last .splitbutton").show();
|
||||
|
||||
$("#reset_" + (nbrTrs) + "_" + index).click(function (event) {
|
||||
$("#reset"+lineId+"_"+(nbrTrs)+"_"+index).click(function (event) {
|
||||
event.preventDefault();
|
||||
id = $(this).attr("id");
|
||||
id = id.split("reset_");
|
||||
id = id.split("reset"+lineId+"_");
|
||||
idrow = id[1];
|
||||
idlast = $("tr[name^='" + type + "_'][name$='_" + index + "']:last .qtydispatchinput").attr("id");
|
||||
if (idlast == $("#qty_" + idrow).attr("id")) {
|
||||
console.log("expedition/js/lib_dispatch.js.php Remove trigger for tr name = " + type + "_" + idrow);
|
||||
$('tr[name="' + type + '_' + idrow + '"').remove();
|
||||
$("tr[name^='" + type + "_'][name$='_" + index + "']:last .splitbutton").show();
|
||||
idlast = $("tr[name^='"+type+"_'][name$='_"+index+"']:last .qtydispatchinput").attr("id");
|
||||
if (idlast == $("#qty"+lineId+"_"+idrow).attr("id")) {
|
||||
console.log("expedition/js/lib_dispatch.js.php Remove trigger for tr name = "+type+"_"+idrow);
|
||||
$('tr[name="'+type+'_'+idrow+'"').remove();
|
||||
$("tr[name^='"+type+"_'][name$='_"+index+"']:last .splitbutton").show();
|
||||
} else {
|
||||
console.log("expedition/js/lib_dispatch.js.php Reset trigger for id = qty_" + idrow);
|
||||
$("#qty_" + idrow).val("");
|
||||
console.log("expedition/js/lib_dispatch.js.php Reset trigger for id = qty_"+idrow);
|
||||
$("#qty"+lineId+"_"+idrow).val("");
|
||||
}
|
||||
});
|
||||
|
||||
if (mode === 'lessone')
|
||||
{
|
||||
qty = 1; // keep 1 in old line
|
||||
$("#qty_"+(nbrTrs-1)+"_"+index).val(qty);
|
||||
$("#qty"+lineId+"_"+(nbrTrs-1)+"_"+index).val(qty);
|
||||
}
|
||||
$("#qty_"+nbrTrs+"_"+index).val(newlineqty);
|
||||
$("#qty"+lineId+"_"+nbrTrs+"_"+index).val(newlineqty);
|
||||
// Store arbitrary data for dispatch qty input field change event
|
||||
$("#qty_" + (nbrTrs - 1) + "_" + index).data('qty', qty);
|
||||
$("#qty_" + (nbrTrs - 1) + "_" + index).data('type', type);
|
||||
$("#qty_" + (nbrTrs - 1) + "_" + index).data('index', index);
|
||||
$("#qty"+lineId+"_" + (nbrTrs - 1) + "_" + index).data('qty', qty);
|
||||
$("#qty"+lineId+"_" + (nbrTrs - 1) + "_" + index).data('type', type);
|
||||
$("#qty"+lineId+"_" + (nbrTrs - 1) + "_" + index).data('index', index);
|
||||
// Update dispatched qty when value dispatch qty input field changed
|
||||
//$("#qty_" + (nbrTrs - 1) + "_" + index).change(this.onChangeDispatchLineQty);
|
||||
//set focus on lot of new line (if it exists)
|
||||
$("#lot_number_" + (nbrTrs) + "_" + index).focus();
|
||||
$("#lot_number"+lineId+"_"+(nbrTrs)+"_"+index).focus();
|
||||
//Clean bad values
|
||||
$("tr[name^='" + type + "_'][name$='_" + index + "']:last").data("remove", "remove");
|
||||
$("#lot_number_" + (nbrTrs) + "_" + index).val("")
|
||||
$("#idline_" + (nbrTrs) + "_" + index).val("-1")
|
||||
$("#qty_" + (nbrTrs) + "_" + index).data('expected', "0");
|
||||
$("tr[name^='"+type+"_'][name$='_"+index + "']:last").data("remove", "remove");
|
||||
$("#lot_number_"+(nbrTrs) + "_"+index).val("")
|
||||
$("#idline"+lineId+"_"+(nbrTrs)+"_"+index).val("-1")
|
||||
$("#qty"+lineId+"_"+(nbrTrs)+"_"+index).data('expected', "0");
|
||||
//$("input[type='hidden']#lot_number_" + (nbrTrs) + "_" + index).remove();
|
||||
$("#lot_number_" + (nbrTrs) + "_" + index).removeAttr("disabled");
|
||||
$("#lot_number"+lineId+"_"+(nbrTrs)+"_"+index).removeAttr("disabled");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -204,13 +215,13 @@ function onChangeDispatchLineQty(element) {
|
|||
index = id[2];
|
||||
|
||||
if (index >= 0 && type && qty >= 0) {
|
||||
nbrTrs = $("tr[name^='" + type + "_'][name$='_" + index + "']").length;
|
||||
nbrTrs = $("tr[name^='"+type+"_'][name$='_"+index+"']").length;
|
||||
qtyChanged = parseFloat($(element).val()) - qty; // qty changed
|
||||
qtyDispatching = parseFloat($(element).val()); // qty currently being dispatched
|
||||
qtyOrdered = parseFloat($("#qty_ordered_0_" + index).val()); // qty ordered
|
||||
qtyDispatched = parseFloat($("#qty_dispatched_0_" + index).val()); // qty already dispatched
|
||||
qtyOrdered = parseFloat($("#qty_ordered_0_"+index).val()); // qty ordered
|
||||
qtyDispatched = parseFloat($("#qty_dispatched_0_"+index).val()); // qty already dispatched
|
||||
|
||||
console.log("onChangeDispatchLineQty qtyChanged: " + qtyChanged + " qtyDispatching: " + qtyDispatching + " qtyOrdered: " + qtyOrdered + " qtyDispatched: " + qtyDispatched);
|
||||
console.log("onChangeDispatchLineQty qtyChanged: "+qtyChanged+" qtyDispatching: "+qtyDispatching+" qtyOrdered: "+qtyOrdered+" qtyDispatched: "+qtyDispatched);
|
||||
|
||||
if ((qtyChanged) <= (qtyOrdered - (qtyDispatched + qtyDispatching))) {
|
||||
$("#qty_dispatched_0_" + index).val(qtyDispatched + qtyChanged);
|
||||
|
|
|
|||
|
|
@ -457,3 +457,4 @@ StockEnabled=Stock enabled
|
|||
GenerateImage=Generate image
|
||||
GenerateWithAI=Generate with AI
|
||||
PriceByCustomeAndMultiPricesAbility=Different prices for each customer + Multiple price segments per product/service (each customer is in one price segment)
|
||||
WhenProductVirtualOnOptionAreForced=When virtual products option is on, automatic stock decrease is forced to 'Decrease real stocks on shipping validation' and automatic increase mode is forced to 'Increase real stocks on manual dispatching into warehouses' and can't be edited. Other options can be defined as you want.
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ SumOfProductWeights=Sum of product weights
|
|||
# warehouse details
|
||||
DetailWarehouseNumber= Warehouse details
|
||||
DetailWarehouseFormat= W:%s (Qty: %d)
|
||||
DetailChildrenFormat=%s : %s (Qty: %s)
|
||||
SHIPPING_DISPLAY_STOCK_ENTRY_DATE=Display last date of entry in stock during shipment creation for serial number or batch
|
||||
CreationOptions=Available options during shipment creation
|
||||
ShipmentDistribution=Shipment distribution
|
||||
|
|
@ -93,3 +94,4 @@ TypeContact_shipping_external_SHIPPING=Customer contact for shipping
|
|||
TypeContact_shipping_external_DELIVERY=Customer contact for delivery
|
||||
CloseShipment=Close shipment
|
||||
ConfirmCloseShipment=Confirm close shipment
|
||||
SHIPPING_SELL_EAT_BY_DATE_PRE_SELECT_EARLIEST=Pre-select the batch/serial number with the earliest sell-by/eat-by date when creating a shipment
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ class FormProduct
|
|||
*/
|
||||
public $cache_warehouses = array();
|
||||
/**
|
||||
* @var array<int,array<int,array{id:int,batch:string,entrepot_id:int,entrepot_label:string,qty:float}>>
|
||||
* @var array<int,array<int,array{id:int,batch:string,entrepot_id:int,entrepot_label:string,qty:float,eatbydate:int|string,sellbydate:int|string}>>
|
||||
*/
|
||||
public $cache_lot = array();
|
||||
/**
|
||||
|
|
@ -861,7 +861,16 @@ class FormProduct
|
|||
$label = $arraytypes['entrepot_label'] . ' - ';
|
||||
$label .= $arraytypes['batch'];
|
||||
// Notice: Chrome show 1 line with value and 1 for label. Firefox show only 1 line with label
|
||||
$out .= '<option data-warehouse="'.dol_escape_htmltag($label).'" value="' . $arraytypes['batch'] . '">' . ($conf->browser->name === 'chrome' ? '' : $arraytypes['batch']) . ' (' . $langs->trans('TotalStock') . ': ' . $arraytypes['qty'] . ')</option>';
|
||||
$optionLabel = ($conf->browser->name === 'chrome' ? '' : $arraytypes['batch']);
|
||||
$optionLabel .= ' ('.$langs->trans('TotalStock').': '.$arraytypes['qty'];
|
||||
if (!empty($arraytypes['sellbydate'])) {
|
||||
$optionLabel .= ' - '.$langs->trans('printSellby', $arraytypes['sellbydate']);
|
||||
}
|
||||
if (!empty($arraytypes['eatbydate'])) {
|
||||
$optionLabel .= ' - '.$langs->trans('printEatby', $arraytypes['eatbydate']);
|
||||
}
|
||||
$optionLabel .= ')';
|
||||
$out .= '<option data-warehouse="'.dol_escape_htmltag($label).'" data-eatbydate="'.$arraytypes['eatbydate'].'" data-sellbydate="'.$arraytypes['sellbydate'].'" value="' . $arraytypes['batch'] . '">'.$optionLabel.'</option>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -923,10 +932,22 @@ class FormProduct
|
|||
return $batch_count;
|
||||
}
|
||||
|
||||
$is_eat_by_enabled = !getDolGlobalInt('PRODUCT_DISABLE_EATBY');
|
||||
$is_sell_by_enabled = !getDolGlobalInt('PRODUCT_DISABLE_SELLBY');
|
||||
|
||||
$sql = "SELECT pb.batch, pb.rowid, ps.fk_entrepot, pb.qty, e.ref as label, ps.fk_product";
|
||||
if ($is_eat_by_enabled) {
|
||||
$sql .= ", pl.eatby";
|
||||
}
|
||||
if ($is_sell_by_enabled) {
|
||||
$sql .= ", pl.sellby";
|
||||
}
|
||||
$sql .= " FROM ".$this->db->prefix()."product_batch as pb";
|
||||
$sql .= " LEFT JOIN ".$this->db->prefix()."product_stock as ps on ps.rowid = pb.fk_product_stock";
|
||||
$sql .= " LEFT JOIN ".$this->db->prefix()."entrepot as e on e.rowid = ps.fk_entrepot AND e.entity IN (".getEntity('stock').")";
|
||||
if ($is_eat_by_enabled || $is_sell_by_enabled) {
|
||||
$sql .= " LEFT JOIN ".$this->db->prefix()."product_lot as pl on ps.fk_product = pl.fk_product AND pb.batch = pl.batch";
|
||||
}
|
||||
if (!empty($productIdList)) {
|
||||
$sql .= " WHERE ps.fk_product IN (".$this->db->sanitize($productIdList).")";
|
||||
}
|
||||
|
|
@ -944,6 +965,14 @@ class FormProduct
|
|||
$this->cache_lot[$obj->fk_product][$obj->rowid]['entrepot_id'] = $obj->fk_entrepot;
|
||||
$this->cache_lot[$obj->fk_product][$obj->rowid]['entrepot_label'] = $obj->label;
|
||||
$this->cache_lot[$obj->fk_product][$obj->rowid]['qty'] = $obj->qty;
|
||||
$this->cache_lot[$obj->fk_product][$obj->rowid]['eatbydate'] = '';
|
||||
if (!empty($obj->eatby)) {
|
||||
$this->cache_lot[$obj->fk_product][$obj->rowid]['eatbydate'] = dol_print_date($this->db->jdate($obj->eatby), 'day');
|
||||
}
|
||||
$this->cache_lot[$obj->fk_product][$obj->rowid]['sellbydate'] = '';
|
||||
if (!empty($obj->sellby)) {
|
||||
$this->cache_lot[$obj->fk_product][$obj->rowid]['sellbydate'] = dol_print_date($this->db->jdate($obj->sellby), 'day');
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6443,6 +6443,88 @@ class Product extends CommonObject
|
|||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load stock for components of virtual product (first level only)
|
||||
*
|
||||
* @param string $option '' = Load all stock info, also from closed and internal warehouses, 'nobatch' = do not load batch detail, 'novirtual' = do no load virtual detail
|
||||
* @param int|float $qtyWish [=1] Default quantity wish for the virtual product (1 by default or put qty ordered)
|
||||
* @return int Return integer < 0 if KO, > 0 if OK
|
||||
*/
|
||||
public function loadStockForVirtualProduct($option = '', $qtyWish = 1)
|
||||
{
|
||||
$this->stock_warehouse = array();
|
||||
$error = 0;
|
||||
|
||||
$this->get_sousproduits_arbo();
|
||||
$prods_arbo = $this->get_arbo_each_prod($qtyWish, 1);
|
||||
if (count($prods_arbo) > 0) {
|
||||
$productCachedList = array();
|
||||
$stockByComponentList = array();
|
||||
|
||||
foreach ($prods_arbo as $componentArr) {
|
||||
$componentId = $componentArr['id'];
|
||||
// only component whose manage stock
|
||||
if ($componentArr['incdec'] == 1) {
|
||||
if (!isset($productCachedList[$componentId])) {
|
||||
$componentStatic = new self($this->db);
|
||||
$componentStatic->fetch($componentId);
|
||||
// check if it's a sub-kit
|
||||
$childrenNb = $componentStatic->hasFatherOrChild(1);
|
||||
if ($childrenNb == 0) {
|
||||
$componentStatic->load_stock('nobatch,novirtual'); // Load stock to get true ->stock_reel
|
||||
if (!isset($stockByComponentList[$componentId])) {
|
||||
$stockByComponentList[$componentId] = array(
|
||||
'qty_need' => 0
|
||||
);
|
||||
}
|
||||
$stockByComponentList[$componentId]['qty_need'] += $componentArr['nb_total'];
|
||||
}
|
||||
$productCachedList[$componentId] = $componentStatic;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($stockByComponentList)) {
|
||||
foreach ($stockByComponentList as $componentId => $stockByComponentArr) {
|
||||
if (!isset($productCachedList[$componentId])) {
|
||||
$componentStatic = new self($this->db);
|
||||
$componentStatic->fetch($componentId);
|
||||
$componentStatic->load_stock('nobatch,novirtual'); // Load stock to get true ->stock_reel
|
||||
$productCachedList[$componentId] = $componentStatic;
|
||||
}
|
||||
$component = $productCachedList[$componentId];
|
||||
|
||||
|
||||
if ($component->stock_reel < $stockByComponentArr['qty_need']) {
|
||||
// not enough stock for this component to assemble this virtual product
|
||||
$error++;
|
||||
$this->error = 'Not enough component [id='.$componentId.'] in stock, real='.$component->stock_reel.' and need='.$stockByComponentArr['qty_need'];
|
||||
$this->errors[] = $this->error;
|
||||
dol_syslog(__METHOD__.' : '.$this->error, LOG_ERR);
|
||||
} else {
|
||||
if (!empty($component->stock_warehouse)) {
|
||||
foreach ($component->stock_warehouse as $warehouseId => $warehouseObj) {
|
||||
$kitWarehouseAvailable = new stdClass();
|
||||
$kitWarehouseAvailable->id = $warehouseObj->id;
|
||||
$kitWarehouseAvailable->real = $qtyWish;
|
||||
$this->stock_warehouse[$warehouseId] = $kitWarehouseAvailable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($error) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($error) {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load existing information about a serial
|
||||
|
|
|
|||
|
|
@ -321,7 +321,11 @@ class MouvementStock extends CommonObject
|
|||
// Define if we must make the stock change (If product type is a service or if stock is used also for services)
|
||||
// Only record into stock tables will be disabled by this (the rest like writing into lot table or movement of subproucts are done)
|
||||
$movestock = 0;
|
||||
if ($product->type != Product::TYPE_SERVICE || getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
|
||||
$productChildrenNb = 0;
|
||||
if (getDolGlobalInt('PRODUIT_SOUSPRODUITS')) {
|
||||
$productChildrenNb = $product->hasFatherOrChild(1);
|
||||
}
|
||||
if (($product->type != Product::TYPE_SERVICE || getDolGlobalString('STOCK_SUPPORTS_SERVICES')) && $productChildrenNb == 0) {
|
||||
$movestock = 1;
|
||||
}
|
||||
|
||||
|
|
@ -798,44 +802,35 @@ class MouvementStock extends CommonObject
|
|||
global $langs;
|
||||
|
||||
$error = 0;
|
||||
$pids = array();
|
||||
$pqtys = array();
|
||||
|
||||
$sql = "SELECT fk_product_pere, fk_product_fils, qty";
|
||||
$sql .= " FROM ".$this->db->prefix()."product_association";
|
||||
$sql .= " WHERE fk_product_pere = ".((int) $idProduct);
|
||||
$sql .= " AND incdec = 1";
|
||||
|
||||
dol_syslog(get_class($this)."::_createSubProduct for parent product ".$idProduct, LOG_DEBUG);
|
||||
dol_syslog(__METHOD__.' for parent product '.$idProduct, LOG_DEBUG);
|
||||
$resql = $this->db->query($sql);
|
||||
if ($resql) {
|
||||
$i = 0;
|
||||
// Create movement for each sub-product
|
||||
while ($obj = $this->db->fetch_object($resql)) {
|
||||
$pids[$i] = $obj->fk_product_fils;
|
||||
$pqtys[$i] = $obj->qty;
|
||||
$i++;
|
||||
}
|
||||
$this->db->free($resql);
|
||||
} else {
|
||||
$error = -2;
|
||||
}
|
||||
|
||||
// Create movement for each subproduct
|
||||
foreach ($pids as $key => $value) {
|
||||
if (!$error) {
|
||||
$tmpmove = dol_clone($this, 1);
|
||||
|
||||
$result = $tmpmove->_create($user, $pids[$key], $entrepot_id, ($qty * $pqtys[$key]), $type, 0, $label, $inventorycode, $datem); // This will also call _createSubProduct making this recursive
|
||||
$result = $tmpmove->_create($user, $obj->fk_product_fils, $entrepot_id, ($qty * $obj->qty), $type, 0, $label, $inventorycode, $datem); // This will also call _createSubProduct making this recursive
|
||||
if ($result < 0) {
|
||||
$this->error = $tmpmove->error;
|
||||
$this->errors = array_merge($this->errors, $tmpmove->errors);
|
||||
if ($result == -2) {
|
||||
$this->errors[] = $langs->trans("ErrorNoteAlsoThatSubProductCantBeFollowedByLot");
|
||||
$this->errors[] = $langs->trans('ErrorNoteAlsoThatSubProductCantBeFollowedByLot');
|
||||
}
|
||||
$error = $result;
|
||||
dol_syslog(__METHOD__ . ' Error : ' . $this->errorsToString(), LOG_ERR);
|
||||
break;
|
||||
}
|
||||
unset($tmpmove);
|
||||
}
|
||||
|
||||
$this->db->free($resql);
|
||||
} else {
|
||||
$error = -2;
|
||||
}
|
||||
|
||||
return $error;
|
||||
|
|
@ -872,28 +867,29 @@ class MouvementStock extends CommonObject
|
|||
/**
|
||||
* Increase stock for product and subproducts
|
||||
*
|
||||
* @param User $user Object user
|
||||
* @param int $fk_product Id product
|
||||
* @param int $entrepot_id Warehouse id
|
||||
* @param float $qty Quantity
|
||||
* @param float $price Price
|
||||
* @param string $label Label of stock movement
|
||||
* @param int|'' $eatby eat-by date
|
||||
* @param int|'' $sellby sell-by date
|
||||
* @param string $batch batch number
|
||||
* @param int|'' $datem Force date of movement
|
||||
* @param int $id_product_batch Id product_batch
|
||||
* @param string $inventorycode Inventory code
|
||||
* @param int<0,1> $donotcleanemptylines Do not clean lines that remains in stock table with qty=0 (because we want to have this done by the caller)
|
||||
* @return int Return integer <0 if KO, >0 if OK
|
||||
* @param User $user Object user
|
||||
* @param int $fk_product Id product
|
||||
* @param int $entrepot_id Warehouse id
|
||||
* @param float $qty Quantity
|
||||
* @param float $price Price
|
||||
* @param string $label Label of stock movement
|
||||
* @param int|'' $eatby eat-by date
|
||||
* @param int|'' $sellby sell-by date
|
||||
* @param string $batch batch number
|
||||
* @param int|'' $datem Force date of movement
|
||||
* @param int $id_product_batch Id product_batch
|
||||
* @param string $inventorycode Inventory code
|
||||
* @param int<0,1> $donotcleanemptylines Do not clean lines that remains in stock table with qty=0 (because we want to have this done by the caller)
|
||||
* @param int $disablestockchangeforsubproduct Disable stock change for sub-products of kit (useful only if product is a subproduct)
|
||||
* @return int Return integer <0 if KO, >0 if OK
|
||||
*/
|
||||
public function reception($user, $fk_product, $entrepot_id, $qty, $price = 0, $label = '', $eatby = '', $sellby = '', $batch = '', $datem = '', $id_product_batch = 0, $inventorycode = '', $donotcleanemptylines = 0)
|
||||
public function reception($user, $fk_product, $entrepot_id, $qty, $price = 0, $label = '', $eatby = '', $sellby = '', $batch = '', $datem = '', $id_product_batch = 0, $inventorycode = '', $donotcleanemptylines = 0, $disablestockchangeforsubproduct = 0)
|
||||
{
|
||||
global $conf;
|
||||
|
||||
$skip_batch = empty($conf->productbatch->enabled);
|
||||
|
||||
return $this->_create($user, $fk_product, $entrepot_id, $qty, 3, $price, $label, $inventorycode, $datem, $eatby, $sellby, $batch, $skip_batch, $id_product_batch, 0, $donotcleanemptylines);
|
||||
return $this->_create($user, $fk_product, $entrepot_id, $qty, 3, $price, $label, $inventorycode, $datem, $eatby, $sellby, $batch, $skip_batch, $id_product_batch, $disablestockchangeforsubproduct, $donotcleanemptylines);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -111,13 +111,20 @@ if ($action == 'getProducts' && $user->hasRight('takepos', 'run')) {
|
|||
// Removed properties we don't need
|
||||
$res = array();
|
||||
if (is_array($prods) && count($prods) > 0) {
|
||||
$productChildrenNb = 0;
|
||||
foreach ($prods as $prod) {
|
||||
'@phan-var-force Product $prod';
|
||||
if (getDolGlobalInt('TAKEPOS_PRODUCT_IN_STOCK') == 1) {
|
||||
// remove products without stock
|
||||
$prod->load_stock('nobatch,novirtual');
|
||||
if ($prod->stock_warehouse[getDolGlobalString('CASHDESK_ID_WAREHOUSE'.$_SESSION['takeposterminal'])]->real <= 0) {
|
||||
continue;
|
||||
if (getDolGlobalInt('PRODUIT_SOUSPRODUITS')) {
|
||||
$productChildrenNb = $prod->hasFatherOrChild(1);
|
||||
}
|
||||
// always show virtual products (don't manage stock)
|
||||
if ($productChildrenNb == 0) {
|
||||
// remove products without stock
|
||||
$prod->load_stock('nobatch,novirtual');
|
||||
if ($prod->stock_warehouse[getDolGlobalString('CASHDESK_ID_WAREHOUSE'.$_SESSION['takeposterminal'])]->real <= 0) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
unset($prod->fields);
|
||||
|
|
|
|||
|
|
@ -2065,45 +2065,52 @@ if ($placeid > 0) {
|
|||
}
|
||||
$htmlforlines .= '<td class="right">'.vatrate(price2num($line->remise_percent), true).'</td>';
|
||||
$htmlforlines .= '<td class="right">';
|
||||
$htmlforlines .= $line->qty;
|
||||
if (isModEnabled('stock') && $user->hasRight('stock', 'mouvement', 'lire')) {
|
||||
$constantforkey = 'CASHDESK_ID_WAREHOUSE'.$_SESSION["takeposterminal"];
|
||||
if (getDolGlobalString($constantforkey) && $line->fk_product > 0 && !getDolGlobalString('TAKEPOS_HIDE_STOCK_ON_LINE')) {
|
||||
$sql = "SELECT e.rowid, e.ref, e.lieu, e.fk_parent, e.statut, ps.reel, ps.rowid as product_stock_id, p.pmp";
|
||||
$sql .= " FROM ".MAIN_DB_PREFIX."entrepot as e,";
|
||||
$sql .= " ".MAIN_DB_PREFIX."product_stock as ps";
|
||||
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON p.rowid = ps.fk_product";
|
||||
$sql .= " WHERE ps.reel != 0";
|
||||
$sql .= " AND ps.fk_entrepot = ".((int) getDolGlobalString($constantforkey));
|
||||
$sql .= " AND e.entity IN (".getEntity('stock').")";
|
||||
$sql .= " AND ps.fk_product = ".((int) $line->fk_product);
|
||||
$resql = $db->query($sql);
|
||||
if ($resql) {
|
||||
$stock_real = 0;
|
||||
$obj = $db->fetch_object($resql);
|
||||
if ($obj) {
|
||||
$stock_real = price2num($obj->reel, 'MS');
|
||||
$productChildrenNb = 0;
|
||||
if (getDolGlobalInt('PRODUIT_SOUSPRODUITS')) {
|
||||
if (empty($line->product) || !($line->product->id > 0)) {
|
||||
$line->fetch_product();
|
||||
}
|
||||
$htmlforlines .= $line->qty;
|
||||
$htmlforlines .= ' ';
|
||||
$htmlforlines .= '<span class="opacitylow" title="'.$langs->trans("Stock").' '.price($stock_real, 1, '', 1, 0).'">';
|
||||
$htmlforlines .= '(';
|
||||
if ($line->qty && $line->qty > $stock_real) {
|
||||
$htmlforlines .= '<span style="color: var(--amountremaintopaycolor)">';
|
||||
if (!empty($line->product)) {
|
||||
$productChildrenNb = $line->product->hasFatherOrChild(1);
|
||||
}
|
||||
}
|
||||
if ($productChildrenNb == 0) {
|
||||
$sql = "SELECT e.rowid, e.ref, e.lieu, e.fk_parent, e.statut, ps.reel, ps.rowid as product_stock_id, p.pmp";
|
||||
$sql .= " FROM ".MAIN_DB_PREFIX."entrepot as e,";
|
||||
$sql .= " ".MAIN_DB_PREFIX."product_stock as ps";
|
||||
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON p.rowid = ps.fk_product";
|
||||
$sql .= " WHERE ps.reel != 0";
|
||||
$sql .= " AND ps.fk_entrepot = ".((int) getDolGlobalString($constantforkey));
|
||||
$sql .= " AND e.entity IN (".getEntity('stock').")";
|
||||
$sql .= " AND ps.fk_product = ".((int) $line->fk_product);
|
||||
$resql = $db->query($sql);
|
||||
if ($resql) {
|
||||
$stock_real = 0;
|
||||
$obj = $db->fetch_object($resql);
|
||||
if ($obj) {
|
||||
$stock_real = price2num($obj->reel, 'MS');
|
||||
}
|
||||
$htmlforlines .= ' ';
|
||||
$htmlforlines .= '<span class="opacitylow" title="'.$langs->trans("Stock").' '.price($stock_real, 1, '', 1, 0).'">';
|
||||
$htmlforlines .= '(';
|
||||
if ($line->qty && $line->qty > $stock_real) {
|
||||
$htmlforlines .= '<span style="color: var(--amountremaintopaycolor)">';
|
||||
}
|
||||
$htmlforlines .= img_picto('', 'stock', 'class="pictofixedwidth"').price($stock_real, 1, '', 1, 0);
|
||||
if ($line->qty && $line->qty > $stock_real) {
|
||||
$htmlforlines .= "</span>";
|
||||
}
|
||||
$htmlforlines .= ')';
|
||||
$htmlforlines .= '</span>';
|
||||
} else {
|
||||
dol_print_error($db);
|
||||
}
|
||||
$htmlforlines .= img_picto('', 'stock', 'class="pictofixedwidth"').price($stock_real, 1, '', 1, 0);
|
||||
if ($line->qty && $line->qty > $stock_real) {
|
||||
$htmlforlines .= "</span>";
|
||||
}
|
||||
$htmlforlines .= ')';
|
||||
$htmlforlines .= '</span>';
|
||||
} else {
|
||||
dol_print_error($db);
|
||||
}
|
||||
} else {
|
||||
$htmlforlines .= $line->qty;
|
||||
}
|
||||
} else {
|
||||
$htmlforlines .= $line->qty;
|
||||
}
|
||||
|
||||
$htmlforlines .= '</td>';
|
||||
|
|
|
|||
Loading…
Reference in a new issue