diff --git a/htdocs/admin/stock.php b/htdocs/admin/stock.php index 98ff43360a8..4e9957fe48d 100644 --- a/htdocs/admin/stock.php +++ b/htdocs/admin/stock.php @@ -52,14 +52,17 @@ if($action) // Mode of stock decrease if ($action == 'STOCK_CALCULATE_ON_BILL' || $action == 'STOCK_CALCULATE_ON_VALIDATE_ORDER' - || $action == 'STOCK_CALCULATE_ON_SHIPMENT') + || $action == 'STOCK_CALCULATE_ON_SHIPMENT' + || $action == 'STOCK_CALCULATE_ON_SHIPMENT_CLOSE') { $res=dolibarr_set_const($db, "STOCK_CALCULATE_ON_BILL", '','chaine',0,'',$conf->entity); $res=dolibarr_set_const($db, "STOCK_CALCULATE_ON_VALIDATE_ORDER", '','chaine',0,'',$conf->entity); $res=dolibarr_set_const($db, "STOCK_CALCULATE_ON_SHIPMENT", '','chaine',0,'',$conf->entity); + $res=dolibarr_set_const($db, "STOCK_CALCULATE_ON_SHIPMENT_CLOSE", '','chaine',0,'',$conf->entity); if ($action == 'STOCK_CALCULATE_ON_BILL') $res=dolibarr_set_const($db, "STOCK_CALCULATE_ON_BILL", GETPOST('STOCK_CALCULATE_ON_BILL','alpha'),'chaine',0,'',$conf->entity); if ($action == 'STOCK_CALCULATE_ON_VALIDATE_ORDER') $res=dolibarr_set_const($db, "STOCK_CALCULATE_ON_VALIDATE_ORDER", GETPOST('STOCK_CALCULATE_ON_VALIDATE_ORDER','alpha'),'chaine',0,'',$conf->entity); if ($action == 'STOCK_CALCULATE_ON_SHIPMENT') $res=dolibarr_set_const($db, "STOCK_CALCULATE_ON_SHIPMENT", GETPOST('STOCK_CALCULATE_ON_SHIPMENT','alpha'),'chaine',0,'',$conf->entity); + if ($action == 'STOCK_CALCULATE_ON_SHIPMENT_CLOSE') $res=dolibarr_set_const($db, "STOCK_CALCULATE_ON_SHIPMENT_CLOSE", GETPOST('STOCK_CALCULATE_ON_SHIPMENT_CLOSE','alpha'),'chaine',0,'',$conf->entity); } // Mode of stock increase if ($action == 'STOCK_CALCULATE_ON_SUPPLIER_BILL' @@ -203,6 +206,26 @@ else print "\n\n"; $found++; +$var=!$var; +print ""; +print ''.$langs->trans("DeStockOnShipmentOnClosing").''; +print ''; +if (! empty($conf->expedition->enabled)) +{ + print "
"; + print ''; + print ""; + print $form->selectyesno("STOCK_CALCULATE_ON_SHIPMENT_CLOSE",$conf->global->STOCK_CALCULATE_ON_SHIPMENT_CLOSE,1,$disabled); + print ''; + print "
\n"; +} +else +{ + print $langs->trans("ModuleMustBeEnabledFirst", $langs->transnoentitiesnoconv("Module80Name")); +} +print "\n\n"; +$found++; + /*if (! $found) { $var=!$var; diff --git a/htdocs/commande/list.php b/htdocs/commande/list.php index 78dedadea43..46ae367b887 100644 --- a/htdocs/commande/list.php +++ b/htdocs/commande/list.php @@ -745,7 +745,7 @@ if ($resql) // stock order and stock order_supplier $stock_order=0; $stock_order_supplier=0; - if (! empty($conf->global->STOCK_CALCULATE_ON_SHIPMENT)) // What about other options ? + if (! empty($conf->global->STOCK_CALCULATE_ON_SHIPMENT) || ! empty($conf->global->STOCK_CALCULATE_ON_SHIPMENT_CLOSE)) // What about other options ? { if (! empty($conf->commande->enabled)) { diff --git a/htdocs/core/class/conf.class.php b/htdocs/core/class/conf.class.php index a127c05bf3c..cfb4ff4f9cf 100644 --- a/htdocs/core/class/conf.class.php +++ b/htdocs/core/class/conf.class.php @@ -405,6 +405,7 @@ class Conf $this->global->STOCK_CALCULATE_ON_BILL=0; $this->global->STOCK_CALCULATE_ON_VALIDATE_ORDER=0; $this->global->STOCK_CALCULATE_ON_SHIPMENT=1; + $this->global->STOCK_CALCULATE_ON_SHIPMENT_CLOSE=0; $this->global->STOCK_CALCULATE_ON_SUPPLIER_BILL=0; $this->global->STOCK_CALCULATE_ON_SUPPLIER_VALIDATE_ORDER=0; $this->global->STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER=1; diff --git a/htdocs/expedition/class/expedition.class.php b/htdocs/expedition/class/expedition.class.php index a938fe82922..1e29a2a36f6 100644 --- a/htdocs/expedition/class/expedition.class.php +++ b/htdocs/expedition/class/expedition.class.php @@ -1800,6 +1800,7 @@ class Expedition extends CommonObject */ function setClosed() { + global $conf,$langs,$user; $error=0; @@ -1813,28 +1814,104 @@ class Expedition extends CommonObject { // TODO: Add option/checkbox to set order billed if 100% of order is shipped $this->statut=2; - - // Call trigger - $result=$this->call_trigger('SHIPPING_CLOSED',$user); - if ($result < 0) { - $error++; + + // If stock increment is done on closing + if (! $error && ! empty($conf->stock->enabled) && ! empty($conf->global->STOCK_CALCULATE_ON_SHIPMENT_CLOSE)) + { + require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php'; + + $langs->load("agenda"); + + // Loop on each product line to add a stock movement + // TODO possibilite d'expedier a partir d'une propale ou autre origine + $sql = "SELECT cd.fk_product, cd.subprice,"; + $sql.= " ed.rowid, ed.qty, ed.fk_entrepot,"; + $sql.= " edb.rowid as edbrowid, edb.eatby, edb.sellby, edb.batch, edb.qty as edbqty, edb.fk_origin_stock"; + $sql.= " FROM ".MAIN_DB_PREFIX."commandedet as cd,"; + $sql.= " ".MAIN_DB_PREFIX."expeditiondet as ed"; + $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."expeditiondet_batch as edb on edb.fk_expeditiondet = ed.rowid"; + $sql.= " WHERE ed.fk_expedition = ".$this->id; + $sql.= " AND cd.rowid = ed.fk_origin_line"; + + dol_syslog(get_class($this)."::valid select details", LOG_DEBUG); + $resql=$this->db->query($sql); + if ($resql) + { + $cpt = $this->db->num_rows($resql); + for ($i = 0; $i < $cpt; $i++) + { + $obj = $this->db->fetch_object($resql); + if (empty($obj->edbrowid)) + { + $qty = $obj->qty; + } + else + { + $qty = $obj->edbqty; + } + if ($qty <= 0) continue; + dol_syslog(get_class($this)."::valid movement index ".$i." ed.rowid=".$obj->rowid." edb.rowid=".$obj->edbrowid); + + $mouvS = new MouvementStock($this->db); + $mouvS->origin = &$this; + + if (empty($obj->edbrowid)) + { + // line without batch detail + + // We decrement stock of product (and sub-products) -> update table llx_product_stock (key of this table is fk_product+fk_entrepot) and add a movement record + $result=$mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->subprice, $langs->trans("ShipmentClassifyClosedInDolibarr",$numref)); + if ($result < 0) { + $this->error = $mouvS->error; + $this->errors = $mouvS->errors; + $error++; break; + } + } + else + { + // line with batch detail + + // We decrement stock of product (and sub-products) -> update table llx_product_stock (key of this table is fk_product+fk_entrepot) and add a movement record + $result=$mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->subprice, $langs->trans("ShipmentClassifyClosedInDolibarr",$numref), '', $obj->eatby, $obj->sellby, $obj->batch, $obj->fk_origin_stock); + if ($result < 0) { + $this->error = $mouvS->error; + $this->errors = $mouvS->errors; + $error++; break; + } + } + } + } + else + { + $this->error=$this->db->lasterror(); + $error++; + } } - } else { - $error++; - $this->errors[]=$this->db->lasterror; - } - - if (empty($error)) { - $this->db->commit(); - return 1; + // Call trigger + if (! $error) + { + $result=$this->call_trigger('SHIPPING_CLOSED',$user); + if ($result < 0) { + $error++; + } + } } else { - $this->db->rollback(); dol_print_error($this->db); - - return -1; + $error++; + } + + if (! $error) + { + $this->db->commit(); + return 1; + } + else + { + $this->db->rollback(); + return -1; } } @@ -1876,8 +1953,6 @@ class Expedition extends CommonObject else { $this->db->rollback(); - dol_print_error($this->db); - return -1; } } @@ -1889,6 +1964,7 @@ class Expedition extends CommonObject */ function reOpen() { + global $conf,$langs,$user; $error=0; @@ -1903,26 +1979,102 @@ class Expedition extends CommonObject $this->statut=1; $this->billed=0; - // Call trigger - $result=$this->call_trigger('SHIPPING_REOPEN',$user); - if ($result < 0) { - $error++; + // If stock increment is done on closing + if (! $error && ! empty($conf->stock->enabled) && ! empty($conf->global->STOCK_CALCULATE_ON_SHIPMENT_CLOSE)) + { + require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php'; + + $langs->load("agenda"); + + // Loop on each product line to add a stock movement + // TODO possibilite d'expedier a partir d'une propale ou autre origine + $sql = "SELECT cd.fk_product, cd.subprice,"; + $sql.= " ed.rowid, ed.qty, ed.fk_entrepot,"; + $sql.= " edb.rowid as edbrowid, edb.eatby, edb.sellby, edb.batch, edb.qty as edbqty, edb.fk_origin_stock"; + $sql.= " FROM ".MAIN_DB_PREFIX."commandedet as cd,"; + $sql.= " ".MAIN_DB_PREFIX."expeditiondet as ed"; + $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."expeditiondet_batch as edb on edb.fk_expeditiondet = ed.rowid"; + $sql.= " WHERE ed.fk_expedition = ".$this->id; + $sql.= " AND cd.rowid = ed.fk_origin_line"; + + dol_syslog(get_class($this)."::valid select details", LOG_DEBUG); + $resql=$this->db->query($sql); + if ($resql) + { + $cpt = $this->db->num_rows($resql); + for ($i = 0; $i < $cpt; $i++) + { + $obj = $this->db->fetch_object($resql); + if (empty($obj->edbrowid)) + { + $qty = $obj->qty; + } + else + { + $qty = $obj->edbqty; + } + if ($qty <= 0) continue; + dol_syslog(get_class($this)."::reopen expedition movement index ".$i." ed.rowid=".$obj->rowid." edb.rowid=".$obj->edbrowid); + + //var_dump($this->lines[$i]); + $mouvS = new MouvementStock($this->db); + $mouvS->origin = &$this; + + if (empty($obj->edbrowid)) + { + // line without batch detail + + // We decrement stock of product (and sub-products) -> update table llx_product_stock (key of this table is fk_product+fk_entrepot) and add a movement record + $result=$mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, -$qty, $obj->subprice, $langs->trans("ShipmentUnClassifyCloseddInDolibarr",$numref)); + if ($result < 0) { + $this->error = $mouvS->error; + $this->errors = $mouvS->errors; + $error++; break; + } + } + else + { + // line with batch detail + + // We decrement stock of product (and sub-products) -> update table llx_product_stock (key of this table is fk_product+fk_entrepot) and add a movement record + $result=$mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, -$qty, $obj->subprice, $langs->trans("ShipmentUnClassifyCloseddInDolibarr",$numref), '', $obj->eatby, $obj->sellby, $obj->batch, $obj->fk_origin_stock); + if ($result < 0) { + $this->error = $mouvS->error; + $this->errors = $mouvS->errors; + $error++; break; + } + } + } + } + else + { + $this->error=$this->db->lasterror(); + $error++; + } } + if (! $error) + { + // Call trigger + $result=$this->call_trigger('SHIPPING_REOPEN',$user); + if ($result < 0) { + $error++; + } + } + } else { $error++; - $this->errors[]=$this->db->lasterror; + $this->errors[]=$this->db->lasterror(); } - - if (empty($error)) { + + if (! $error) + { $this->db->commit(); return 1; } else { $this->db->rollback(); - dol_print_error($this->db); - return -1; } } diff --git a/htdocs/langs/en_US/other.lang b/htdocs/langs/en_US/other.lang index af0aea4c274..c8bde912cdd 100644 --- a/htdocs/langs/en_US/other.lang +++ b/htdocs/langs/en_US/other.lang @@ -231,6 +231,8 @@ MemberResiliatedInDolibarr=Member %s resiliated MemberDeletedInDolibarr=Member %s deleted MemberSubscriptionAddedInDolibarr=Subscription for member %s added ShipmentValidatedInDolibarr=Shipment %s validated +ShipmentClassifyClosedInDolibarr=Shipment %s classify billed +ShipmentUnClassifyCloseddInDolibarr=Shipment %s classify reopened ShipmentDeletedInDolibarr=Shipment %s deleted ##### Export ##### Export=Export diff --git a/htdocs/langs/en_US/stocks.lang b/htdocs/langs/en_US/stocks.lang index 3416d1b1909..6b1a28eae17 100644 --- a/htdocs/langs/en_US/stocks.lang +++ b/htdocs/langs/en_US/stocks.lang @@ -59,6 +59,7 @@ RuleForStockManagementIncrease=Rule for automatic stock management increase (man DeStockOnBill=Decrease real stocks on customers invoices/credit notes validation DeStockOnValidateOrder=Decrease real stocks on customers orders validation DeStockOnShipment=Decrease real stocks on shipping validation +DeStockOnShipmentOnClosing=Decrease real stocks on shipping classification closed ReStockOnBill=Increase real stocks on suppliers invoices/credit notes validation ReStockOnValidateOrder=Increase real stocks on suppliers orders approbation ReStockOnDispatchOrder=Increase real stocks on manual dispatching into warehouses, after supplier order receiving diff --git a/htdocs/product/class/product.class.php b/htdocs/product/class/product.class.php index 50f5cd0c19a..84d11e3ff99 100644 --- a/htdocs/product/class/product.class.php +++ b/htdocs/product/class/product.class.php @@ -3477,7 +3477,7 @@ class Product extends CommonObject } // Stock decrease mode - if (! empty($conf->global->STOCK_CALCULATE_ON_SHIPMENT)) { + if (! empty($conf->global->STOCK_CALCULATE_ON_SHIPMENT) || ! empty($conf->global->STOCK_CALCULATE_ON_SHIPMENT_CLOSE)) { $this->stock_theorique=$this->stock_reel-$stock_commande_client+$stock_sending_client; } if (! empty($conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER)) { diff --git a/htdocs/product/stock/product.php b/htdocs/product/stock/product.php index 1b4c159b1d6..56504432840 100644 --- a/htdocs/product/stock/product.php +++ b/htdocs/product/stock/product.php @@ -496,7 +496,7 @@ if ($id > 0 || $ref) // Real stock $text_stock_options = ''; - $text_stock_options.= (! empty($conf->global->STOCK_CALCULATE_ON_SHIPMENT)?$langs->trans("DeStockOnShipment").'
':''); + $text_stock_options.= (! empty($conf->global->STOCK_CALCULATE_ON_SHIPMENT) || ! empty($conf->global->STOCK_CALCULATE_ON_SHIPMENT_CLOSE)?$langs->trans("DeStockOnShipment").'
':''); $text_stock_options.= (! empty($conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER)?$langs->trans("DeStockOnValidateOrder").'
':''); $text_stock_options.= (! empty($conf->global->STOCK_CALCULATE_ON_BILL)?$langs->trans("DeStockOnBill").'
':''); $text_stock_options.= (! empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_BILL)?$langs->trans("ReStockOnBill").'
':''); diff --git a/htdocs/product/stock/replenish.php b/htdocs/product/stock/replenish.php index 21f492e0c00..aed3f5ed73c 100644 --- a/htdocs/product/stock/replenish.php +++ b/htdocs/product/stock/replenish.php @@ -71,7 +71,9 @@ if (!$sortorder) { // Define virtualdiffersfromphysical $virtualdiffersfromphysical=0; -if (! empty($conf->global->STOCK_CALCULATE_ON_SHIPMENT) || ! empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER)) +if (! empty($conf->global->STOCK_CALCULATE_ON_SHIPMENT) +|| ! empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER) +|| ! empty($conf->global->STOCK_CALCULATE_ON_SHIPMENT_CLOSE) { $virtualdiffersfromphysical=1; // According to increase/decrease stock options, virtual and physical stock may differs. }