diff --git a/htdocs/core/lib/product.lib.php b/htdocs/core/lib/product.lib.php
index d97b66443de..1b87088a98b 100644
--- a/htdocs/core/lib/product.lib.php
+++ b/htdocs/core/lib/product.lib.php
@@ -357,34 +357,7 @@ function show_stats_for_company($product, $socid)
print '
'.$langs->trans("TotalQuantity").' | ';
print '';
- // MO
- if (!empty($conf->mrp->enabled) && $user->rights->mrp->read)
- {
- $nblines++;
- $ret = $product->load_stats_mo($socid);
- if ($ret < 0) dol_print_error($db);
- $langs->load("orders");
- print '| ';
- print ''.img_object('', 'mrp').' '.$langs->trans("MO").'';
- print ' | ';
- print $form->textwithpicto($product->stats_mo['customers_toconsume'],$langs->trans("ToConsume"));
- print $form->textwithpicto($product->stats_mo['customers_consumed'],$langs->trans("QtyAlreadyConsumed"));
- print $form->textwithpicto($product->stats_mo['customers_toproduce'],$langs->trans("QtyToProduce"));
- print $form->textwithpicto($product->stats_mo['customers_produced'],$langs->trans("QtyAlreadyProduced"));
- print ' | ';
- print $form->textwithpicto($product->stats_mo['nb_toconsume'],$langs->trans("ToConsume"));
- print $form->textwithpicto($product->stats_mo['nb_consumed'],$langs->trans("QtyAlreadyConsumed"));
- print $form->textwithpicto($product->stats_mo['nb_toproduce'],$langs->trans("QtyToProduce"));
- print $form->textwithpicto($product->stats_mo['nb_produced'],$langs->trans("QtyAlreadyProduced"));
- print ' | ';
- print $form->textwithpicto($product->stats_mo['qty_toconsume'],$langs->trans("ToConsume"));
- print $form->textwithpicto($product->stats_mo['qty_consumed'],$langs->trans("QtyAlreadyConsumed"));
- print $form->textwithpicto($product->stats_mo['qty_toproduce'],$langs->trans("QtyToProduce"));
- print $form->textwithpicto($product->stats_mo['qty_produced'],$langs->trans("QtyAlreadyProduced"));
- print ' | ';
- print '
';
- }
- // Customer proposals
+ // Customer proposals
if (!empty($conf->propal->enabled) && $user->rights->propale->lire)
{
$nblines++;
@@ -512,6 +485,61 @@ function show_stats_for_company($product, $socid)
print '';
}
+ // BOM
+ if (!empty($conf->bom->enabled) && $user->rights->bom->read)
+ {
+ $nblines++;
+ $ret = $product->load_stats_bom($socid);
+ if ($ret < 0) {
+ setEventMessage($product->error,'errors');
+ }
+ $langs->load("mrp");
+
+ print '| ';
+ print ''.img_object('', 'mrp').' '.$langs->trans("BOM").'';
+ print ' | ';
+
+ print ' | ';
+ print $form->textwithpicto($product->stats_bom['nb_toproduce'],$langs->trans("QtyToProduce"));
+ print $form->textwithpicto($product->stats_bom['nb_toconsume'],$langs->trans("ToConsume"));
+ print ' | ';
+ print $form->textwithpicto($product->stats_bom['qty_toproduce'],$langs->trans("QtyToProduce"));
+ print $form->textwithpicto($product->stats_bom['qty_toconsume'],$langs->trans("ToConsume"));
+ print ' | ';
+ print '
';
+ }
+
+
+ // MO
+ if (!empty($conf->mrp->enabled) && $user->rights->mrp->read)
+ {
+ $nblines++;
+ $ret = $product->load_stats_mo($socid);
+ if ($ret < 0) {
+ setEventMessage($product->error,'errors');
+ }
+ $langs->load("mrp");
+ print '| ';
+ print ''.img_object('', 'mrp').' '.$langs->trans("MO").'';
+ print ' | ';
+ print $form->textwithpicto($product->stats_mo['customers_toconsume'],$langs->trans("ToConsume"));
+ print $form->textwithpicto($product->stats_mo['customers_consumed'],$langs->trans("QtyAlreadyConsumed"));
+ print $form->textwithpicto($product->stats_mo['customers_toproduce'],$langs->trans("QtyToProduce"));
+ print $form->textwithpicto($product->stats_mo['customers_produced'],$langs->trans("QtyAlreadyProduced"));
+ print ' | ';
+ print $form->textwithpicto($product->stats_mo['nb_toconsume'],$langs->trans("ToConsume"));
+ print $form->textwithpicto($product->stats_mo['nb_consumed'],$langs->trans("QtyAlreadyConsumed"));
+ print $form->textwithpicto($product->stats_mo['nb_toproduce'],$langs->trans("QtyToProduce"));
+ print $form->textwithpicto($product->stats_mo['nb_produced'],$langs->trans("QtyAlreadyProduced"));
+ print ' | ';
+ print $form->textwithpicto($product->stats_mo['qty_toconsume'],$langs->trans("ToConsume"));
+ print $form->textwithpicto($product->stats_mo['qty_consumed'],$langs->trans("QtyAlreadyConsumed"));
+ print $form->textwithpicto($product->stats_mo['qty_toproduce'],$langs->trans("QtyToProduce"));
+ print $form->textwithpicto($product->stats_mo['qty_produced'],$langs->trans("QtyAlreadyProduced"));
+ print ' | ';
+ print '
';
+ }
+
return $nblines++;
}
diff --git a/htdocs/product/class/product.class.php b/htdocs/product/class/product.class.php
index 4197facf313..3e9be60d81e 100644
--- a/htdocs/product/class/product.class.php
+++ b/htdocs/product/class/product.class.php
@@ -2414,6 +2414,73 @@ class Product extends CommonObject
return 1;
}
+ // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
+ /**
+ * Charge tableau des stats OF pour le produit/service
+ *
+ * @param int $socid Id societe
+ * @return integer Tableau des stats dans $this->stats_mo, <0 if ko >0 if ok
+ */
+ public function load_stats_bom($socid = 0) {
+ // phpcs:enable
+ global $user, $hookmanager;
+
+ $error=0;
+
+ $this->stats_bom['nb_toproduce'] = 0;
+ $this->stats_bom['nb_toconsume'] = 0;
+ $this->stats_bom['qty_toproduce'] = 0;
+ $this->stats_bom['qty_toconsume'] = 0;
+
+ $sql = "SELECT COUNT(DISTINCT b.rowid) as nb_toproduce,";
+ $sql .= " b.qty as qty_toproduce";
+ $sql .= " FROM ".MAIN_DB_PREFIX."bom_bom as b";
+ $sql .= " INNER JOIN ".MAIN_DB_PREFIX."bom_bomline as bl ON bl.fk_bom=b.rowid";
+ $sql .= " WHERE ";
+ $sql .= " b.entity IN (".getEntity('bom').")";
+ $sql .= " AND b.fk_product =".$this->id;
+
+ $result = $this->db->query($sql);
+ if ($result) {
+ $obj = $this->db->fetch_object($result);
+ $this->stats_bom['nb_toproduce'] = $obj->nb_toproduce ? $obj->nb_toproduce : 0;
+ $this->stats_bom['qty_toproduce'] = $obj->qty_toproduce ? price2num($obj->qty_toproduce) : 0;
+
+ } else {
+ $this->error = $this->db->error();
+ $error++;
+ }
+
+ $sql = "SELECT COUNT(DISTINCT bl.rowid) as nb_toconsume,";
+ $sql .= " SUM(bl.qty) as qty_toconsume";
+ $sql .= " FROM ".MAIN_DB_PREFIX."bom_bom as b";
+ $sql .= " INNER JOIN ".MAIN_DB_PREFIX."bom_bomline as bl ON bl.fk_bom=b.rowid";
+ $sql .= " WHERE ";
+ $sql .= " b.entity IN (".getEntity('bom').")";
+ $sql .= " AND bl.fk_product =".$this->id;
+
+ $result = $this->db->query($sql);
+ if ($result) {
+ $obj = $this->db->fetch_object($result);
+ $this->stats_bom['nb_toconsume'] = $obj->nb_toconsume ? $obj->nb_toconsume : 0;
+ $this->stats_bom['qty_toconsume'] = $obj->qty_toconsume ? price2num($obj->qty_toconsume) : 0;
+
+ } else {
+ $this->error = $this->db->error();
+ $error++;
+ }
+
+ if (!empty($error)) {
+ return -1;
+ }
+
+ $parameters = array('socid' => $socid);
+ $reshook = $hookmanager->executeHooks('loadStatsCustomerMO', $parameters, $this, $action);
+ if ($reshook > 0) $this->stats_bom = $hookmanager->resArray['stats_bom'];
+
+ return 1;
+ }
+
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Charge tableau des stats propale pour le produit/service
diff --git a/htdocs/product/stats/bom.php b/htdocs/product/stats/bom.php
new file mode 100644
index 00000000000..4fd4586bb17
--- /dev/null
+++ b/htdocs/product/stats/bom.php
@@ -0,0 +1,277 @@
+
+ * Copyright (C) 2004-2009 Laurent Destailleur
+ * Copyright (C) 2005-2009 Regis Houssin
+ *
+ * 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 .
+ */
+
+/**
+ * \file htdocs/product/stats/mo.php
+ * \ingroup product mo
+ * \brief Page of MO referring product
+ */
+
+require '../../main.inc.php';
+require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
+require_once DOL_DOCUMENT_ROOT.'/bom/class/bom.class.php';
+require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
+
+// Load translation files required by the page
+$langs->loadLangs(array('mrp', 'products', 'companies'));
+
+$id = GETPOST('id', 'int');
+$ref = GETPOST('ref', 'alpha');
+
+// Security check
+$fieldvalue = (!empty($id) ? $id : (!empty($ref) ? $ref : ''));
+$fieldtype = (!empty($ref) ? 'ref' : 'rowid');
+if ($user->socid) $socid = $user->socid;
+$result = restrictedArea($user, 'produit|service', $fieldvalue, 'product&product', '', '', $fieldtype);
+
+// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context
+$hookmanager->initHooks(array('productstatscontract'));
+
+$mesg = '';
+$option = '';
+
+// Load variable for pagination
+$limit = GETPOST('limit', 'int') ?GETPOST('limit', 'int') : $conf->liste_limit;
+$sortfield = GETPOST("sortfield", 'alpha');
+$sortorder = GETPOST("sortorder", 'alpha');
+$page = GETPOSTISSET('pageplusone') ? (GETPOST('pageplusone') - 1) : GETPOST("page", 'int');
+if (empty($page) || $page == -1) { $page = 0; } // If $page is not defined, or '' or -1
+$offset = $limit * $page;
+$pageprev = $page - 1;
+$pagenext = $page + 1;
+if (!$sortorder) $sortorder = "DESC";
+if (!$sortfield) $sortfield = "b.date_valid";
+
+
+/*
+ * View
+ */
+
+$form = new Form($db);
+
+if ($id > 0 || !empty($ref))
+{
+ $product = new Product($db);
+ $result = $product->fetch($id, $ref);
+
+ $object = $product;
+
+ $parameters = array('id'=>$id);
+ $reshook = $hookmanager->executeHooks('doActions', $parameters, $product, $action); // Note that $action and $object may have been modified by some hooks
+ if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
+
+ llxHeader("", "", $langs->trans("CardProduct".$product->type));
+
+ if ($result > 0)
+ {
+ $head = product_prepare_head($product);
+ $titre = $langs->trans("CardProduct".$product->type);
+ $picto = ($product->type == Product::TYPE_SERVICE ? 'service' : 'product');
+ dol_fiche_head($head, 'referers', $titre, -1, $picto);
+
+ $reshook = $hookmanager->executeHooks('formObjectOptions', $parameters, $product, $action); // Note that $action and $object may have been modified by hook
+ print $hookmanager->resPrint;
+ if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
+
+ $linkback = ''.$langs->trans("BackToList").'';
+
+ $shownav = 1;
+ if ($user->socid && !in_array('product', explode(',', $conf->global->MAIN_MODULES_FOR_EXTERNAL))) $shownav = 0;
+
+ dol_banner_tab($object, 'ref', $linkback, $shownav, 'ref');
+
+ print '';
+
+ print '
';
+ print '
';
+
+ $nboflines = show_stats_for_company($product, $socid);
+
+ print "
";
+
+ print '
';
+ print '';
+
+ dol_fiche_end();
+
+ $now = dol_now();
+
+ //Calcul total qty and amount for global if full scan list
+ $total_qty_toconsume = 0;
+ $total_qty_toproduce = 0;
+ $bom_data_result=array();
+
+
+ //Qauntity to produce
+ $sql = "SELECT b.rowid as rowid, b.ref, b.status, b.date_valid,";
+ $sql .= " b.qty as qty_toproduce";
+ $sql .= " FROM ".MAIN_DB_PREFIX."bom_bom as b";
+ $sql .= " WHERE ";
+ $sql .= " b.entity IN (".getEntity('bom').")";
+ $sql .= " AND b.fk_product =".$product->id;
+ $sql .= $db->order($sortfield, $sortorder);
+
+ // Count total nb of records
+ $totalofrecords = '';
+ if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST))
+ {
+ $result = $db->query($sql);
+ if ($result) {
+ $totalofrecords = $db->num_rows($result);
+ while ($objp = $db->fetch_object($result)) {
+ $total_qty_toproduce += $objp->qty_toproduce;
+ }
+ } else {
+ dol_print_error($db);
+ }
+ }
+ $sql .= $db->plimit($limit + 1, $offset);
+
+ $result = $db->query($sql);
+ if ($result) {
+ $bomtmp = new BOM($db);
+ $num = $db->num_rows($result);
+ $i = 0;
+ if ($num > 0) {
+ while ($i < min($num, $limit)) {
+ $objp = $db->fetch_object($result);
+ $bomtmp->id = $objp->rowid;
+ $bomtmp->ref = $objp->ref;
+ $bom_data_result[$objp->rowid]['link'] = $bomtmp->getNomUrl(1, 'production');
+ $bom_data_result[$objp->rowid]['qty_toproduce']+=($objp->qty_toproduce > 0 ? $objp->qty_toproduce : 0);
+ $bom_data_result[$objp->rowid]['qty_toconsume']=0;
+ $bom_data_result[$objp->rowid]['date_valid']=dol_print_date($db->jdate($objp->date_valid), 'dayhour');
+ $bom_data_result[$objp->rowid]['status']=$bomtmp->LibStatut($objp->status,5);
+ $i++;
+ }
+ }
+ }else {
+ dol_print_error($db);
+ }
+ $db->free($result);
+
+ //Qauntity to consume
+ $sql = "SELECT b.rowid as rowid, b.ref, b.status, b.date_valid,";
+ $sql .= " SUM(bl.qty) as qty_toconsume";
+ $sql .= " FROM ".MAIN_DB_PREFIX."bom_bom as b";
+ $sql .= " INNER JOIN ".MAIN_DB_PREFIX."bom_bomline as bl ON bl.fk_bom=b.rowid";
+ $sql .= " WHERE ";
+ $sql .= " b.entity IN (".getEntity('bom').")";
+ $sql .= " AND bl.fk_product=".$product->id;
+ $sql .= " GROUP BY b.rowid, b.ref, b.date_valid, b.status";
+ $sql .= $db->order($sortfield, $sortorder);
+
+ // Count total nb of records
+ $totalofrecords = '';
+ if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST))
+ {
+ $result = $db->query($sql);
+ if ($result) {
+ $totalofrecords = $db->num_rows($result);
+ while ($objp = $db->fetch_object($result)) {
+ $total_qty_toconsume += $objp->qty_toconsume;
+ }
+ } else {
+ dol_print_error($db);
+ }
+ }
+ $sql .= $db->plimit($limit + 1, $offset);
+
+ $result = $db->query($sql);
+ if ($result) {
+ $bomtmp = new BOM($db);
+ $num = $db->num_rows($result);
+ $i = 0;
+ if ($num > 0) {
+ while ($i < min($num, $limit)) {
+ $objp = $db->fetch_object($result);
+ $bomtmp->id = $objp->rowid;
+ $bomtmp->ref = $objp->ref;
+
+ if (!array_key_exists($objp->rowid,$bom_data_result)) {
+ $bom_data_result[$objp->rowid]['link'] = $bomtmp->getNomUrl(1, 'production');
+ $bom_data_result[$objp->rowid]['qty_toproduce']=0;
+ $bom_data_result[$objp->rowid]['qty_toconsume']+=($objp->qty_toconsume > 0 ? $objp->qty_toconsume : 0);
+ $bom_data_result[$objp->rowid]['date_valid']=dol_print_date($db->jdate($objp->date_valid), 'dayhour');
+ $bom_data_result[$objp->rowid]['status']=$bomtmp->LibStatut($objp->status,5);
+ } else {
+ $bom_data_result[$objp->rowid]['qty_toconsume']+=($objp->qty_toconsume > 0 ? $objp->qty_toconsume : 0);
+ }
+ $i++;
+ }
+ }
+ }else {
+ dol_print_error($db);
+ }
+ $db->free($result);
+
+
+ if ($limit > 0 && $limit != $conf->liste_limit) $option .= '&limit='.urlencode($limit);
+ if (!empty($id)) $option .= '&id='.$product->id;
+ if (!empty($search_month)) $option .= '&search_month='.urlencode($search_month);
+ if (!empty($search_year)) $option .= '&search_year='.urlencode($search_year);
+
+ print '';
+ }
+ }
+} else {
+ dol_print_error();
+}
+
+// End of page
+llxFooter();
+$db->close();