2004-10-20 23:06:45 +02:00
< ? php
2019-02-24 23:32:09 +01:00
/* Copyright ( C ) 2001 - 2007 Rodolphe Quiedeville < rodolphe @ quiedeville . org >
2014-10-02 09:30:31 +02:00
* Copyright ( C ) 2004 - 2014 Laurent Destailleur < eldy @ users . sourceforge . net >
2018-10-27 14:43:12 +02:00
* Copyright ( C ) 2005 - 2015 Regis Houssin < regis . houssin @ inodbox . com >
2014-10-02 09:30:31 +02:00
* Copyright ( C ) 2006 Andre Cianfarani < acianfa @ free . fr >
* Copyright ( C ) 2007 - 2011 Jean Heimburger < jean @ tiaris . info >
2018-10-11 15:44:23 +02:00
* Copyright ( C ) 2010 - 2018 Juanjo Menent < jmenent @ 2 byte . es >
2016-03-17 09:40:05 +01:00
* Copyright ( C ) 2012 Cedric Salvador < csalvador @ gpcsolutions . fr >
2014-10-02 09:30:31 +02:00
* Copyright ( C ) 2013 - 2014 Cedric GROSS < c . gross @ kreiz - it . fr >
2016-03-30 11:38:39 +02:00
* Copyright ( C ) 2013 - 2016 Marcos García < marcosgdf @ gmail . com >
2021-03-20 07:42:16 +01:00
* Copyright ( C ) 2011 - 2021 Open - DSI < support @ open - dsi . fr >
2014-10-02 09:30:31 +02:00
* Copyright ( C ) 2014 Henry Florian < florian . henry @ open - concept . pro >
2016-03-17 09:40:05 +01:00
* Copyright ( C ) 2014 - 2016 Philippe Grand < philippe . grand @ atoo - net . com >
* Copyright ( C ) 2014 Ion agorria < ion @ agorria . com >
2018-10-17 10:01:02 +02:00
* Copyright ( C ) 2016 - 2018 Ferran Marcet < fmarcet @ 2 byte . es >
2017-06-09 12:50:56 +02:00
* Copyright ( C ) 2017 Gustavo Novaro
2021-05-20 08:50:15 +02:00
* Copyright ( C ) 2019 - 2021 Frédéric France < frederic . france @ netlogic . fr >
2006-11-30 15:23:38 +01:00
*
2002-04-30 17:52:56 +02:00
* 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
2013-01-16 15:36:08 +01:00
* the Free Software Foundation ; either version 3 of the License , or
2002-04-30 17:52:56 +02:00
* ( 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
2019-09-23 21:55:30 +02:00
* along with this program . If not , see < https :// www . gnu . org / licenses />.
2002-04-30 17:52:56 +02:00
*/
2005-03-01 22:44:32 +01:00
/**
2018-11-06 12:13:16 +01:00
* \file htdocs / product / class / product . class . php
* \ingroup produit
* \brief File of class to manage predefined products or services
2008-07-27 22:47:13 +02:00
*/
2020-11-30 09:04:13 +01:00
require_once DOL_DOCUMENT_ROOT . '/core/lib/product.lib.php' ;
2015-01-31 14:53:14 +01:00
require_once DOL_DOCUMENT_ROOT . '/core/class/commonobject.class.php' ;
2014-03-05 10:30:06 +01:00
require_once DOL_DOCUMENT_ROOT . '/product/class/productbatch.class.php' ;
2016-12-05 18:08:09 +01:00
require_once DOL_DOCUMENT_ROOT . '/product/stock/class/entrepot.class.php' ;
2007-11-05 23:37:41 +01:00
2005-03-01 22:44:32 +01:00
/**
2012-02-22 12:37:39 +01:00
* Class to manage products or services
2008-07-27 22:47:13 +02:00
*/
2007-11-05 23:37:41 +01:00
class Product extends CommonObject
2003-06-22 14:16:11 +02:00
{
2020-09-07 10:18:17 +02:00
/**
* @ var string ID to identify managed object
*/
public $element = 'product' ;
/**
* @ var string Name of table without prefix where object is stored
*/
public $table_element = 'product' ;
/**
2020-12-05 23:53:55 +01:00
* @ var string Field with ID of parent key if this field has a parent
2020-09-07 10:18:17 +02:00
*/
public $fk_element = 'fk_product' ;
/**
* @ var array List of child tables . To test if we can delete object .
*/
2020-12-05 23:53:55 +01:00
protected $childtables = array (
2022-03-26 08:32:20 +01:00
'supplier_proposaldet' => array ( 'name' => 'SupplierProposal' , 'parent' => 'supplier_proposal' , 'parentkey' => 'fk_supplier_proposal' ),
'propaldet' => array ( 'name' => 'Proposal' , 'parent' => 'propal' , 'parentkey' => 'fk_propal' ),
'commandedet' => array ( 'name' => 'Order' , 'parent' => 'commande' , 'parentkey' => 'fk_commande' ),
'facturedet' => array ( 'name' => 'Invoice' , 'parent' => 'facture' , 'parentkey' => 'fk_facture' ),
'contratdet' => array ( 'name' => 'Contract' , 'parent' => 'contrat' , 'parentkey' => 'fk_contrat' ),
'facture_fourn_det' => array ( 'name' => 'SupplierInvoice' , 'parent' => 'facture_fourn' , 'parentkey' => 'fk_facture_fourn' ),
'commande_fournisseurdet' => array ( 'name' => 'SupplierOrder' , 'parent' => 'commande_fournisseur' , 'parentkey' => 'fk_commande' )
2020-12-05 23:53:55 +01:00
);
2020-09-07 10:18:17 +02:00
/**
* 0 = No test on entity , 1 = Test with field entity , 2 = Test with link by societe
*
* @ var int
*/
public $ismultientitymanaged = 1 ;
2020-10-28 22:47:29 +01:00
/**
* @ var string picto
*/
2020-09-07 10:18:17 +02:00
public $picto = 'product' ;
/**
* { @ inheritdoc }
*/
protected $table_ref_field = 'ref' ;
public $regeximgext = '\.gif|\.jpg|\.jpeg|\.png|\.bmp|\.webp|\.xpm|\.xbm' ; // See also into images.lib.php
2021-03-08 16:17:20 +01:00
/**
* @ deprecated
* @ see $label
*/
2020-09-07 10:18:17 +02:00
public $libelle ;
2020-10-28 22:47:29 +01:00
2020-09-07 10:18:17 +02:00
/**
* Product label
*
* @ var string
*/
public $label ;
/**
* Product description
*
* @ var string
*/
public $description ;
/**
* Product other fields PRODUCT_USE_OTHER_FIELD_IN_TRANSLATION
*
* @ var string
*/
public $other ;
/**
* Check TYPE constants
*
* @ var int
*/
public $type = self :: TYPE_PRODUCT ;
/**
* Selling price
*
* @ var float
*/
public $price ; // Price net
/**
* Price with tax
*
* @ var float
*/
public $price_ttc ;
/**
* Minimum price net
*
* @ var float
*/
public $price_min ;
/**
* Minimum price with tax
*
* @ var float
*/
public $price_min_ttc ;
/**
* Base price ( 'TTC' for price including tax or 'HT' for net price )
* @ var string
*/
public $price_base_type ;
//! Arrays for multiprices
public $multiprices = array ();
public $multiprices_ttc = array ();
public $multiprices_base_type = array ();
public $multiprices_min = array ();
public $multiprices_min_ttc = array ();
public $multiprices_tva_tx = array ();
public $multiprices_recuperableonly = array ();
//! Price by quantity arrays
public $price_by_qty ;
public $prices_by_qty = array ();
public $prices_by_qty_id = array ();
public $prices_by_qty_list = array ();
2021-09-08 20:59:56 +02:00
//! Array for multilangs
public $multilangs = array ();
2020-09-07 10:18:17 +02:00
//! Default VAT code for product (link to code into llx_c_tva but without foreign keys)
public $default_vat_code ;
//! Default VAT rate of product
public $tva_tx ;
//! French VAT NPR (0 or 1)
public $tva_npr = 0 ;
//! Other local taxes
public $localtax1_tx ;
public $localtax2_tx ;
public $localtax1_type ;
public $localtax2_type ;
2021-05-13 19:07:47 +02:00
public $lifetime ;
public $qc_frequency ;
2020-09-07 10:18:17 +02:00
/**
* Stock real
*
* @ var int
*/
public $stock_reel = 0 ;
/**
* Stock virtual
*
* @ var int
*/
public $stock_theorique ;
/**
* Cost price
*
* @ var float
*/
public $cost_price ;
//! Average price value for product entry into stock (PMP)
public $pmp ;
/**
* Stock alert
*
* @ var float
*/
public $seuil_stock_alerte = 0 ;
/**
* Ask for replenishment when $desiredstock < $stock_reel
*/
public $desiredstock = 0 ;
/*
2021-06-16 20:17:21 +02:00
* Service expiration
*/
2020-09-07 10:18:17 +02:00
public $duration_value ;
/**
* Exoiration unit
*/
public $duration_unit ;
/**
* Status indicates whether the product is on sale '1' or not '0'
*
* @ var int
*/
public $status = 0 ;
/**
* Status indicate whether the product is available for purchase '1' or not '0'
*
* @ var int
*/
public $status_buy = 0 ;
/**
* Status indicates whether the product is a finished product '1' or a raw material '0'
*
* @ var int
*/
public $finished ;
2020-11-26 11:59:00 +01:00
/**
* fk_default_bom indicates the default bom
*
* @ var int
*/
public $fk_default_bom ;
2020-09-07 10:18:17 +02:00
/**
2022-03-01 16:41:07 +01:00
* We must manage lot / batch number , sell - by date and so on : '0' : no , '1' : yes , ' 2 " : yes with unique serial number
2020-09-07 10:18:17 +02:00
*
* @ var int
*/
public $status_batch = 0 ;
2021-04-08 18:03:46 +02:00
/**
* If allowed , we can edit batch or serial number mask for each product
*
* @ var string
*/
public $batch_mask = '' ;
2020-09-07 10:18:17 +02:00
/**
* Customs code
*
* @ var string
*/
public $customcode ;
/**
* Product URL
*
* @ var string
*/
public $url ;
//! Metric of products
public $weight ;
2020-12-15 11:39:18 +01:00
public $weight_units ; // scale -3, 0, 3, 6
2020-09-07 10:18:17 +02:00
public $length ;
2020-12-15 11:39:18 +01:00
public $length_units ; // scale -3, 0, 3, 6
2020-09-07 10:18:17 +02:00
public $width ;
2020-12-15 11:39:18 +01:00
public $width_units ; // scale -3, 0, 3, 6
2020-09-07 10:18:17 +02:00
public $height ;
2020-12-15 11:39:18 +01:00
public $height_units ; // scale -3, 0, 3, 6
2020-09-07 10:18:17 +02:00
public $surface ;
2020-12-15 11:39:18 +01:00
public $surface_units ; // scale -3, 0, 3, 6
2020-09-07 10:18:17 +02:00
public $volume ;
2020-12-15 11:39:18 +01:00
public $volume_units ; // scale -3, 0, 3, 6
2020-09-07 10:18:17 +02:00
public $net_measure ;
2020-12-15 11:39:18 +01:00
public $net_measure_units ; // scale -3, 0, 3, 6
2020-09-07 10:18:17 +02:00
public $accountancy_code_sell ;
public $accountancy_code_sell_intra ;
public $accountancy_code_sell_export ;
public $accountancy_code_buy ;
public $accountancy_code_buy_intra ;
public $accountancy_code_buy_export ;
/**
* Main Barcode value
*
* @ var string
*/
public $barcode ;
/**
* Main Barcode type ID
*
* @ var int
*/
public $barcode_type ;
/**
* Main Barcode type code
*
* @ var string
*/
public $barcode_type_code ;
public $stats_propale = array ();
public $stats_commande = array ();
public $stats_contrat = array ();
public $stats_facture = array ();
public $stats_commande_fournisseur = array ();
public $stats_reception = array ();
public $stats_mrptoconsume = array ();
public $stats_mrptoproduce = array ();
//! Size of image
public $imgWidth ;
public $imgHeight ;
/**
* @ var integer | string date_creation
*/
public $date_creation ;
/**
* @ var integer | string date_modification
*/
public $date_modification ;
//! Id du fournisseur
public $product_fourn_id ;
//! Product ID already linked to a reference supplier
public $product_id_already_linked ;
public $nbphoto = 0 ;
//! Contains detail of stock of product into each warehouse
public $stock_warehouse = array ();
public $oldcopy ;
2021-05-20 08:50:15 +02:00
/**
* @ var int Default warehouse Id
*/
2020-09-07 10:18:17 +02:00
public $fk_default_warehouse ;
/**
* @ var int ID
*/
public $fk_price_expression ;
/* To store supplier price found */
public $fourn_pu ;
public $fourn_price_base_type ;
public $fourn_socid ;
/**
* @ deprecated
* @ see $ref_supplier
*/
public $ref_fourn ;
2021-05-20 08:50:15 +02:00
/**
* @ var string ref supplier
*/
2020-09-07 10:18:17 +02:00
public $ref_supplier ;
/**
* Unit code ( 'km' , 'm' , 'l' , 'p' , ... )
*
* @ var string
*/
public $fk_unit ;
/**
* Price is generated using multiprice rules
*
* @ var int
*/
public $price_autogen = 0 ;
2020-10-27 19:44:15 +01:00
/**
* Array with list of supplier prices of product
*
* @ var array
*/
public $supplierprices ;
2021-03-08 16:17:20 +01:00
/**
* Property set to save result of isObjectUsed () . Used for example by Product API .
*
* @ var boolean
*/
public $is_object_used ;
2021-07-19 14:03:03 +02:00
/**
*
*
*
*/
public $mandatory_period ;
2021-06-13 13:29:15 +02:00
/**
* 'type' if the field format ( 'integer' , 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter]]' , 'varchar(x)' , 'double(24,8)' , 'real' , 'price' , 'text' , 'html' , 'date' , 'datetime' , 'timestamp' , 'duration' , 'mail' , 'phone' , 'url' , 'password' )
* Note : Filter can be a string like " (t.ref:like:'SO-%') or (t.date_creation:<:'20160101') or (t.nature:is:NULL) "
* 'label' the translation key .
* 'enabled' is a condition when the field must be managed ( Example : 1 or ' $conf -> global -> MY_SETUP_PARAM )
* 'position' is the sort order of field .
* 'notnull' is set to 1 if not null in database . Set to - 1 if we must set data to null if empty ( '' or 0 ) .
* 'visible' says if field is visible in list ( Examples : 0 = Not visible , 1 = Visible on list and create / update / view forms , 2 = Visible on list only , 3 = Visible on create / update / view form only ( not list ), 4 = Visible on list and update / view form only ( not create ) . 5 = Visible on list and view only ( not create / not update ) . Using a negative value means field is not shown by default on list but can be selected for viewing )
* 'noteditable' says if field is not editable ( 1 or 0 )
* 'default' is a default value for creation ( can still be overwrote by the Setup of Default Values if field is editable in creation form ) . Note : If default is set to '(PROV)' and field is 'ref' , the default value will be set to '(PROVid)' where id is rowid when a new record is created .
* 'index' if we want an index in database .
* 'foreignkey' => 'tablename.field' if the field is a foreign key ( it is recommanded to name the field fk_ ... ) .
* 'searchall' is 1 if we want to search in this field when making a search from the quick search button .
* 'isameasure' must be set to 1 if you want to have a total on list for this field . Field type must be summable like integer or double ( 24 , 8 ) .
* 'css' is the CSS style to use on field . For example : 'maxwidth200'
* 'help' is a string visible as a tooltip on field
* 'showoncombobox' if value of the field must be visible into the label of the combobox that list record
* 'disabled' is 1 if we want to have the field locked by a 'disabled' attribute . In most cases , this is never set into the definition of $fields into class , but is set dynamically by some part of code .
* 'arrayofkeyval' to set list of value if type is a list of predefined values . For example : array ( " 0 " => " Draft " , " 1 " => " Active " , " -1 " => " Cancel " )
* 'autofocusoncreate' to have field having the focus on a create form . Only 1 field should have this property set to 1.
* 'comment' is not used . You can store here any text of your choice . It is not used by application .
*
* Note : To have value dynamic , you can set value to 0 in definition and edit the value on the fly into the constructor .
*/
2020-10-28 22:47:29 +01:00
/**
* @ var array fields of object product
*/
2020-09-07 10:18:17 +02:00
public $fields = array (
'rowid' => array ( 'type' => 'integer' , 'label' => 'TechnicalID' , 'enabled' => 1 , 'visible' =>- 2 , 'notnull' => 1 , 'index' => 1 , 'position' => 1 , 'comment' => 'Id' ),
'ref' => array ( 'type' => 'varchar(128)' , 'label' => 'Ref' , 'enabled' => 1 , 'visible' => 1 , 'notnull' => 1 , 'showoncombobox' => 1 , 'index' => 1 , 'position' => 10 , 'searchall' => 1 , 'comment' => 'Reference of object' ),
2021-03-08 18:56:32 +01:00
'entity' => array ( 'type' => 'integer' , 'label' => 'Entity' , 'enabled' => 1 , 'visible' => 0 , 'default' => 1 , 'notnull' => 1 , 'index' => 1 , 'position' => 5 ),
2021-06-05 15:14:10 +02:00
'label' => array ( 'type' => 'varchar(255)' , 'label' => 'Label' , 'enabled' => 1 , 'visible' => 1 , 'notnull' => 1 , 'showoncombobox' => 2 , 'position' => 15 ),
'barcode' => array ( 'type' => 'varchar(255)' , 'label' => 'Barcode' , 'enabled' => '!empty($conf->barcode->enabled)' , 'position' => 20 , 'visible' =>- 1 , 'showoncombobox' => 3 ),
2021-03-08 18:56:32 +01:00
'fk_barcode_type' => array ( 'type' => 'integer' , 'label' => 'BarcodeType' , 'enabled' => '1' , 'position' => 21 , 'notnull' => 0 , 'visible' =>- 1 ,),
2020-09-07 10:18:17 +02:00
'note_public' => array ( 'type' => 'html' , 'label' => 'NotePublic' , 'enabled' => 1 , 'visible' => 0 , 'position' => 61 ),
'note' => array ( 'type' => 'html' , 'label' => 'NotePrivate' , 'enabled' => 1 , 'visible' => 0 , 'position' => 62 ),
'datec' => array ( 'type' => 'datetime' , 'label' => 'DateCreation' , 'enabled' => 1 , 'visible' =>- 2 , 'notnull' => 1 , 'position' => 500 ),
'tms' => array ( 'type' => 'timestamp' , 'label' => 'DateModification' , 'enabled' => 1 , 'visible' =>- 2 , 'notnull' => 1 , 'position' => 501 ),
//'date_valid' =>array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>1, 'visible'=>-2, 'position'=>502),
'fk_user_author' => array ( 'type' => 'integer' , 'label' => 'UserAuthor' , 'enabled' => 1 , 'visible' =>- 2 , 'notnull' => 1 , 'position' => 510 , 'foreignkey' => 'llx_user.rowid' ),
'fk_user_modif' => array ( 'type' => 'integer' , 'label' => 'UserModif' , 'enabled' => 1 , 'visible' =>- 2 , 'notnull' =>- 1 , 'position' => 511 ),
//'fk_user_valid' =>array('type'=>'integer', 'label'=>'UserValidation', 'enabled'=>1, 'visible'=>-1, 'position'=>512),
2021-03-08 18:56:32 +01:00
'localtax1_tx' => array ( 'type' => 'double(6,3)' , 'label' => 'Localtax1tx' , 'enabled' => '1' , 'position' => 150 , 'notnull' => 0 , 'visible' =>- 1 ,),
'localtax1_type' => array ( 'type' => 'varchar(10)' , 'label' => 'Localtax1type' , 'enabled' => '1' , 'position' => 155 , 'notnull' => 1 , 'visible' =>- 1 ,),
'localtax2_tx' => array ( 'type' => 'double(6,3)' , 'label' => 'Localtax2tx' , 'enabled' => '1' , 'position' => 160 , 'notnull' => 0 , 'visible' =>- 1 ,),
'localtax2_type' => array ( 'type' => 'varchar(10)' , 'label' => 'Localtax2type' , 'enabled' => '1' , 'position' => 165 , 'notnull' => 1 , 'visible' =>- 1 ,),
2020-09-07 10:18:17 +02:00
'import_key' => array ( 'type' => 'varchar(14)' , 'label' => 'ImportId' , 'enabled' => 1 , 'visible' =>- 2 , 'notnull' =>- 1 , 'index' => 0 , 'position' => 1000 ),
//'tosell' =>array('type'=>'integer', 'label'=>'Status', 'enabled'=>1, 'visible'=>1, 'notnull'=>1, 'default'=>0, 'index'=>1, 'position'=>1000, 'arrayofkeyval'=>array(0=>'Draft', 1=>'Active', -1=>'Cancel')),
//'tobuy' =>array('type'=>'integer', 'label'=>'Status', 'enabled'=>1, 'visible'=>1, 'notnull'=>1, 'default'=>0, 'index'=>1, 'position'=>1000, 'arrayofkeyval'=>array(0=>'Draft', 1=>'Active', -1=>'Cancel')),
2021-11-23 21:38:29 +01:00
'mandatory_period' => array ( 'type' => 'integer' , 'label' => 'mandatory_period' , 'enabled' => 1 , 'visible' => 1 , 'notnull' => 1 , 'default' => 0 , 'index' => 1 , 'position' => 1000 ),
2020-09-07 10:18:17 +02:00
);
/**
* Regular product
*/
const TYPE_PRODUCT = 0 ;
/**
* Service
*/
const TYPE_SERVICE = 1 ;
/**
* Advanced feature : assembly kit
*/
const TYPE_ASSEMBLYKIT = 2 ;
/**
* Advanced feature : stock kit
*/
const TYPE_STOCKKIT = 3 ;
/**
* Constructor
*
* @ param DoliDB $db Database handler
*/
public function __construct ( $db )
{
$this -> db = $db ;
$this -> canvas = '' ;
}
/**
* Check that ref and label are ok
*
* @ return int > 1 if OK , <= 0 if KO
*/
public function check ()
{
$this -> ref = dol_sanitizeFileName ( stripslashes ( $this -> ref ));
$err = 0 ;
if ( dol_strlen ( trim ( $this -> ref )) == 0 ) {
$err ++ ;
}
if ( dol_strlen ( trim ( $this -> label )) == 0 ) {
$err ++ ;
}
if ( $err > 0 ) {
return 0 ;
} else {
return 1 ;
}
}
/**
* Insert product into database
*
* @ param User $user User making insert
* @ param int $notrigger Disable triggers
* @ return int Id of product / service if OK , < 0 if KO
*/
public function create ( $user , $notrigger = 0 )
{
global $conf , $langs ;
2021-06-16 20:17:21 +02:00
$error = 0 ;
2020-09-07 10:18:17 +02:00
// Clean parameters
$this -> ref = dol_sanitizeFileName ( dol_string_nospecial ( trim ( $this -> ref )));
$this -> label = trim ( $this -> label );
$this -> price_ttc = price2num ( $this -> price_ttc );
$this -> price = price2num ( $this -> price );
$this -> price_min_ttc = price2num ( $this -> price_min_ttc );
$this -> price_min = price2num ( $this -> price_min );
if ( empty ( $this -> tva_tx )) {
$this -> tva_tx = 0 ;
}
if ( empty ( $this -> tva_npr )) {
$this -> tva_npr = 0 ;
}
//Local taxes
if ( empty ( $this -> localtax1_tx )) {
$this -> localtax1_tx = 0 ;
}
if ( empty ( $this -> localtax2_tx )) {
$this -> localtax2_tx = 0 ;
}
if ( empty ( $this -> localtax1_type )) {
$this -> localtax1_type = '0' ;
}
if ( empty ( $this -> localtax2_type )) {
$this -> localtax2_type = '0' ;
}
if ( empty ( $this -> price )) {
$this -> price = 0 ;
}
if ( empty ( $this -> price_min )) {
$this -> price_min = 0 ;
}
// Price by quantity
if ( empty ( $this -> price_by_qty )) {
$this -> price_by_qty = 0 ;
}
if ( empty ( $this -> status )) {
$this -> status = 0 ;
}
if ( empty ( $this -> status_buy )) {
$this -> status_buy = 0 ;
}
$price_ht = 0 ;
$price_ttc = 0 ;
$price_min_ht = 0 ;
$price_min_ttc = 0 ;
//
if ( $this -> price_base_type == 'TTC' && $this -> price_ttc > 0 ) {
$price_ttc = price2num ( $this -> price_ttc , 'MU' );
$price_ht = price2num ( $this -> price_ttc / ( 1 + ( $this -> tva_tx / 100 )), 'MU' );
}
//
if ( $this -> price_base_type != 'TTC' && $this -> price > 0 ) {
$price_ht = price2num ( $this -> price , 'MU' );
$price_ttc = price2num ( $this -> price * ( 1 + ( $this -> tva_tx / 100 )), 'MU' );
}
//
if (( $this -> price_min_ttc > 0 ) && ( $this -> price_base_type == 'TTC' )) {
$price_min_ttc = price2num ( $this -> price_min_ttc , 'MU' );
$price_min_ht = price2num ( $this -> price_min_ttc / ( 1 + ( $this -> tva_tx / 100 )), 'MU' );
}
//
if (( $this -> price_min > 0 ) && ( $this -> price_base_type != 'TTC' )) {
$price_min_ht = price2num ( $this -> price_min , 'MU' );
$price_min_ttc = price2num ( $this -> price_min * ( 1 + ( $this -> tva_tx / 100 )), 'MU' );
}
$this -> accountancy_code_buy = trim ( $this -> accountancy_code_buy );
$this -> accountancy_code_buy_intra = trim ( $this -> accountancy_code_buy_intra );
$this -> accountancy_code_buy_export = trim ( $this -> accountancy_code_buy_export );
$this -> accountancy_code_sell = trim ( $this -> accountancy_code_sell );
$this -> accountancy_code_sell_intra = trim ( $this -> accountancy_code_sell_intra );
$this -> accountancy_code_sell_export = trim ( $this -> accountancy_code_sell_export );
// Barcode value
$this -> barcode = trim ( $this -> barcode );
2021-07-20 16:04:12 +02:00
$this -> mandatory_period = empty ( $this -> mandatory_period ) ? 0 : $this -> mandatory_period ;
2020-09-07 10:18:17 +02:00
// Check parameters
if ( empty ( $this -> label )) {
$this -> error = 'ErrorMandatoryParametersNotProvided' ;
return - 1 ;
}
if ( empty ( $this -> ref ) || $this -> ref == 'auto' ) {
// Load object modCodeProduct
$module = ( ! empty ( $conf -> global -> PRODUCT_CODEPRODUCT_ADDON ) ? $conf -> global -> PRODUCT_CODEPRODUCT_ADDON : 'mod_codeproduct_leopard' );
2021-02-26 14:25:17 +01:00
if ( $module != 'mod_codeproduct_leopard' ) { // Do not load module file for leopard
2020-09-07 10:18:17 +02:00
if ( substr ( $module , 0 , 16 ) == 'mod_codeproduct_' && substr ( $module , - 3 ) == 'php' ) {
$module = substr ( $module , 0 , dol_strlen ( $module ) - 4 );
}
dol_include_once ( '/core/modules/product/' . $module . '.php' );
$modCodeProduct = new $module ;
if ( ! empty ( $modCodeProduct -> code_auto )) {
$this -> ref = $modCodeProduct -> getNextValue ( $this , $this -> type );
}
unset ( $modCodeProduct );
}
if ( empty ( $this -> ref )) {
$this -> error = 'ProductModuleNotSetupForAutoRef' ;
return - 2 ;
}
}
dol_syslog ( get_class ( $this ) . " ::create ref= " . $this -> ref . " price= " . $this -> price . " price_ttc= " . $this -> price_ttc . " tva_tx= " . $this -> tva_tx . " price_base_type= " . $this -> price_base_type , LOG_DEBUG );
$now = dol_now ();
$this -> db -> begin ();
// For automatic creation during create action (not used by Dolibarr GUI, can be used by scripts)
if ( $this -> barcode == - 1 ) {
$this -> barcode = $this -> get_barcode ( $this , $this -> barcode_type_code );
}
// Check more parameters
// If error, this->errors[] is filled
$result = $this -> verify ();
if ( $result >= 0 ) {
$sql = " SELECT count(*) as nb " ;
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " product " ;
2020-09-07 10:18:17 +02:00
$sql .= " WHERE entity IN ( " . getEntity ( 'product' ) . " ) " ;
$sql .= " AND ref = ' " . $this -> db -> escape ( $this -> ref ) . " ' " ;
$result = $this -> db -> query ( $sql );
if ( $result ) {
$obj = $this -> db -> fetch_object ( $result );
if ( $obj -> nb == 0 ) {
// Produit non deja existant
2022-01-27 10:12:10 +01:00
$sql = " INSERT INTO " . $this -> db -> prefix () . " product ( " ;
2020-09-07 10:18:17 +02:00
$sql .= " datec " ;
$sql .= " , entity " ;
$sql .= " , ref " ;
$sql .= " , ref_ext " ;
$sql .= " , price_min " ;
$sql .= " , price_min_ttc " ;
$sql .= " , label " ;
$sql .= " , fk_user_author " ;
$sql .= " , fk_product_type " ;
$sql .= " , price " ;
$sql .= " , price_ttc " ;
$sql .= " , price_base_type " ;
$sql .= " , tobuy " ;
$sql .= " , tosell " ;
2021-03-27 05:23:20 +01:00
if ( empty ( $conf -> global -> MAIN_PRODUCT_PERENTITY_SHARED )) {
2021-03-20 07:42:16 +01:00
$sql .= " , accountancy_code_buy " ;
$sql .= " , accountancy_code_buy_intra " ;
$sql .= " , accountancy_code_buy_export " ;
$sql .= " , accountancy_code_sell " ;
$sql .= " , accountancy_code_sell_intra " ;
$sql .= " , accountancy_code_sell_export " ;
}
2020-09-07 10:18:17 +02:00
$sql .= " , canvas " ;
$sql .= " , finished " ;
$sql .= " , tobatch " ;
2021-04-09 13:40:37 +02:00
$sql .= " , batch_mask " ;
2020-09-07 10:18:17 +02:00
$sql .= " , fk_unit " ;
2021-07-20 16:04:12 +02:00
$sql .= " , mandatory_period " ;
2020-09-07 10:18:17 +02:00
$sql .= " ) VALUES ( " ;
$sql .= " ' " . $this -> db -> idate ( $now ) . " ' " ;
2021-09-03 21:25:17 +02:00
$sql .= " , " . (( int ) $conf -> entity );
2020-09-07 10:18:17 +02:00
$sql .= " , ' " . $this -> db -> escape ( $this -> ref ) . " ' " ;
$sql .= " , " . ( ! empty ( $this -> ref_ext ) ? " ' " . $this -> db -> escape ( $this -> ref_ext ) . " ' " : " null " );
$sql .= " , " . price2num ( $price_min_ht );
$sql .= " , " . price2num ( $price_min_ttc );
$sql .= " , " . ( ! empty ( $this -> label ) ? " ' " . $this -> db -> escape ( $this -> label ) . " ' " : " null " );
2021-09-03 21:25:17 +02:00
$sql .= " , " . (( int ) $user -> id );
$sql .= " , " . (( int ) $this -> type );
$sql .= " , " . price2num ( $price_ht , 'MT' );
$sql .= " , " . price2num ( $price_ttc , 'MT' );
2020-09-07 10:18:17 +02:00
$sql .= " , ' " . $this -> db -> escape ( $this -> price_base_type ) . " ' " ;
2021-09-03 21:25:17 +02:00
$sql .= " , " . (( int ) $this -> status );
$sql .= " , " . (( int ) $this -> status_buy );
2021-03-27 05:23:20 +01:00
if ( empty ( $conf -> global -> MAIN_PRODUCT_PERENTITY_SHARED )) {
2021-03-20 07:42:16 +01:00
$sql .= " , ' " . $this -> db -> escape ( $this -> accountancy_code_buy ) . " ' " ;
$sql .= " , ' " . $this -> db -> escape ( $this -> accountancy_code_buy_intra ) . " ' " ;
$sql .= " , ' " . $this -> db -> escape ( $this -> accountancy_code_buy_export ) . " ' " ;
$sql .= " , ' " . $this -> db -> escape ( $this -> accountancy_code_sell ) . " ' " ;
$sql .= " , ' " . $this -> db -> escape ( $this -> accountancy_code_sell_intra ) . " ' " ;
$sql .= " , ' " . $this -> db -> escape ( $this -> accountancy_code_sell_export ) . " ' " ;
}
2020-09-07 10:18:17 +02:00
$sql .= " , ' " . $this -> db -> escape ( $this -> canvas ) . " ' " ;
2021-09-03 21:25:17 +02:00
$sql .= " , " . (( ! isset ( $this -> finished ) || $this -> finished < 0 || $this -> finished == '' ) ? 'NULL' : ( int ) $this -> finished );
$sql .= " , " . (( empty ( $this -> status_batch ) || $this -> status_batch < 0 ) ? '0' : (( int ) $this -> status_batch ));
2021-04-08 18:03:46 +02:00
$sql .= " , ' " . $this -> db -> escape ( $this -> batch_mask ) . " ' " ;
2021-09-03 21:25:17 +02:00
$sql .= " , " . ( $this -> fk_unit > 0 ? (( int ) $this -> fk_unit ) : 'NULL' );
2021-07-20 16:17:17 +02:00
$sql .= " , ' " . $this -> db -> escape ( $this -> mandatory_period ) . " ' " ;
2020-09-07 10:18:17 +02:00
$sql .= " ) " ;
dol_syslog ( get_class ( $this ) . " ::Create " , LOG_DEBUG );
$result = $this -> db -> query ( $sql );
if ( $result ) {
2022-01-27 10:12:10 +01:00
$id = $this -> db -> last_insert_id ( $this -> db -> prefix () . " product " );
2020-09-07 10:18:17 +02:00
if ( $id > 0 ) {
$this -> id = $id ;
$this -> price = $price_ht ;
$this -> price_ttc = $price_ttc ;
$this -> price_min = $price_min_ht ;
$this -> price_min_ttc = $price_min_ttc ;
$result = $this -> _log_price ( $user );
if ( $result > 0 ) {
if ( $this -> update ( $id , $user , true , 'add' ) <= 0 ) {
$error ++ ;
}
} else {
$error ++ ;
$this -> error = $this -> db -> lasterror ();
}
2021-03-20 07:42:16 +01:00
// update accountancy for this entity
2021-03-27 05:23:20 +01:00
if ( ! $error && ! empty ( $conf -> global -> MAIN_PRODUCT_PERENTITY_SHARED )) {
2022-01-27 10:12:10 +01:00
$this -> db -> query ( " DELETE FROM " . $this -> db -> prefix () . " product_perentity WHERE fk_product = " . (( int ) $this -> id ) . " AND entity = " . (( int ) $conf -> entity ));
2021-04-14 04:45:17 +02:00
2022-01-27 10:12:10 +01:00
$sql = " INSERT INTO " . $this -> db -> prefix () . " product_perentity ( " ;
2021-03-20 07:42:16 +01:00
$sql .= " fk_product " ;
$sql .= " , entity " ;
$sql .= " , accountancy_code_buy " ;
$sql .= " , accountancy_code_buy_intra " ;
$sql .= " , accountancy_code_buy_export " ;
$sql .= " , accountancy_code_sell " ;
$sql .= " , accountancy_code_sell_intra " ;
$sql .= " , accountancy_code_sell_export " ;
$sql .= " ) VALUES ( " ;
$sql .= $this -> id ;
$sql .= " , " . $conf -> entity ;
$sql .= " , ' " . $this -> db -> escape ( $this -> accountancy_code_buy ) . " ' " ;
$sql .= " , ' " . $this -> db -> escape ( $this -> accountancy_code_buy_intra ) . " ' " ;
$sql .= " , ' " . $this -> db -> escape ( $this -> accountancy_code_buy_export ) . " ' " ;
$sql .= " , ' " . $this -> db -> escape ( $this -> accountancy_code_sell ) . " ' " ;
$sql .= " , ' " . $this -> db -> escape ( $this -> accountancy_code_sell_intra ) . " ' " ;
$sql .= " , ' " . $this -> db -> escape ( $this -> accountancy_code_sell_export ) . " ' " ;
$sql .= " ) " ;
$result = $this -> db -> query ( $sql );
if ( ! $result ) {
$error ++ ;
$this -> error = 'ErrorFailedToInsertAccountancyForEntity' ;
}
}
2020-09-07 10:18:17 +02:00
} else {
$error ++ ;
$this -> error = 'ErrorFailedToGetInsertedId' ;
}
} else {
$error ++ ;
$this -> error = $this -> db -> lasterror ();
}
} else {
// Product already exists with this ref
$langs -> load ( " products " );
$error ++ ;
$this -> error = " ErrorProductAlreadyExists " ;
}
} else {
$error ++ ;
$this -> error = $this -> db -> lasterror ();
}
if ( ! $error && ! $notrigger ) {
// Call trigger
$result = $this -> call_trigger ( 'PRODUCT_CREATE' , $user );
2021-02-26 14:25:17 +01:00
if ( $result < 0 ) {
$error ++ ;
2020-09-07 10:18:17 +02:00
}
// End call triggers
}
if ( ! $error ) {
$this -> db -> commit ();
return $this -> id ;
} else {
$this -> db -> rollback ();
return - $error ;
}
} else {
$this -> db -> rollback ();
dol_syslog ( get_class ( $this ) . " ::Create fails verify " . join ( ',' , $this -> errors ), LOG_WARNING );
return - 3 ;
}
}
/**
* Check properties of product are ok ( like name , barcode , ... ) .
* All properties must be already loaded on object ( this -> barcode , this -> barcode_type_code , ... ) .
*
* @ return int 0 if OK , < 0 if KO
*/
public function verify ()
{
2022-01-29 15:50:59 +01:00
global $langs ;
2020-09-07 10:18:17 +02:00
$this -> errors = array ();
$result = 0 ;
$this -> ref = trim ( $this -> ref );
if ( ! $this -> ref ) {
$this -> errors [] = 'ErrorBadRef' ;
$result = - 2 ;
}
2022-01-29 15:50:59 +01:00
$arrayofnonnegativevalue = array ( 'weight' => 'Weight' , 'width' => 'Width' , 'height' => 'Height' , 'length' => 'Length' , 'surface' => 'Surface' , 'volume' => 'Volume' );
foreach ( $arrayofnonnegativevalue as $key => $value ) {
2022-01-31 21:46:35 +01:00
if ( property_exists ( $this , $key ) && ! empty ( $this -> $key ) && ( $this -> $key < 0 )) {
$langs -> loadLangs ( array ( " main " , " other " ));
$this -> error = $langs -> trans ( " FieldCannotBeNegative " , $langs -> transnoentitiesnoconv ( $value ));
$this -> errors [] = $this -> error ;
2022-01-29 15:50:59 +01:00
$result = - 4 ;
}
}
2020-09-07 10:18:17 +02:00
$rescode = $this -> check_barcode ( $this -> barcode , $this -> barcode_type_code );
if ( $rescode ) {
if ( $rescode == - 1 ) {
$this -> errors [] = 'ErrorBadBarCodeSyntax' ;
} elseif ( $rescode == - 2 ) {
$this -> errors [] = 'ErrorBarCodeRequired' ;
} elseif ( $rescode == - 3 ) {
// Note: Common usage is to have barcode unique. For variants, we should have a different barcode.
$this -> errors [] = 'ErrorBarCodeAlreadyUsed' ;
}
$result = - 3 ;
}
return $result ;
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Check barcode
*
* @ param string $valuetotest Value to test
* @ param string $typefortest Type of barcode ( ISBN , EAN , ... )
* @ return int 0 if OK
* - 1 ErrorBadBarCodeSyntax
* - 2 ErrorBarCodeRequired
* - 3 ErrorBarCodeAlreadyUsed
*/
public function check_barcode ( $valuetotest , $typefortest )
{
// phpcs:enable
global $conf ;
if ( ! empty ( $conf -> barcode -> enabled ) && ! empty ( $conf -> global -> BARCODE_PRODUCT_ADDON_NUM )) {
$module = strtolower ( $conf -> global -> BARCODE_PRODUCT_ADDON_NUM );
$dirsociete = array_merge ( array ( '/core/modules/barcode/' ), $conf -> modules_parts [ 'barcode' ]);
2021-02-26 14:25:17 +01:00
foreach ( $dirsociete as $dirroot ) {
2020-09-07 10:18:17 +02:00
$res = dol_include_once ( $dirroot . $module . '.php' );
2021-02-26 14:25:17 +01:00
if ( $res ) {
break ;
2020-09-07 10:18:17 +02:00
}
}
$mod = new $module ();
dol_syslog ( get_class ( $this ) . " ::check_barcode value= " . $valuetotest . " type= " . $typefortest . " module= " . $module );
$result = $mod -> verif ( $this -> db , $valuetotest , $this , 0 , $typefortest );
return $result ;
} else {
return 0 ;
}
}
/**
* Update a record into database .
* If batch flag is set to on , we create records into llx_product_batch
*
* @ param int $id Id of product
* @ param User $user Object user making update
* @ param int $notrigger Disable triggers
* @ param string $action Current action for hookmanager ( 'add' or 'update' )
* @ param boolean $updatetype Update product type
* @ return int 1 if OK , - 1 if ref already exists , - 2 if other error
*/
public function update ( $id , $user , $notrigger = false , $action = 'update' , $updatetype = false )
{
global $langs , $conf , $hookmanager ;
$error = 0 ;
// Check parameters
2020-12-05 18:55:39 +01:00
if ( ! $this -> label ) {
$this -> label = 'MISSING LABEL' ;
2020-09-07 10:18:17 +02:00
}
// Clean parameters
$this -> ref = dol_string_nospecial ( trim ( $this -> ref ));
$this -> label = trim ( $this -> label );
$this -> description = trim ( $this -> description );
$this -> note = ( isset ( $this -> note ) ? trim ( $this -> note ) : null );
$this -> net_measure = price2num ( $this -> net_measure );
$this -> net_measure_units = trim ( $this -> net_measure_units );
$this -> weight = price2num ( $this -> weight );
$this -> weight_units = trim ( $this -> weight_units );
$this -> length = price2num ( $this -> length );
$this -> length_units = trim ( $this -> length_units );
$this -> width = price2num ( $this -> width );
$this -> width_units = trim ( $this -> width_units );
$this -> height = price2num ( $this -> height );
$this -> height_units = trim ( $this -> height_units );
2020-12-04 19:24:29 +01:00
$this -> surface = price2num ( $this -> surface );
$this -> surface_units = trim ( $this -> surface_units );
$this -> volume = price2num ( $this -> volume );
$this -> volume_units = trim ( $this -> volume_units );
2020-09-07 10:18:17 +02:00
// set unit not defined
if ( is_numeric ( $this -> length_units )) {
$this -> width_units = $this -> length_units ; // Not used yet
}
if ( is_numeric ( $this -> length_units )) {
$this -> height_units = $this -> length_units ; // Not used yet
}
2020-12-04 19:24:29 +01:00
2020-09-07 10:18:17 +02:00
// Automated compute surface and volume if not filled
if ( empty ( $this -> surface ) && ! empty ( $this -> length ) && ! empty ( $this -> width ) && $this -> length_units == $this -> width_units ) {
$this -> surface = $this -> length * $this -> width ;
$this -> surface_units = measuring_units_squared ( $this -> length_units );
}
2020-12-04 19:24:29 +01:00
if ( empty ( $this -> volume ) && ! empty ( $this -> surface ) && ! empty ( $this -> height ) && $this -> length_units == $this -> height_units ) {
2020-09-07 10:18:17 +02:00
$this -> volume = $this -> surface * $this -> height ;
$this -> volume_units = measuring_units_cubed ( $this -> height_units );
}
if ( empty ( $this -> tva_tx )) {
$this -> tva_tx = 0 ;
}
if ( empty ( $this -> tva_npr )) {
$this -> tva_npr = 0 ;
}
if ( empty ( $this -> localtax1_tx )) {
$this -> localtax1_tx = 0 ;
}
if ( empty ( $this -> localtax2_tx )) {
$this -> localtax2_tx = 0 ;
}
if ( empty ( $this -> localtax1_type )) {
$this -> localtax1_type = '0' ;
}
if ( empty ( $this -> localtax2_type )) {
$this -> localtax2_type = '0' ;
}
if ( empty ( $this -> status )) {
$this -> status = 0 ;
}
if ( empty ( $this -> status_buy )) {
$this -> status_buy = 0 ;
}
if ( empty ( $this -> country_id )) {
$this -> country_id = 0 ;
}
2020-09-14 11:01:11 +02:00
2020-09-14 10:59:09 +02:00
if ( empty ( $this -> state_id )) {
$this -> state_id = 0 ;
}
2020-09-07 10:18:17 +02:00
// Barcode value
$this -> barcode = trim ( $this -> barcode );
$this -> accountancy_code_buy = trim ( $this -> accountancy_code_buy );
$this -> accountancy_code_buy_intra = trim ( $this -> accountancy_code_buy_intra );
$this -> accountancy_code_buy_export = trim ( $this -> accountancy_code_buy_export );
$this -> accountancy_code_sell = trim ( $this -> accountancy_code_sell );
$this -> accountancy_code_sell_intra = trim ( $this -> accountancy_code_sell_intra );
$this -> accountancy_code_sell_export = trim ( $this -> accountancy_code_sell_export );
$this -> db -> begin ();
2020-12-06 19:33:43 +01:00
$result = 0 ;
// Check name is required and codes are ok or unique. If error, this->errors[] is filled
2020-09-07 10:18:17 +02:00
if ( $action != 'add' ) {
$result = $this -> verify (); // We don't check when update called during a create because verify was already done
2020-12-05 18:55:39 +01:00
} else {
// we can continue
$result = 0 ;
2020-09-07 10:18:17 +02:00
}
if ( $result >= 0 ) {
if ( empty ( $this -> oldcopy )) {
$org = new self ( $this -> db );
$org -> fetch ( $this -> id );
$this -> oldcopy = $org ;
}
// Test if batch management is activated on existing product
// If yes, we create missing entries into product_batch
if ( $this -> hasbatch () && ! $this -> oldcopy -> hasbatch ()) {
//$valueforundefinedlot = 'Undefined'; // In previous version, 39 and lower
$valueforundefinedlot = '000000' ;
2021-02-26 14:25:17 +01:00
if ( ! empty ( $conf -> global -> STOCK_DEFAULT_BATCH )) {
$valueforundefinedlot = $conf -> global -> STOCK_DEFAULT_BATCH ;
}
2020-09-07 10:18:17 +02:00
dol_syslog ( " Flag batch of product id= " . $this -> id . " is set to ON, so we will create missing records into product_batch " );
$this -> load_stock ();
2021-02-26 14:25:17 +01:00
foreach ( $this -> stock_warehouse as $idW => $ObjW ) { // For each warehouse where we have stocks defined for this product (for each lines in product_stock)
2020-09-07 10:18:17 +02:00
$qty_batch = 0 ;
2021-02-26 14:25:17 +01:00
foreach ( $ObjW -> detail_batch as $detail ) { // Each lines of detail in product_batch of the current $ObjW = product_stock
2020-09-07 10:18:17 +02:00
if ( $detail -> batch == $valueforundefinedlot || $detail -> batch == 'Undefined' ) {
// We discard this line, we will create it later
2022-01-27 10:12:10 +01:00
$sqlclean = " DELETE FROM " . $this -> db -> prefix () . " product_batch WHERE batch in('Undefined', ' " . $this -> db -> escape ( $valueforundefinedlot ) . " ') AND fk_product_stock = " . (( int ) $ObjW -> id );
2020-09-07 10:18:17 +02:00
$result = $this -> db -> query ( $sqlclean );
if ( ! $result ) {
dol_print_error ( $this -> db );
exit ;
}
continue ;
}
$qty_batch += $detail -> qty ;
}
// Quantities in batch details are not same as stock quantity,
// so we add a default batch record to complete and get same qty in parent and child table
if ( $ObjW -> real <> $qty_batch ) {
$ObjBatch = new Productbatch ( $this -> db );
$ObjBatch -> batch = $valueforundefinedlot ;
$ObjBatch -> qty = ( $ObjW -> real - $qty_batch );
$ObjBatch -> fk_product_stock = $ObjW -> id ;
if ( $ObjBatch -> create ( $user , 1 ) < 0 ) {
$error ++ ;
$this -> errors = $ObjBatch -> errors ;
}
}
}
}
// For automatic creation
2021-02-26 14:25:17 +01:00
if ( $this -> barcode == - 1 ) {
$this -> barcode = $this -> get_barcode ( $this , $this -> barcode_type_code );
2020-09-07 10:18:17 +02:00
}
2022-01-27 10:12:10 +01:00
$sql = " UPDATE " . $this -> db -> prefix () . " product " ;
2020-09-07 10:18:17 +02:00
$sql .= " SET label = ' " . $this -> db -> escape ( $this -> label ) . " ' " ;
if ( $updatetype && ( $this -> isProduct () || $this -> isService ())) {
2021-06-09 15:36:47 +02:00
$sql .= " , fk_product_type = " . (( int ) $this -> type );
2020-09-07 10:18:17 +02:00
}
$sql .= " , ref = ' " . $this -> db -> escape ( $this -> ref ) . " ' " ;
$sql .= " , ref_ext = " . ( ! empty ( $this -> ref_ext ) ? " ' " . $this -> db -> escape ( $this -> ref_ext ) . " ' " : " null " );
$sql .= " , default_vat_code = " . ( $this -> default_vat_code ? " ' " . $this -> db -> escape ( $this -> default_vat_code ) . " ' " : " null " );
2021-06-09 15:36:47 +02:00
$sql .= " , tva_tx = " . (( float ) $this -> tva_tx );
$sql .= " , recuperableonly = " . (( int ) $this -> tva_npr );
$sql .= " , localtax1_tx = " . (( float ) $this -> localtax1_tx );
$sql .= " , localtax2_tx = " . (( float ) $this -> localtax2_tx );
2020-09-07 10:18:17 +02:00
$sql .= " , localtax1_type = " . ( $this -> localtax1_type != '' ? " ' " . $this -> db -> escape ( $this -> localtax1_type ) . " ' " : " '0' " );
$sql .= " , localtax2_type = " . ( $this -> localtax2_type != '' ? " ' " . $this -> db -> escape ( $this -> localtax2_type ) . " ' " : " '0' " );
$sql .= " , barcode = " . ( empty ( $this -> barcode ) ? " null " : " ' " . $this -> db -> escape ( $this -> barcode ) . " ' " );
$sql .= " , fk_barcode_type = " . ( empty ( $this -> barcode_type ) ? " null " : $this -> db -> escape ( $this -> barcode_type ));
$sql .= " , tosell = " . ( int ) $this -> status ;
$sql .= " , tobuy = " . ( int ) $this -> status_buy ;
$sql .= " , tobatch = " . (( empty ( $this -> status_batch ) || $this -> status_batch < 0 ) ? '0' : ( int ) $this -> status_batch );
2021-04-08 18:03:46 +02:00
$sql .= " , batch_mask = ' " . $this -> db -> escape ( $this -> batch_mask ) . " ' " ;
2020-10-14 15:54:48 +02:00
$sql .= " , finished = " . (( ! isset ( $this -> finished ) || $this -> finished < 0 || $this -> finished == '' ) ? " null " : ( int ) $this -> finished );
2020-11-26 11:59:00 +01:00
$sql .= " , fk_default_bom = " . (( ! isset ( $this -> fk_default_bom ) || $this -> fk_default_bom < 0 || $this -> fk_default_bom == '' ) ? " null " : ( int ) $this -> fk_default_bom );
2020-09-07 10:18:17 +02:00
$sql .= " , net_measure = " . ( $this -> net_measure != '' ? " ' " . $this -> db -> escape ( $this -> net_measure ) . " ' " : 'null' );
$sql .= " , net_measure_units = " . ( $this -> net_measure_units != '' ? " ' " . $this -> db -> escape ( $this -> net_measure_units ) . " ' " : 'null' );
$sql .= " , weight = " . ( $this -> weight != '' ? " ' " . $this -> db -> escape ( $this -> weight ) . " ' " : 'null' );
$sql .= " , weight_units = " . ( $this -> weight_units != '' ? " ' " . $this -> db -> escape ( $this -> weight_units ) . " ' " : 'null' );
$sql .= " , length = " . ( $this -> length != '' ? " ' " . $this -> db -> escape ( $this -> length ) . " ' " : 'null' );
$sql .= " , length_units = " . ( $this -> length_units != '' ? " ' " . $this -> db -> escape ( $this -> length_units ) . " ' " : 'null' );
$sql .= " , width= " . ( $this -> width != '' ? " ' " . $this -> db -> escape ( $this -> width ) . " ' " : 'null' );
$sql .= " , width_units = " . ( $this -> width_units != '' ? " ' " . $this -> db -> escape ( $this -> width_units ) . " ' " : 'null' );
$sql .= " , height = " . ( $this -> height != '' ? " ' " . $this -> db -> escape ( $this -> height ) . " ' " : 'null' );
$sql .= " , height_units = " . ( $this -> height_units != '' ? " ' " . $this -> db -> escape ( $this -> height_units ) . " ' " : 'null' );
$sql .= " , surface = " . ( $this -> surface != '' ? " ' " . $this -> db -> escape ( $this -> surface ) . " ' " : 'null' );
$sql .= " , surface_units = " . ( $this -> surface_units != '' ? " ' " . $this -> db -> escape ( $this -> surface_units ) . " ' " : 'null' );
$sql .= " , volume = " . ( $this -> volume != '' ? " ' " . $this -> db -> escape ( $this -> volume ) . " ' " : 'null' );
$sql .= " , volume_units = " . ( $this -> volume_units != '' ? " ' " . $this -> db -> escape ( $this -> volume_units ) . " ' " : 'null' );
$sql .= " , fk_default_warehouse = " . ( $this -> fk_default_warehouse > 0 ? $this -> db -> escape ( $this -> fk_default_warehouse ) : 'null' );
$sql .= " , seuil_stock_alerte = " . (( isset ( $this -> seuil_stock_alerte ) && is_numeric ( $this -> seuil_stock_alerte )) ? ( float ) $this -> seuil_stock_alerte : 'null' );
$sql .= " , description = ' " . $this -> db -> escape ( $this -> description ) . " ' " ;
$sql .= " , url = " . ( $this -> url ? " ' " . $this -> db -> escape ( $this -> url ) . " ' " : 'null' );
$sql .= " , customcode = ' " . $this -> db -> escape ( $this -> customcode ) . " ' " ;
$sql .= " , fk_country = " . ( $this -> country_id > 0 ? ( int ) $this -> country_id : 'null' );
2020-09-14 11:01:11 +02:00
$sql .= " , fk_state = " . ( $this -> state_id > 0 ? ( int ) $this -> state_id : 'null' );
2021-04-28 17:08:17 +02:00
$sql .= " , lifetime = " . ( $this -> lifetime > 0 ? ( int ) $this -> lifetime : 'null' );
$sql .= " , qc_frequency = " . ( $this -> qc_frequency > 0 ? ( int ) $this -> qc_frequency : 'null' );
2020-09-07 10:18:17 +02:00
$sql .= " , note = " . ( isset ( $this -> note ) ? " ' " . $this -> db -> escape ( $this -> note ) . " ' " : 'null' );
$sql .= " , duration = ' " . $this -> db -> escape ( $this -> duration_value . $this -> duration_unit ) . " ' " ;
2021-03-27 05:23:20 +01:00
if ( empty ( $conf -> global -> MAIN_PRODUCT_PERENTITY_SHARED )) {
2021-03-20 07:42:16 +01:00
$sql .= " , accountancy_code_buy = ' " . $this -> db -> escape ( $this -> accountancy_code_buy ) . " ' " ;
$sql .= " , accountancy_code_buy_intra = ' " . $this -> db -> escape ( $this -> accountancy_code_buy_intra ) . " ' " ;
$sql .= " , accountancy_code_buy_export = ' " . $this -> db -> escape ( $this -> accountancy_code_buy_export ) . " ' " ;
$sql .= " , accountancy_code_sell= ' " . $this -> db -> escape ( $this -> accountancy_code_sell ) . " ' " ;
$sql .= " , accountancy_code_sell_intra= ' " . $this -> db -> escape ( $this -> accountancy_code_sell_intra ) . " ' " ;
$sql .= " , accountancy_code_sell_export= ' " . $this -> db -> escape ( $this -> accountancy_code_sell_export ) . " ' " ;
}
2020-09-07 10:18:17 +02:00
$sql .= " , desiredstock = " . (( isset ( $this -> desiredstock ) && is_numeric ( $this -> desiredstock )) ? ( float ) $this -> desiredstock : " null " );
$sql .= " , cost_price = " . ( $this -> cost_price != '' ? $this -> db -> escape ( $this -> cost_price ) : 'null' );
$sql .= " , fk_unit= " . ( ! $this -> fk_unit ? 'NULL' : ( int ) $this -> fk_unit );
$sql .= " , price_autogen = " . ( ! $this -> price_autogen ? 0 : 1 );
$sql .= " , fk_price_expression = " . ( $this -> fk_price_expression != 0 ? ( int ) $this -> fk_price_expression : 'NULL' );
$sql .= " , fk_user_modif = " . ( $user -> id > 0 ? $user -> id : 'NULL' );
2021-07-19 14:03:03 +02:00
$sql .= " , mandatory_period = " . ( $this -> mandatory_period );
2020-09-07 10:18:17 +02:00
// stock field is not here because it is a denormalized value from product_stock.
2021-03-14 11:48:39 +01:00
$sql .= " WHERE rowid = " . (( int ) $id );
2020-09-07 10:18:17 +02:00
dol_syslog ( get_class ( $this ) . " ::update " , LOG_DEBUG );
$resql = $this -> db -> query ( $sql );
if ( $resql ) {
$this -> id = $id ;
// Multilangs
if ( ! empty ( $conf -> global -> MAIN_MULTILANGS )) {
if ( $this -> setMultiLangs ( $user ) < 0 ) {
2021-06-16 20:17:21 +02:00
$this -> error = $langs -> trans ( " Error " ) . " : " . $this -> db -> error () . " - " . $sql ;
return - 2 ;
2020-09-07 10:18:17 +02:00
}
}
$action = 'update' ;
2021-03-20 07:42:16 +01:00
// update accountancy for this entity
2021-03-27 05:23:20 +01:00
if ( ! $error && ! empty ( $conf -> global -> MAIN_PRODUCT_PERENTITY_SHARED )) {
2022-01-27 10:12:10 +01:00
$this -> db -> query ( " DELETE FROM " . $this -> db -> prefix () . " product_perentity WHERE fk_product = " . (( int ) $this -> id ) . " AND entity = " . (( int ) $conf -> entity ));
2021-03-20 07:42:16 +01:00
2022-01-27 10:12:10 +01:00
$sql = " INSERT INTO " . $this -> db -> prefix () . " product_perentity ( " ;
2021-03-20 07:42:16 +01:00
$sql .= " fk_product " ;
$sql .= " , entity " ;
$sql .= " , accountancy_code_buy " ;
$sql .= " , accountancy_code_buy_intra " ;
$sql .= " , accountancy_code_buy_export " ;
$sql .= " , accountancy_code_sell " ;
$sql .= " , accountancy_code_sell_intra " ;
$sql .= " , accountancy_code_sell_export " ;
$sql .= " ) VALUES ( " ;
$sql .= $this -> id ;
$sql .= " , " . $conf -> entity ;
$sql .= " , ' " . $this -> db -> escape ( $this -> accountancy_code_buy ) . " ' " ;
$sql .= " , ' " . $this -> db -> escape ( $this -> accountancy_code_buy_intra ) . " ' " ;
$sql .= " , ' " . $this -> db -> escape ( $this -> accountancy_code_buy_export ) . " ' " ;
$sql .= " , ' " . $this -> db -> escape ( $this -> accountancy_code_sell ) . " ' " ;
$sql .= " , ' " . $this -> db -> escape ( $this -> accountancy_code_sell_intra ) . " ' " ;
$sql .= " , ' " . $this -> db -> escape ( $this -> accountancy_code_sell_export ) . " ' " ;
$sql .= " ) " ;
$result = $this -> db -> query ( $sql );
if ( ! $result ) {
$error ++ ;
$this -> error = 'ErrorFailedToUpdateAccountancyForEntity' ;
}
}
2020-09-07 10:18:17 +02:00
// Actions on extra fields
if ( ! $error ) {
$result = $this -> insertExtraFields ();
if ( $result < 0 ) {
$error ++ ;
}
}
if ( ! $error && ! $notrigger ) {
// Call trigger
$result = $this -> call_trigger ( 'PRODUCT_MODIFY' , $user );
if ( $result < 0 ) {
$error ++ ;
}
// End call triggers
}
if ( ! $error && ( is_object ( $this -> oldcopy ) && $this -> oldcopy -> ref !== $this -> ref )) {
// We remove directory
if ( $conf -> product -> dir_output ) {
$olddir = $conf -> product -> dir_output . " / " . dol_sanitizeFileName ( $this -> oldcopy -> ref );
$newdir = $conf -> product -> dir_output . " / " . dol_sanitizeFileName ( $this -> ref );
if ( file_exists ( $olddir )) {
//include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
//$res = dol_move($olddir, $newdir);
// do not use dol_move with directory
$res = @ rename ( $olddir , $newdir );
if ( ! $res ) {
$langs -> load ( " errors " );
$this -> error = $langs -> trans ( 'ErrorFailToRenameDir' , $olddir , $newdir );
$error ++ ;
}
}
}
}
if ( ! $error ) {
2020-12-07 10:37:55 +01:00
if ( ! empty ( $conf -> variants -> enabled )) {
2020-09-07 10:18:17 +02:00
include_once DOL_DOCUMENT_ROOT . '/variants/class/ProductCombination.class.php' ;
$comb = new ProductCombination ( $this -> db );
foreach ( $comb -> fetchAllByFkProductParent ( $this -> id ) as $currcomb ) {
$currcomb -> updateProperties ( $this , $user );
}
}
$this -> db -> commit ();
return 1 ;
} else {
$this -> db -> rollback ();
return - $error ;
}
} else {
if ( $this -> db -> errno () == 'DB_ERROR_RECORD_ALREADY_EXISTS' ) {
$langs -> load ( " errors " );
if ( empty ( $conf -> barcode -> enabled ) || empty ( $this -> barcode )) {
$this -> error = $langs -> trans ( " Error " ) . " : " . $langs -> trans ( " ErrorProductAlreadyExists " , $this -> ref );
} else {
$this -> error = $langs -> trans ( " Error " ) . " : " . $langs -> trans ( " ErrorProductBarCodeAlreadyExists " , $this -> barcode );
}
$this -> errors [] = $this -> error ;
$this -> db -> rollback ();
return - 1 ;
} else {
$this -> error = $langs -> trans ( " Error " ) . " : " . $this -> db -> error () . " - " . $sql ;
$this -> errors [] = $this -> error ;
$this -> db -> rollback ();
return - 2 ;
}
}
} else {
$this -> db -> rollback ();
dol_syslog ( get_class ( $this ) . " ::Update fails verify " . join ( ',' , $this -> errors ), LOG_WARNING );
return - 3 ;
}
}
/**
* Delete a product from database ( if not used )
*
* @ param User $user User ( object ) deleting product
* @ param int $notrigger Do not execute trigger
* @ return int < 0 if KO , 0 = Not possible , > 0 if OK
*/
public function delete ( User $user , $notrigger = 0 )
{
global $conf , $langs ;
include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php' ;
$error = 0 ;
// Check parameters
if ( empty ( $this -> id )) {
$this -> error = " Object must be fetched before calling delete " ;
return - 1 ;
}
if (( $this -> type == Product :: TYPE_PRODUCT && empty ( $user -> rights -> produit -> supprimer )) || ( $this -> type == Product :: TYPE_SERVICE && empty ( $user -> rights -> service -> supprimer ))) {
$this -> error = " ErrorForbidden " ;
return 0 ;
}
$objectisused = $this -> isObjectUsed ( $this -> id );
if ( empty ( $objectisused )) {
$this -> db -> begin ();
if ( ! $error && empty ( $notrigger )) {
// Call trigger
$result = $this -> call_trigger ( 'PRODUCT_DELETE' , $user );
if ( $result < 0 ) {
$error ++ ;
}
// End call triggers
}
// Delete from product_batch on product delete
if ( ! $error ) {
2022-01-27 10:12:10 +01:00
$sql = " DELETE FROM " . $this -> db -> prefix () . 'product_batch' ;
2020-09-07 10:18:17 +02:00
$sql .= " WHERE fk_product_stock IN ( " ;
2022-01-27 10:12:10 +01:00
$sql .= " SELECT rowid FROM " . $this -> db -> prefix () . 'product_stock' ;
2021-08-23 18:56:46 +02:00
$sql .= " WHERE fk_product = " . (( int ) $this -> id ) . " ) " ;
2020-09-07 10:18:17 +02:00
$result = $this -> db -> query ( $sql );
if ( ! $result ) {
$error ++ ;
$this -> errors [] = $this -> db -> lasterror ();
}
}
// Delete all child tables
if ( ! $error ) {
$elements = array ( 'product_fournisseur_price' , 'product_price' , 'product_lang' , 'categorie_product' , 'product_stock' , 'product_customer_price' , 'product_lot' ); // product_batch is done before
2021-02-26 14:25:17 +01:00
foreach ( $elements as $table ) {
2020-09-07 10:18:17 +02:00
if ( ! $error ) {
2022-01-27 10:12:10 +01:00
$sql = " DELETE FROM " . $this -> db -> prefix () . $table ;
2020-09-07 10:18:17 +02:00
$sql .= " WHERE fk_product = " . ( int ) $this -> id ;
$result = $this -> db -> query ( $sql );
if ( ! $result ) {
$error ++ ;
$this -> errors [] = $this -> db -> lasterror ();
}
}
}
}
if ( ! $error ) {
include_once DOL_DOCUMENT_ROOT . '/variants/class/ProductCombination.class.php' ;
include_once DOL_DOCUMENT_ROOT . '/variants/class/ProductCombination2ValuePair.class.php' ;
//If it is a parent product, then we remove the association with child products
$prodcomb = new ProductCombination ( $this -> db );
if ( $prodcomb -> deleteByFkProductParent ( $user , $this -> id ) < 0 ) {
$error ++ ;
$this -> errors [] = 'Error deleting combinations' ;
}
//We also check if it is a child product
if ( ! $error && ( $prodcomb -> fetchByFkProductChild ( $this -> id ) > 0 ) && ( $prodcomb -> delete ( $user ) < 0 )) {
$error ++ ;
$this -> errors [] = 'Error deleting child combination' ;
}
}
// Delete from product_association
if ( ! $error ) {
2022-01-27 10:12:10 +01:00
$sql = " DELETE FROM " . $this -> db -> prefix () . " product_association " ;
2020-09-07 10:18:17 +02:00
$sql .= " WHERE fk_product_pere = " . ( int ) $this -> id . " OR fk_product_fils = " . ( int ) $this -> id ;
$result = $this -> db -> query ( $sql );
if ( ! $result ) {
$error ++ ;
$this -> errors [] = $this -> db -> lasterror ();
}
}
2020-12-23 15:45:33 +01:00
// Remove extrafields
if ( ! $error ) {
$result = $this -> deleteExtraFields ();
if ( $result < 0 ) {
$error ++ ;
dol_syslog ( get_class ( $this ) . " ::delete error -4 " . $this -> error , LOG_ERR );
}
}
2020-09-07 10:18:17 +02:00
// Delete product
if ( ! $error ) {
2022-01-27 10:12:10 +01:00
$sqlz = " DELETE FROM " . $this -> db -> prefix () . " product " ;
2020-09-07 10:18:17 +02:00
$sqlz .= " WHERE rowid = " . ( int ) $this -> id ;
$resultz = $this -> db -> query ( $sqlz );
if ( ! $resultz ) {
$error ++ ;
$this -> errors [] = $this -> db -> lasterror ();
}
}
2023-01-03 11:42:39 +01:00
// Delete record into ECM index and physically
if ( ! $error ) {
$res = $this -> deleteEcmFiles ( 0 ); // Deleting files physically is done later with the dol_delete_dir_recursive
if ( ! $res ) {
$error ++ ;
}
}
2020-09-07 10:18:17 +02:00
if ( ! $error ) {
// We remove directory
$ref = dol_sanitizeFileName ( $this -> ref );
if ( $conf -> product -> dir_output ) {
$dir = $conf -> product -> dir_output . " / " . $ref ;
if ( file_exists ( $dir )) {
$res = @ dol_delete_dir_recursive ( $dir );
if ( ! $res ) {
$this -> errors [] = 'ErrorFailToDeleteDir' ;
$error ++ ;
}
}
}
}
if ( ! $error ) {
$this -> db -> commit ();
return 1 ;
} else {
2021-02-26 14:25:17 +01:00
foreach ( $this -> errors as $errmsg ) {
2020-09-07 10:18:17 +02:00
dol_syslog ( get_class ( $this ) . " ::delete " . $errmsg , LOG_ERR );
$this -> error .= ( $this -> error ? ', ' . $errmsg : $errmsg );
}
$this -> db -> rollback ();
return - $error ;
}
} else {
$this -> error = " ErrorRecordIsUsedCantDelete " ;
return 0 ;
}
}
/**
* Update or add a translation for a product
*
* @ param User $user Object user making update
* @ return int < 0 if KO , > 0 if OK
*/
public function setMultiLangs ( $user )
{
global $conf , $langs ;
$langs_available = $langs -> get_available_languages ( DOL_DOCUMENT_ROOT , 0 , 2 );
$current_lang = $langs -> getDefaultLang ();
2021-02-26 14:25:17 +01:00
foreach ( $langs_available as $key => $value ) {
2020-09-07 10:18:17 +02:00
if ( $key == $current_lang ) {
$sql = " SELECT rowid " ;
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " product_lang " ;
2021-08-27 18:18:50 +02:00
$sql .= " WHERE fk_product = " . (( int ) $this -> id );
$sql .= " AND lang = ' " . $this -> db -> escape ( $key ) . " ' " ;
2020-09-07 10:18:17 +02:00
$result = $this -> db -> query ( $sql );
2021-02-26 14:25:17 +01:00
if ( $this -> db -> num_rows ( $result )) { // if there is already a description line for this language
2022-01-27 10:12:10 +01:00
$sql2 = " UPDATE " . $this -> db -> prefix () . " product_lang " ;
2020-09-07 10:18:17 +02:00
$sql2 .= " SET " ;
$sql2 .= " label=' " . $this -> db -> escape ( $this -> label ) . " ', " ;
$sql2 .= " description=' " . $this -> db -> escape ( $this -> description ) . " ' " ;
2021-02-26 14:25:17 +01:00
if ( ! empty ( $conf -> global -> PRODUCT_USE_OTHER_FIELD_IN_TRANSLATION )) {
$sql2 .= " , note=' " . $this -> db -> escape ( $this -> other ) . " ' " ;
2020-09-07 10:18:17 +02:00
}
2021-08-27 18:18:50 +02:00
$sql2 .= " WHERE fk_product = " . (( int ) $this -> id ) . " AND lang = ' " . $this -> db -> escape ( $key ) . " ' " ;
2020-09-07 10:18:17 +02:00
} else {
2022-01-27 10:12:10 +01:00
$sql2 = " INSERT INTO " . $this -> db -> prefix () . " product_lang (fk_product, lang, label, description " ;
2021-02-26 14:25:17 +01:00
if ( ! empty ( $conf -> global -> PRODUCT_USE_OTHER_FIELD_IN_TRANSLATION )) {
$sql2 .= " , note " ;
2020-09-07 10:18:17 +02:00
}
$sql2 .= " ) " ;
$sql2 .= " VALUES( " . $this -> id . " ,' " . $this -> db -> escape ( $key ) . " ',' " . $this -> db -> escape ( $this -> label ) . " ', " ;
$sql2 .= " ' " . $this -> db -> escape ( $this -> description ) . " ' " ;
if ( ! empty ( $conf -> global -> PRODUCT_USE_OTHER_FIELD_IN_TRANSLATION )) {
$sql2 .= " , ' " . $this -> db -> escape ( $this -> other ) . " ' " ;
}
$sql2 .= " ) " ;
}
dol_syslog ( get_class ( $this ) . '::setMultiLangs key = current_lang = ' . $key );
if ( ! $this -> db -> query ( $sql2 )) {
$this -> error = $this -> db -> lasterror ();
return - 1 ;
}
} elseif ( isset ( $this -> multilangs [ $key ])) {
2021-02-27 02:00:00 +01:00
if ( empty ( $this -> multilangs [ " $key " ][ " label " ])) {
$this -> error = $langs -> trans ( " ErrorFieldRequired " , $langs -> transnoentitiesnoconv ( " Label " ));
return - 1 ;
}
2020-09-07 10:18:17 +02:00
$sql = " SELECT rowid " ;
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " product_lang " ;
2021-08-27 18:18:50 +02:00
$sql .= " WHERE fk_product = " . (( int ) $this -> id );
$sql .= " AND lang = ' " . $this -> db -> escape ( $key ) . " ' " ;
2020-09-07 10:18:17 +02:00
$result = $this -> db -> query ( $sql );
2021-02-26 14:25:17 +01:00
if ( $this -> db -> num_rows ( $result )) { // if there is already a description line for this language
2022-01-27 10:12:10 +01:00
$sql2 = " UPDATE " . $this -> db -> prefix () . " product_lang " ;
2020-09-07 10:18:17 +02:00
$sql2 .= " SET " ;
2021-08-27 18:18:50 +02:00
$sql2 .= " label = ' " . $this -> db -> escape ( $this -> multilangs [ " $key " ][ " label " ]) . " ', " ;
$sql2 .= " description = ' " . $this -> db -> escape ( $this -> multilangs [ " $key " ][ " description " ]) . " ' " ;
2020-09-07 10:18:17 +02:00
if ( ! empty ( $conf -> global -> PRODUCT_USE_OTHER_FIELD_IN_TRANSLATION )) {
2021-08-27 18:18:50 +02:00
$sql2 .= " , note = ' " . $this -> db -> escape ( $this -> multilangs [ " $key " ][ " other " ]) . " ' " ;
2020-09-07 10:18:17 +02:00
}
2021-08-27 18:18:50 +02:00
$sql2 .= " WHERE fk_product = " . (( int ) $this -> id ) . " AND lang = ' " . $this -> db -> escape ( $key ) . " ' " ;
2020-09-07 10:18:17 +02:00
} else {
2022-01-27 10:12:10 +01:00
$sql2 = " INSERT INTO " . $this -> db -> prefix () . " product_lang (fk_product, lang, label, description " ;
2021-02-26 14:25:17 +01:00
if ( ! empty ( $conf -> global -> PRODUCT_USE_OTHER_FIELD_IN_TRANSLATION )) {
$sql2 .= " , note " ;
2020-09-07 10:18:17 +02:00
}
$sql2 .= " ) " ;
$sql2 .= " VALUES( " . $this -> id . " ,' " . $this -> db -> escape ( $key ) . " ',' " . $this -> db -> escape ( $this -> multilangs [ " $key " ][ " label " ]) . " ', " ;
$sql2 .= " ' " . $this -> db -> escape ( $this -> multilangs [ " $key " ][ " description " ]) . " ' " ;
if ( ! empty ( $conf -> global -> PRODUCT_USE_OTHER_FIELD_IN_TRANSLATION )) {
$sql2 .= " , ' " . $this -> db -> escape ( $this -> multilangs [ " $key " ][ " other " ]) . " ' " ;
}
$sql2 .= " ) " ;
}
// We do not save if main fields are empty
if ( $this -> multilangs [ " $key " ][ " label " ] || $this -> multilangs [ " $key " ][ " description " ]) {
if ( ! $this -> db -> query ( $sql2 )) {
$this -> error = $this -> db -> lasterror ();
return - 1 ;
}
}
} else {
// language is not current language and we didn't provide a multilang description for this language
}
}
// Call trigger
$result = $this -> call_trigger ( 'PRODUCT_SET_MULTILANGS' , $user );
if ( $result < 0 ) {
$this -> error = $this -> db -> lasterror ();
return - 1 ;
}
// End call triggers
return 1 ;
}
/**
* Delete a language for this product
*
* @ param string $langtodelete Language code to delete
* @ param User $user Object user making delete
*
* @ return int < 0 if KO , > 0 if OK
*/
public function delMultiLangs ( $langtodelete , $user )
{
2022-01-27 10:12:10 +01:00
$sql = " DELETE FROM " . $this -> db -> prefix () . " product_lang " ;
2021-08-27 18:18:50 +02:00
$sql .= " WHERE fk_product = " . (( int ) $this -> id ) . " AND lang = ' " . $this -> db -> escape ( $langtodelete ) . " ' " ;
2020-09-07 10:18:17 +02:00
dol_syslog ( get_class ( $this ) . '::delMultiLangs' , LOG_DEBUG );
$result = $this -> db -> query ( $sql );
if ( $result ) {
// Call trigger
$result = $this -> call_trigger ( 'PRODUCT_DEL_MULTILANGS' , $user );
if ( $result < 0 ) {
$this -> error = $this -> db -> lasterror ();
dol_syslog ( get_class ( $this ) . '::delMultiLangs error=' . $this -> error , LOG_ERR );
return - 1 ;
}
// End call triggers
return 1 ;
} else {
$this -> error = $this -> db -> lasterror ();
dol_syslog ( get_class ( $this ) . '::delMultiLangs error=' . $this -> error , LOG_ERR );
return - 1 ;
}
}
/**
* Sets an accountancy code for a product .
* Also calls PRODUCT_MODIFY trigger when modified
*
* @ param string $type It can be 'buy' , 'buy_intra' , 'buy_export' , 'sell' , 'sell_intra' or 'sell_export'
* @ param string $value Accountancy code
* @ return int < 0 KO > 0 OK
*/
public function setAccountancyCode ( $type , $value )
{
global $user , $langs , $conf ;
$error = 0 ;
$this -> db -> begin ();
if ( $type == 'buy' ) {
$field = 'accountancy_code_buy' ;
} elseif ( $type == 'buy_intra' ) {
$field = 'accountancy_code_buy_intra' ;
} elseif ( $type == 'buy_export' ) {
$field = 'accountancy_code_buy_export' ;
} elseif ( $type == 'sell' ) {
$field = 'accountancy_code_sell' ;
} elseif ( $type == 'sell_intra' ) {
$field = 'accountancy_code_sell_intra' ;
} elseif ( $type == 'sell_export' ) {
$field = 'accountancy_code_sell_export' ;
} else {
return - 1 ;
}
2022-01-27 10:12:10 +01:00
$sql = " UPDATE " . $this -> db -> prefix () . $this -> table_element . " SET " ;
2020-09-07 10:18:17 +02:00
$sql .= " $field = ' " . $this -> db -> escape ( $value ) . " ' " ;
2021-08-27 16:33:03 +02:00
$sql .= " WHERE rowid = " . (( int ) $this -> id );
2020-09-07 10:18:17 +02:00
2021-08-27 22:42:04 +02:00
dol_syslog ( __METHOD__ . " " , LOG_DEBUG );
2020-09-07 10:18:17 +02:00
$resql = $this -> db -> query ( $sql );
if ( $resql ) {
// Call trigger
$result = $this -> call_trigger ( 'PRODUCT_MODIFY' , $user );
2021-02-26 14:25:17 +01:00
if ( $result < 0 ) {
$error ++ ;
}
2020-09-07 10:18:17 +02:00
// End call triggers
if ( $error ) {
$this -> db -> rollback ();
return - 1 ;
}
$this -> $field = $value ;
$this -> db -> commit ();
return 1 ;
} else {
$this -> error = $this -> db -> lasterror ();
$this -> db -> rollback ();
return - 1 ;
}
}
/**
* Load array this -> multilangs
*
* @ return int < 0 if KO , > 0 if OK
*/
public function getMultiLangs ()
{
global $langs ;
$current_lang = $langs -> getDefaultLang ();
$sql = " SELECT lang, label, description, note as other " ;
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " product_lang " ;
2021-08-27 18:18:50 +02:00
$sql .= " WHERE fk_product = " . (( int ) $this -> id );
2020-09-07 10:18:17 +02:00
$result = $this -> db -> query ( $sql );
if ( $result ) {
2021-02-26 14:25:17 +01:00
while ( $obj = $this -> db -> fetch_object ( $result )) {
2020-09-07 10:18:17 +02:00
//print 'lang='.$obj->lang.' current='.$current_lang.'<br>';
2021-02-26 14:25:17 +01:00
if ( $obj -> lang == $current_lang ) { // si on a les traduct. dans la langue courante on les charge en infos principales.
2020-09-07 10:18:17 +02:00
$this -> label = $obj -> label ;
$this -> description = $obj -> description ;
$this -> other = $obj -> other ;
}
$this -> multilangs [ " $obj->lang " ][ " label " ] = $obj -> label ;
$this -> multilangs [ " $obj->lang " ][ " description " ] = $obj -> description ;
$this -> multilangs [ " $obj->lang " ][ " other " ] = $obj -> other ;
}
return 1 ;
} else {
$this -> error = " Error: " . $this -> db -> lasterror () . " - " . $sql ;
return - 1 ;
}
}
2021-05-19 11:52:34 +02:00
/**
2021-05-19 12:01:47 +02:00
* used to check if price have really change to avoid log pollution
2021-05-19 11:52:34 +02:00
*
* @ param int $level price level to change
2021-05-19 12:01:47 +02:00
* @ return array
2021-05-19 11:52:34 +02:00
*/
private function getArrayForPriceCompare ( $level = 0 )
{
$testExit = array ( 'multiprices' , 'multiprices_ttc' , 'multiprices_base_type' , 'multiprices_min' , 'multiprices_min_ttc' , 'multiprices_tva_tx' , 'multiprices_recuperableonly' );
2021-05-20 15:30:33 +02:00
foreach ( $testExit as $field ) {
2022-04-13 12:11:35 +02:00
if ( ! isset ( $this -> $field )) {
return array ();
}
$tmparray = $this -> $field ;
if ( ! isset ( $tmparray [ $level ])) {
2021-05-19 11:52:34 +02:00
return array ();
}
}
$lastPrice = array (
'level' => $level ? $level : 1 ,
'multiprices' => doubleval ( $this -> multiprices [ $level ]),
'multiprices_ttc' => doubleval ( $this -> multiprices_ttc [ $level ]),
'multiprices_base_type' => $this -> multiprices_base_type [ $level ],
'multiprices_min' => doubleval ( $this -> multiprices_min [ $level ]),
'multiprices_min_ttc' => doubleval ( $this -> multiprices_min_ttc [ $level ]),
'multiprices_tva_tx' => doubleval ( $this -> multiprices_tva_tx [ $level ]),
'multiprices_recuperableonly' => doubleval ( $this -> multiprices_recuperableonly [ $level ]),
);
return $lastPrice ;
}
2020-09-07 10:18:17 +02:00
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Insert a track that we changed a customer price
*
* @ param User $user User making change
* @ param int $level price level to change
* @ return int < 0 if KO , > 0 if OK
*/
private function _log_price ( $user , $level = 0 )
{
// phpcs:enable
global $conf ;
$now = dol_now ();
// Clean parameters
if ( empty ( $this -> price_by_qty )) {
$this -> price_by_qty = 0 ;
}
// Add new price
2022-01-27 10:12:10 +01:00
$sql = " INSERT INTO " . $this -> db -> prefix () . " product_price(price_level,date_price, fk_product, fk_user_author, price, price_ttc, price_base_type,tosell, tva_tx, default_vat_code, recuperableonly, " ;
2020-09-07 10:18:17 +02:00
$sql .= " localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, price_min,price_min_ttc,price_by_qty,entity,fk_price_expression) " ;
2021-08-28 03:09:18 +02:00
$sql .= " VALUES( " . ( $level ? (( int ) $level ) : 1 ) . " , ' " . $this -> db -> idate ( $now ) . " ', " . (( int ) $this -> id ) . " , " . (( int ) $user -> id ) . " , " . (( float ) price2num ( $this -> price )) . " , " . (( float ) price2num ( $this -> price_ttc )) . " ,' " . $this -> db -> escape ( $this -> price_base_type ) . " ', " . (( int ) $this -> status ) . " , " . (( float ) price2num ( $this -> tva_tx )) . " , " . ( $this -> default_vat_code ? ( " ' " . $this -> db -> escape ( $this -> default_vat_code ) . " ' " ) : " null " ) . " , " . (( int ) $this -> tva_npr ) . " , " ;
$sql .= " " . price2num ( $this -> localtax1_tx ) . " , " . price2num ( $this -> localtax2_tx ) . " , ' " . $this -> db -> escape ( $this -> localtax1_type ) . " ', ' " . $this -> db -> escape ( $this -> localtax2_type ) . " ', " . price2num ( $this -> price_min ) . " , " . price2num ( $this -> price_min_ttc ) . " , " . price2num ( $this -> price_by_qty ) . " , " . (( int ) $conf -> entity ) . " , " . ( $this -> fk_price_expression > 0 ? (( int ) $this -> fk_price_expression ) : 'null' );
2020-09-07 10:18:17 +02:00
$sql .= " ) " ;
dol_syslog ( get_class ( $this ) . " ::_log_price " , LOG_DEBUG );
$resql = $this -> db -> query ( $sql );
if ( ! $resql ) {
$this -> error = $this -> db -> lasterror ();
dol_print_error ( $this -> db );
return - 1 ;
} else {
return 1 ;
}
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Delete a price line
*
* @ param User $user Object user
* @ param int $rowid Line id to delete
* @ return int < 0 if KO , > 0 if OK
*/
public function log_price_delete ( $user , $rowid )
{
// phpcs:enable
2022-01-27 10:12:10 +01:00
$sql = " DELETE FROM " . $this -> db -> prefix () . " product_price_by_qty " ;
2021-08-27 18:18:50 +02:00
$sql .= " WHERE fk_product_price = " . (( int ) $rowid );
2020-09-07 10:18:17 +02:00
$resql = $this -> db -> query ( $sql );
2022-01-27 10:12:10 +01:00
$sql = " DELETE FROM " . $this -> db -> prefix () . " product_price " ;
2021-04-24 20:18:11 +02:00
$sql .= " WHERE rowid= " . (( int ) $rowid );
2020-09-07 10:18:17 +02:00
$resql = $this -> db -> query ( $sql );
if ( $resql ) {
return 1 ;
} else {
$this -> error = $this -> db -> lasterror ();
return - 1 ;
}
}
/**
* Return price of sell of a product for a seller / buyer / product .
*
* @ param Societe $thirdparty_seller Seller
* @ param Societe $thirdparty_buyer Buyer
2020-11-01 17:58:41 +01:00
* @ param int $pqp Id of product price per quantity if a selection was done of such a price
2020-09-07 10:18:17 +02:00
* @ return array Array of price information array ( 'pu_ht' => , 'pu_ttc' => , 'tva_tx' => 'X.Y (code)' , ... ), 'tva_npr' => 0 , ... )
* @ see get_buyprice (), find_min_price_product_fournisseur ()
*/
public function getSellPrice ( $thirdparty_seller , $thirdparty_buyer , $pqp = 0 )
{
2022-04-29 16:42:43 +02:00
global $conf , $db , $hookmanager , $action ;
// Call hook if any
if ( is_object ( $hookmanager )) {
$parameters = array ( 'thirdparty_seller' => $thirdparty_seller , 'thirdparty_buyer' => $thirdparty_buyer , 'pqp' => $pqp );
// Note that $action and $object may have been modified by some hooks
$reshook = $hookmanager -> executeHooks ( 'getSellPrice' , $parameters , $this , $action );
if ( $reshook > 0 ) {
return $hookmanager -> resArray ;
}
2022-04-27 09:03:51 +02:00
}
2020-09-07 10:18:17 +02:00
// Update if prices fields are defined
$tva_tx = get_default_tva ( $thirdparty_seller , $thirdparty_buyer , $this -> id );
$tva_npr = get_default_npr ( $thirdparty_seller , $thirdparty_buyer , $this -> id );
2021-02-26 14:25:17 +01:00
if ( empty ( $tva_tx )) {
$tva_npr = 0 ;
}
2020-09-07 10:18:17 +02:00
$pu_ht = $this -> price ;
$pu_ttc = $this -> price_ttc ;
$price_min = $this -> price_min ;
$price_base_type = $this -> price_base_type ;
// If price per segment
if ( ! empty ( $conf -> global -> PRODUIT_MULTIPRICES ) && ! empty ( $thirdparty_buyer -> price_level )) {
$pu_ht = $this -> multiprices [ $thirdparty_buyer -> price_level ];
$pu_ttc = $this -> multiprices_ttc [ $thirdparty_buyer -> price_level ];
$price_min = $this -> multiprices_min [ $thirdparty_buyer -> price_level ];
$price_base_type = $this -> multiprices_base_type [ $thirdparty_buyer -> price_level ];
2021-02-26 14:25:17 +01:00
if ( ! empty ( $conf -> global -> PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL )) { // using this option is a bug. kept for backward compatibility
if ( isset ( $this -> multiprices_tva_tx [ $thirdparty_buyer -> price_level ])) {
$tva_tx = $this -> multiprices_tva_tx [ $thirdparty_buyer -> price_level ];
}
if ( isset ( $this -> multiprices_recuperableonly [ $thirdparty_buyer -> price_level ])) {
$tva_npr = $this -> multiprices_recuperableonly [ $thirdparty_buyer -> price_level ];
}
if ( empty ( $tva_tx )) {
$tva_npr = 0 ;
}
2020-09-07 10:18:17 +02:00
}
} elseif ( ! empty ( $conf -> global -> PRODUIT_CUSTOMER_PRICES )) {
// If price per customer
require_once DOL_DOCUMENT_ROOT . '/product/class/productcustomerprice.class.php' ;
2020-09-20 02:57:15 +02:00
$prodcustprice = new Productcustomerprice ( $this -> db );
2020-09-07 10:18:17 +02:00
$filter = array ( 't.fk_product' => $this -> id , 't.fk_soc' => $thirdparty_buyer -> id );
$result = $prodcustprice -> fetch_all ( '' , '' , 0 , 0 , $filter );
if ( $result ) {
if ( count ( $prodcustprice -> lines ) > 0 ) {
$pu_ht = price ( $prodcustprice -> lines [ 0 ] -> price );
2021-02-09 14:08:49 +01:00
$price_min = price ( $prodcustprice -> lines [ 0 ] -> price_min );
2020-09-07 10:18:17 +02:00
$pu_ttc = price ( $prodcustprice -> lines [ 0 ] -> price_ttc );
$price_base_type = $prodcustprice -> lines [ 0 ] -> price_base_type ;
$tva_tx = $prodcustprice -> lines [ 0 ] -> tva_tx ;
2021-02-26 14:25:17 +01:00
if ( $prodcustprice -> lines [ 0 ] -> default_vat_code && ! preg_match ( '/\(.*\)/' , $tva_tx )) {
$tva_tx .= ' (' . $prodcustprice -> lines [ 0 ] -> default_vat_code . ')' ;
}
2020-09-07 10:18:17 +02:00
$tva_npr = $prodcustprice -> lines [ 0 ] -> recuperableonly ;
2021-02-26 14:25:17 +01:00
if ( empty ( $tva_tx )) {
$tva_npr = 0 ;
}
2020-09-07 10:18:17 +02:00
}
}
} elseif ( ! empty ( $conf -> global -> PRODUIT_CUSTOMER_PRICES_BY_QTY )) {
// If price per quantity
if ( $this -> prices_by_qty [ 0 ]) {
// yes, this product has some prices per quantity
// Search price into product_price_by_qty from $this->id
foreach ( $this -> prices_by_qty_list [ 0 ] as $priceforthequantityarray ) {
2021-02-26 14:25:17 +01:00
if ( $priceforthequantityarray [ 'rowid' ] != $pqp ) {
continue ;
}
2020-09-07 10:18:17 +02:00
// We found the price
2021-02-26 14:25:17 +01:00
if ( $priceforthequantityarray [ 'price_base_type' ] == 'HT' ) {
2020-09-07 10:18:17 +02:00
$pu_ht = $priceforthequantityarray [ 'unitprice' ];
} else {
$pu_ttc = $priceforthequantityarray [ 'unitprice' ];
}
break ;
}
}
} elseif ( ! empty ( $conf -> global -> PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES )) {
// If price per quantity and customer
if ( $this -> prices_by_qty [ $thirdparty_buyer -> price_level ]) {
// yes, this product has some prices per quantity
// Search price into product_price_by_qty from $this->id
2021-02-26 14:25:17 +01:00
foreach ( $this -> prices_by_qty_list [ $thirdparty_buyer -> price_level ] as $priceforthequantityarray ) {
if ( $priceforthequantityarray [ 'rowid' ] != $pqp ) {
continue ;
}
2020-09-07 10:18:17 +02:00
// We found the price
2021-02-26 14:25:17 +01:00
if ( $priceforthequantityarray [ 'price_base_type' ] == 'HT' ) {
2020-09-07 10:18:17 +02:00
$pu_ht = $priceforthequantityarray [ 'unitprice' ];
} else {
$pu_ttc = $priceforthequantityarray [ 'unitprice' ];
}
break ;
}
}
}
return array ( 'pu_ht' => $pu_ht , 'pu_ttc' => $pu_ttc , 'price_min' => $price_min , 'price_base_type' => $price_base_type , 'tva_tx' => $tva_tx , 'tva_npr' => $tva_npr );
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Read price used by a provider .
* We enter as input couple prodfournprice / qty or triplet qty / product_id / fourn_ref .
* This also set some properties on product like -> buyprice , -> fourn_pu , ...
*
* @ param int $prodfournprice Id du tarif = rowid table product_fournisseur_price
* @ param double $qty Quantity asked or - 1 to get first entry found
* @ param int $product_id Filter on a particular product id
* @ param string $fourn_ref Filter on a supplier price ref . 'none' to exclude ref in search .
* @ param int $fk_soc If of supplier
* @ return int <- 1 if KO , - 1 if qty not enough , 0 if OK but nothing found , id_product if OK and found . May also initialize some properties like ( -> ref_supplier , buyprice , fourn_pu , vatrate_supplier ... )
* @ see getSellPrice (), find_min_price_product_fournisseur ()
*/
public function get_buyprice ( $prodfournprice , $qty , $product_id = 0 , $fourn_ref = '' , $fk_soc = 0 )
{
// phpcs:enable
global $conf ;
$result = 0 ;
// We do a first seach with a select by searching with couple prodfournprice and qty only (later we will search on triplet qty/product_id/fourn_ref)
$sql = " SELECT pfp.rowid, pfp.price as price, pfp.quantity as quantity, pfp.remise_percent, " ;
2020-12-17 18:08:56 +01:00
$sql .= " pfp.fk_product, pfp.ref_fourn, pfp.desc_fourn, pfp.fk_soc, pfp.tva_tx, pfp.fk_supplier_price_expression, " ;
$sql .= " pfp.default_vat_code, " ;
2022-07-24 00:01:03 +02:00
$sql .= " pfp.multicurrency_price, pfp.multicurrency_unitprice, pfp.multicurrency_tx, pfp.fk_multicurrency, pfp.multicurrency_code, " ;
$sql .= " pfp.packaging " ;
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " product_fournisseur_price as pfp " ;
2021-03-30 19:12:07 +02:00
$sql .= " WHERE pfp.rowid = " . (( int ) $prodfournprice );
2021-02-26 14:25:17 +01:00
if ( $qty > 0 ) {
2021-03-30 19:12:07 +02:00
$sql .= " AND pfp.quantity <= " . (( float ) $qty );
2020-09-07 10:18:17 +02:00
}
$sql .= " ORDER BY pfp.quantity DESC " ;
dol_syslog ( get_class ( $this ) . " ::get_buyprice first search by prodfournprice/qty " , LOG_DEBUG );
$resql = $this -> db -> query ( $sql );
if ( $resql ) {
$obj = $this -> db -> fetch_object ( $resql );
2021-02-26 14:25:17 +01:00
if ( $obj && $obj -> quantity > 0 ) { // If we found a supplier prices from the id of supplier price
2020-09-07 10:18:17 +02:00
if ( ! empty ( $conf -> dynamicprices -> enabled ) && ! empty ( $obj -> fk_supplier_price_expression )) {
include_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php' ;
$prod_supplier = new ProductFournisseur ( $this -> db );
$prod_supplier -> product_fourn_price_id = $obj -> rowid ;
$prod_supplier -> id = $obj -> fk_product ;
$prod_supplier -> fourn_qty = $obj -> quantity ;
$prod_supplier -> fourn_tva_tx = $obj -> tva_tx ;
$prod_supplier -> fk_supplier_price_expression = $obj -> fk_supplier_price_expression ;
$priceparser = new PriceParser ( $this -> db );
$price_result = $priceparser -> parseProductSupplier ( $prod_supplier );
if ( $price_result >= 0 ) {
$obj -> price = $price_result ;
}
}
$this -> product_fourn_price_id = $obj -> rowid ;
$this -> buyprice = $obj -> price ; // deprecated
$this -> fourn_pu = $obj -> price / $obj -> quantity ; // Unit price of product of supplier
$this -> fourn_price_base_type = 'HT' ; // Price base type
$this -> fourn_socid = $obj -> fk_soc ; // Company that offer this price
$this -> ref_fourn = $obj -> ref_fourn ; // deprecated
$this -> ref_supplier = $obj -> ref_fourn ; // Ref supplier
$this -> desc_supplier = $obj -> desc_fourn ; // desc supplier
$this -> remise_percent = $obj -> remise_percent ; // remise percent if present and not typed
$this -> vatrate_supplier = $obj -> tva_tx ; // Vat ref supplier
$this -> default_vat_code = $obj -> default_vat_code ; // Vat code supplier
$this -> fourn_multicurrency_price = $obj -> multicurrency_price ;
$this -> fourn_multicurrency_unitprice = $obj -> multicurrency_unitprice ;
$this -> fourn_multicurrency_tx = $obj -> multicurrency_tx ;
$this -> fourn_multicurrency_id = $obj -> fk_multicurrency ;
$this -> fourn_multicurrency_code = $obj -> multicurrency_code ;
2021-02-26 14:25:17 +01:00
if ( ! empty ( $conf -> global -> PRODUCT_USE_SUPPLIER_PACKAGING )) {
$this -> packaging = $obj -> packaging ;
}
2020-09-07 10:18:17 +02:00
$result = $obj -> fk_product ;
return $result ;
2020-12-17 18:08:56 +01:00
} else { // If not found
2020-09-07 10:18:17 +02:00
// We do a second search by doing a select again but searching with less reliable criteria: couple qty/id product, and if set fourn_ref or fk_soc.
2020-12-17 18:08:56 +01:00
$sql = " SELECT pfp.rowid, pfp.price as price, pfp.quantity as quantity, pfp.remise_percent, pfp.fk_soc, " ;
$sql .= " pfp.fk_product, pfp.ref_fourn as ref_supplier, pfp.desc_fourn as desc_supplier, pfp.tva_tx, pfp.fk_supplier_price_expression, " ;
$sql .= " pfp.default_vat_code, " ;
$sql .= " pfp.multicurrency_price, pfp.multicurrency_unitprice, pfp.multicurrency_tx, pfp.fk_multicurrency, pfp.multicurrency_code, " ;
$sql .= " pfp.packaging " ;
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " product_fournisseur_price as pfp " ;
2022-03-07 15:13:25 +01:00
$sql .= " WHERE 1 = 1 " ;
2022-03-07 15:19:51 +01:00
if ( $product_id > 0 ) {
2022-03-07 15:13:25 +01:00
$sql .= " AND pfp.fk_product = " . (( int ) $product_id );
2020-09-07 10:18:17 +02:00
}
2021-02-26 14:25:17 +01:00
if ( $fourn_ref != 'none' ) {
$sql .= " AND pfp.ref_fourn = ' " . $this -> db -> escape ( $fourn_ref ) . " ' " ;
2020-09-07 10:18:17 +02:00
}
2021-02-26 14:25:17 +01:00
if ( $fk_soc > 0 ) {
2021-03-30 19:12:07 +02:00
$sql .= " AND pfp.fk_soc = " . (( int ) $fk_soc );
2020-09-07 10:18:17 +02:00
}
2021-02-26 14:25:17 +01:00
if ( $qty > 0 ) {
2021-03-30 19:12:07 +02:00
$sql .= " AND pfp.quantity <= " . (( float ) $qty );
2020-09-07 10:18:17 +02:00
}
$sql .= " ORDER BY pfp.quantity DESC " ;
$sql .= " LIMIT 1 " ;
dol_syslog ( get_class ( $this ) . " ::get_buyprice second search from qty/ref/product_id " , LOG_DEBUG );
$resql = $this -> db -> query ( $sql );
if ( $resql ) {
$obj = $this -> db -> fetch_object ( $resql );
2021-02-26 14:25:17 +01:00
if ( $obj && $obj -> quantity > 0 ) { // If found
2020-09-07 10:18:17 +02:00
if ( ! empty ( $conf -> dynamicprices -> enabled ) && ! empty ( $obj -> fk_supplier_price_expression )) {
include_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php' ;
$prod_supplier = new ProductFournisseur ( $this -> db );
$prod_supplier -> product_fourn_price_id = $obj -> rowid ;
$prod_supplier -> id = $obj -> fk_product ;
$prod_supplier -> fourn_qty = $obj -> quantity ;
$prod_supplier -> fourn_tva_tx = $obj -> tva_tx ;
$prod_supplier -> fk_supplier_price_expression = $obj -> fk_supplier_price_expression ;
$priceparser = new PriceParser ( $this -> db );
$price_result = $priceparser -> parseProductSupplier ( $prod_supplier );
if ( $result >= 0 ) {
$obj -> price = $price_result ;
}
}
$this -> product_fourn_price_id = $obj -> rowid ;
$this -> buyprice = $obj -> price ; // deprecated
$this -> fourn_qty = $obj -> quantity ; // min quantity for price for a virtual supplier
$this -> fourn_pu = $obj -> price / $obj -> quantity ; // Unit price of product for a virtual supplier
$this -> fourn_price_base_type = 'HT' ; // Price base type for a virtual supplier
$this -> fourn_socid = $obj -> fk_soc ; // Company that offer this price
$this -> ref_fourn = $obj -> ref_supplier ; // deprecated
$this -> ref_supplier = $obj -> ref_supplier ; // Ref supplier
$this -> desc_supplier = $obj -> desc_supplier ; // desc supplier
$this -> remise_percent = $obj -> remise_percent ; // remise percent if present and not typed
$this -> vatrate_supplier = $obj -> tva_tx ; // Vat ref supplier
$this -> default_vat_code = $obj -> default_vat_code ; // Vat code supplier
$this -> fourn_multicurrency_price = $obj -> multicurrency_price ;
$this -> fourn_multicurrency_unitprice = $obj -> multicurrency_unitprice ;
$this -> fourn_multicurrency_tx = $obj -> multicurrency_tx ;
$this -> fourn_multicurrency_id = $obj -> fk_multicurrency ;
$this -> fourn_multicurrency_code = $obj -> multicurrency_code ;
2021-02-26 14:25:17 +01:00
if ( ! empty ( $conf -> global -> PRODUCT_USE_SUPPLIER_PACKAGING )) {
$this -> packaging = $obj -> packaging ;
}
2020-09-07 10:18:17 +02:00
$result = $obj -> fk_product ;
return $result ;
} else {
return - 1 ; // Ce produit n'existe pas avec cet id tarif fournisseur ou existe mais qte insuffisante, ni pour le couple produit/ref fournisseur dans la quantité.
}
} else {
$this -> error = $this -> db -> lasterror ();
return - 3 ;
}
}
} else {
$this -> error = $this -> db -> lasterror ();
return - 2 ;
}
}
/**
2022-05-06 12:37:18 +02:00
* Modify customer price of a product / Service for a given level
2020-09-07 10:18:17 +02:00
*
* @ param double $newprice New price
* @ param string $newpricebase HT or TTC
* @ param User $user Object user that make change
* @ param double $newvat New VAT Rate ( For example 8.5 . Should not be a string )
* @ param double $newminprice New price min
* @ param int $level 0 = standard , > 0 = level if multilevel prices
* @ param int $newnpr 0 = Standard vat rate , 1 = Special vat rate for French NPR VAT
* @ param int $newpbq 1 if it has price by quantity
* @ param int $ignore_autogen Used to avoid infinite loops
* @ param array $localtaxes_array Array with localtaxes info array ( '0' => type1 , '1' => rate1 , '2' => type2 , '3' => rate2 ) ( loaded by getLocalTaxesFromRate ( vatrate , 0 , ... ) function ) .
* @ param string $newdefaultvatcode Default vat code
* @ return int < 0 if KO , > 0 if OK
*/
public function updatePrice ( $newprice , $newpricebase , $user , $newvat = '' , $newminprice = 0 , $level = 0 , $newnpr = 0 , $newpbq = 0 , $ignore_autogen = 0 , $localtaxes_array = array (), $newdefaultvatcode = '' )
{
global $conf , $langs ;
2021-05-19 11:52:34 +02:00
$lastPriceData = $this -> getArrayForPriceCompare ( $level ); // temporary store current price before update
2020-09-07 10:18:17 +02:00
$id = $this -> id ;
dol_syslog ( get_class ( $this ) . " ::update_price id= " . $id . " newprice= " . $newprice . " newpricebase= " . $newpricebase . " newminprice= " . $newminprice . " level= " . $level . " npr= " . $newnpr . " newdefaultvatcode= " . $newdefaultvatcode );
// Clean parameters
if ( empty ( $this -> tva_tx )) {
$this -> tva_tx = 0 ;
}
if ( empty ( $newnpr )) {
$newnpr = 0 ;
}
if ( empty ( $newminprice )) {
$newminprice = 0 ;
}
if ( empty ( $newminprice )) {
$newminprice = 0 ;
}
// Check parameters
if ( $newvat == '' ) {
$newvat = $this -> tva_tx ;
}
// If multiprices are enabled, then we check if the current product is subject to price autogeneration
// Price will be modified ONLY when the first one is the one that is being modified
if (( ! empty ( $conf -> global -> PRODUIT_MULTIPRICES ) || ! empty ( $conf -> global -> PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES )) && ! $ignore_autogen && $this -> price_autogen && ( $level == 1 )) {
return $this -> generateMultiprices ( $user , $newprice , $newpricebase , $newvat , $newnpr , $newpbq );
}
if ( ! empty ( $newminprice ) && ( $newminprice > $newprice )) {
$this -> error = 'ErrorPriceCantBeLowerThanMinPrice' ;
return - 1 ;
}
if ( $newprice !== '' || $newprice === 0 ) {
if ( $newpricebase == 'TTC' ) {
$price_ttc = price2num ( $newprice , 'MU' );
$price = price2num ( $newprice ) / ( 1 + ( $newvat / 100 ));
$price = price2num ( $price , 'MU' );
if ( $newminprice != '' || $newminprice == 0 ) {
$price_min_ttc = price2num ( $newminprice , 'MU' );
$price_min = price2num ( $newminprice ) / ( 1 + ( $newvat / 100 ));
$price_min = price2num ( $price_min , 'MU' );
} else {
$price_min = 0 ;
$price_min_ttc = 0 ;
}
} else {
$price = price2num ( $newprice , 'MU' );
$price_ttc = ( $newnpr != 1 ) ? price2num ( $newprice ) * ( 1 + ( $newvat / 100 )) : $price ;
$price_ttc = price2num ( $price_ttc , 'MU' );
if ( $newminprice !== '' || $newminprice === 0 ) {
$price_min = price2num ( $newminprice , 'MU' );
$price_min_ttc = price2num ( $newminprice ) * ( 1 + ( $newvat / 100 ));
$price_min_ttc = price2num ( $price_min_ttc , 'MU' );
//print 'X'.$newminprice.'-'.$price_min;
} else {
$price_min = 0 ;
$price_min_ttc = 0 ;
}
}
//print 'x'.$id.'-'.$newprice.'-'.$newpricebase.'-'.$price.'-'.$price_ttc.'-'.$price_min.'-'.$price_min_ttc;
if ( count ( $localtaxes_array ) > 0 ) {
$localtaxtype1 = $localtaxes_array [ '0' ];
$localtax1 = $localtaxes_array [ '1' ];
$localtaxtype2 = $localtaxes_array [ '2' ];
$localtax2 = $localtaxes_array [ '3' ];
2020-10-23 20:08:35 +02:00
} else // old method. deprecated because ot can't retrieve type
2020-09-07 10:18:17 +02:00
{
$localtaxtype1 = '0' ;
$localtax1 = get_localtax ( $newvat , 1 );
$localtaxtype2 = '0' ;
$localtax2 = get_localtax ( $newvat , 2 );
}
if ( empty ( $localtax1 )) {
$localtax1 = 0 ; // If = '' then = 0
}
if ( empty ( $localtax2 )) {
$localtax2 = 0 ; // If = '' then = 0
}
$this -> db -> begin ();
// Ne pas mettre de quote sur les numeriques decimaux.
// Ceci provoque des stockages avec arrondis en base au lieu des valeurs exactes.
2022-01-27 10:12:10 +01:00
$sql = " UPDATE " . $this -> db -> prefix () . " product SET " ;
2020-09-20 02:57:15 +02:00
$sql .= " price_base_type=' " . $this -> db -> escape ( $newpricebase ) . " ', " ;
2020-09-07 10:18:17 +02:00
$sql .= " price= " . $price . " , " ;
$sql .= " price_ttc= " . $price_ttc . " , " ;
$sql .= " price_min= " . $price_min . " , " ;
$sql .= " price_min_ttc= " . $price_min_ttc . " , " ;
$sql .= " localtax1_tx= " . ( $localtax1 >= 0 ? $localtax1 : 'NULL' ) . " , " ;
$sql .= " localtax2_tx= " . ( $localtax2 >= 0 ? $localtax2 : 'NULL' ) . " , " ;
2020-09-20 02:57:15 +02:00
$sql .= " localtax1_type= " . ( $localtaxtype1 != '' ? " ' " . $this -> db -> escape ( $localtaxtype1 ) . " ' " : " '0' " ) . " , " ;
$sql .= " localtax2_type= " . ( $localtaxtype2 != '' ? " ' " . $this -> db -> escape ( $localtaxtype2 ) . " ' " : " '0' " ) . " , " ;
2020-09-07 10:18:17 +02:00
$sql .= " default_vat_code= " . ( $newdefaultvatcode ? " ' " . $this -> db -> escape ( $newdefaultvatcode ) . " ' " : " null " ) . " , " ;
$sql .= " tva_tx=' " . price2num ( $newvat ) . " ', " ;
2020-09-20 02:57:15 +02:00
$sql .= " recuperableonly=' " . $this -> db -> escape ( $newnpr ) . " ' " ;
2021-03-14 11:48:39 +01:00
$sql .= " WHERE rowid = " . (( int ) $id );
2020-09-07 10:18:17 +02:00
dol_syslog ( get_class ( $this ) . " ::update_price " , LOG_DEBUG );
$resql = $this -> db -> query ( $sql );
if ( $resql ) {
$this -> multiprices [ $level ] = $price ;
$this -> multiprices_ttc [ $level ] = $price_ttc ;
$this -> multiprices_min [ $level ] = $price_min ;
$this -> multiprices_min_ttc [ $level ] = $price_min_ttc ;
$this -> multiprices_base_type [ $level ] = $newpricebase ;
$this -> multiprices_default_vat_code [ $level ] = $newdefaultvatcode ;
$this -> multiprices_tva_tx [ $level ] = $newvat ;
$this -> multiprices_recuperableonly [ $level ] = $newnpr ;
$this -> price = $price ;
$this -> price_ttc = $price_ttc ;
$this -> price_min = $price_min ;
$this -> price_min_ttc = $price_min_ttc ;
$this -> price_base_type = $newpricebase ;
$this -> default_vat_code = $newdefaultvatcode ;
$this -> tva_tx = $newvat ;
$this -> tva_npr = $newnpr ;
//Local taxes
$this -> localtax1_tx = $localtax1 ;
$this -> localtax2_tx = $localtax2 ;
$this -> localtax1_type = $localtaxtype1 ;
$this -> localtax2_type = $localtaxtype2 ;
// Price by quantity
$this -> price_by_qty = $newpbq ;
2021-05-19 11:52:34 +02:00
// check if price have really change before log
$newPriceData = $this -> getArrayForPriceCompare ( $level );
if ( ! empty ( array_diff_assoc ( $newPriceData , $lastPriceData )) || empty ( $conf -> global -> PRODUIT_MULTIPRICES )) {
$this -> _log_price ( $user , $level ); // Save price for level into table product_price
}
2020-09-07 10:18:17 +02:00
$this -> level = $level ; // Store level of price edited for trigger
// Call trigger
$result = $this -> call_trigger ( 'PRODUCT_PRICE_MODIFY' , $user );
if ( $result < 0 ) {
$this -> db -> rollback ();
return - 1 ;
}
// End call triggers
$this -> db -> commit ();
} else {
$this -> db -> rollback ();
2022-03-08 14:49:26 +01:00
$this -> error = $this -> db -> lasterror ();
return - 1 ;
2020-09-07 10:18:17 +02:00
}
}
return 1 ;
}
/**
* Sets the supplier price expression
*
* @ param int $expression_id Expression
* @ return int < 0 if KO , > 0 if OK
* @ deprecated Use Product :: update instead
*/
public function setPriceExpression ( $expression_id )
{
global $user ;
$this -> fk_price_expression = $expression_id ;
return $this -> update ( $this -> id , $user );
}
/**
* Load a product in memory from database
*
* @ param int $id Id of product / service to load
* @ param string $ref Ref of product / service to load
* @ param string $ref_ext Ref ext of product / service to load
* @ param string $barcode Barcode of product / service to load
2021-08-18 18:43:33 +02:00
* @ param int $ignore_expression When module dynamicprices is on , ignores the math expression for calculating price and uses the db value instead
2021-09-08 20:59:56 +02:00
* @ param int $ignore_price_load Load product without loading $this -> multiprices ... array ( when we are sure we don ' t need them )
* @ param int $ignore_lang_load Load product without loading $this -> multilangs language arrays ( when we are sure we don ' t need them )
2020-09-07 10:18:17 +02:00
* @ return int < 0 if KO , 0 if not found , > 0 if OK
*/
public function fetch ( $id = '' , $ref = '' , $ref_ext = '' , $barcode = '' , $ignore_expression = 0 , $ignore_price_load = 0 , $ignore_lang_load = 0 )
{
include_once DOL_DOCUMENT_ROOT . '/core/lib/company.lib.php' ;
global $langs , $conf ;
dol_syslog ( get_class ( $this ) . " ::fetch id= " . $id . " ref= " . $ref . " ref_ext= " . $ref_ext );
// Check parameters
if ( ! $id && ! $ref && ! $ref_ext && ! $barcode ) {
$this -> error = 'ErrorWrongParameters' ;
2021-07-05 12:49:10 +02:00
dol_syslog ( get_class ( $this ) . " ::fetch " . $this -> error , LOG_ERR );
2020-09-07 10:18:17 +02:00
return - 1 ;
}
2021-04-28 17:08:17 +02:00
$sql = " SELECT p.rowid, p.ref, p.ref_ext, p.label, p.description, p.url, p.note_public, p.note as note_private, p.customcode, p.fk_country, p.fk_state, p.lifetime, p.qc_frequency, p.price, p.price_ttc, " ;
2021-03-20 07:42:16 +01:00
$sql .= " p.price_min, p.price_min_ttc, p.price_base_type, p.cost_price, p.default_vat_code, p.tva_tx, p.recuperableonly as tva_npr, p.localtax1_tx, p.localtax2_tx, p.localtax1_type, p.localtax2_type, p.tosell, " ;
$sql .= " p.tobuy, p.fk_product_type, p.duration, p.fk_default_warehouse, p.seuil_stock_alerte, p.canvas, p.net_measure, p.net_measure_units, p.weight, p.weight_units, " ;
$sql .= " p.length, p.length_units, p.width, p.width_units, p.height, p.height_units, " ;
2021-09-06 09:43:52 +02:00
$sql .= " p.surface, p.surface_units, p.volume, p.volume_units, p.barcode, p.fk_barcode_type, p.finished, p.fk_default_bom, p.mandatory_period, " ;
2021-03-27 05:23:20 +01:00
if ( empty ( $conf -> global -> MAIN_PRODUCT_PERENTITY_SHARED )) {
2021-03-20 07:42:16 +01:00
$sql .= " p.accountancy_code_buy, p.accountancy_code_buy_intra, p.accountancy_code_buy_export, p.accountancy_code_sell, p.accountancy_code_sell_intra, p.accountancy_code_sell_export, " ;
} else {
2021-04-14 04:45:17 +02:00
$sql .= " ppe.accountancy_code_buy, ppe.accountancy_code_buy_intra, ppe.accountancy_code_buy_export, ppe.accountancy_code_sell, ppe.accountancy_code_sell_intra, ppe.accountancy_code_sell_export, " ;
2021-03-20 07:42:16 +01:00
}
2021-04-27 09:54:38 +02:00
2021-04-27 09:58:36 +02:00
//For MultiCompany
//PMP per entity & Stocks Sharings stock_reel includes only stocks shared with this entity
2021-06-16 20:17:21 +02:00
$separatedEntityPMP = false ; // Set to true to get the AWP from table llx_product_perentity instead of field 'pmp' into llx_product.
$separatedStock = false ; // Set to true will count stock from subtable llx_product_stock. It is slower than using denormalized field 'stock', but it is required when using multientity and shared warehouses.
2022-05-18 12:40:44 +02:00
$visibleWarehousesEntities = $conf -> entity ;
2021-04-27 09:54:38 +02:00
if ( ! empty ( $conf -> global -> MULTICOMPANY_PRODUCT_SHARING_ENABLED )) {
if ( ! empty ( $conf -> global -> MULTICOMPANY_PMP_PER_ENTITY_ENABLED )) {
2022-01-27 10:12:10 +01:00
$checkPMPPerEntity = $this -> db -> query ( " SELECT pmp FROM " . $this -> db -> prefix () . " product_perentity WHERE fk_product = " . (( int ) $id ) . " AND entity = " . ( int ) $conf -> entity );
2021-04-27 09:54:38 +02:00
if ( $this -> db -> num_rows ( $checkPMPPerEntity ) > 0 ) {
$separatedEntityPMP = true ;
}
}
2021-04-26 19:29:56 +02:00
global $mc ;
$separatedStock = true ;
2021-04-26 20:06:45 +02:00
if ( isset ( $mc -> sharings [ 'stock' ]) && ! empty ( $mc -> sharings [ 'stock' ])) {
2021-04-26 20:10:57 +02:00
$visibleWarehousesEntities .= " , " . implode ( " , " , $mc -> sharings [ 'stock' ]);
2021-04-26 19:29:56 +02:00
}
}
2021-06-16 20:17:21 +02:00
if ( $separatedEntityPMP ) {
$sql .= " ppe.pmp, " ;
2021-04-26 20:13:55 +02:00
} else {
2021-06-16 20:17:21 +02:00
$sql .= " p.pmp, " ;
2021-04-26 19:29:56 +02:00
}
2021-06-16 20:17:21 +02:00
$sql .= " p.datec, p.tms, p.import_key, p.entity, p.desiredstock, p.tobatch, p.batch_mask, p.fk_unit, " ;
$sql .= " p.fk_price_expression, p.price_autogen, p.model_pdf, " ;
if ( $separatedStock ) {
$sql .= " SUM(sp.reel) as stock " ;
2021-04-26 20:13:55 +02:00
} else {
2021-06-16 20:17:21 +02:00
$sql .= " p.stock " ;
2021-04-26 19:29:56 +02:00
}
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " product as p " ;
2021-04-27 09:54:38 +02:00
if ( ! empty ( $conf -> global -> MAIN_PRODUCT_PERENTITY_SHARED ) || $separatedEntityPMP ) {
2022-01-27 10:12:10 +01:00
$sql .= " LEFT JOIN " . $this -> db -> prefix () . " product_perentity as ppe ON ppe.fk_product = p.rowid AND ppe.entity = " . (( int ) $conf -> entity );
2021-03-20 07:42:16 +01:00
}
2021-04-26 20:06:45 +02:00
if ( $separatedStock ) {
2022-01-27 10:12:10 +01:00
$sql .= " LEFT JOIN " . $this -> db -> prefix () . " product_stock as sp ON sp.fk_product = p.rowid AND sp.fk_entrepot IN (SELECT rowid FROM " . $this -> db -> prefix () . " entrepot WHERE entity IN ( " . $this -> db -> sanitize ( $visibleWarehousesEntities ) . " )) " ;
2021-04-26 19:29:56 +02:00
}
2021-08-11 21:43:42 +02:00
2020-09-07 10:18:17 +02:00
if ( $id ) {
2021-03-30 02:13:57 +02:00
$sql .= " WHERE p.rowid = " . (( int ) $id );
2020-09-07 10:18:17 +02:00
} else {
2021-03-30 02:13:57 +02:00
$sql .= " WHERE p.entity IN ( " . getEntity ( $this -> element ) . " ) " ;
2020-09-07 10:18:17 +02:00
if ( $ref ) {
2021-03-30 02:13:57 +02:00
$sql .= " AND p.ref = ' " . $this -> db -> escape ( $ref ) . " ' " ;
2020-09-07 10:18:17 +02:00
} elseif ( $ref_ext ) {
2021-03-30 02:13:57 +02:00
$sql .= " AND p.ref_ext = ' " . $this -> db -> escape ( $ref_ext ) . " ' " ;
2020-09-07 10:18:17 +02:00
} elseif ( $barcode ) {
2021-03-30 02:13:57 +02:00
$sql .= " AND p.barcode = ' " . $this -> db -> escape ( $barcode ) . " ' " ;
2020-09-07 10:18:17 +02:00
}
}
2021-06-16 20:17:21 +02:00
if ( $separatedStock ) {
$sql .= " GROUP BY p.rowid, p.ref, p.ref_ext, p.label, p.description, p.url, p.note_public, p.note, p.customcode, p.fk_country, p.fk_state, p.lifetime, p.qc_frequency, p.price, p.price_ttc, " ;
$sql .= " p.price_min, p.price_min_ttc, p.price_base_type, p.cost_price, p.default_vat_code, p.tva_tx, p.recuperableonly, p.localtax1_tx, p.localtax2_tx, p.localtax1_type, p.localtax2_type, p.tosell, " ;
$sql .= " p.tobuy, p.fk_product_type, p.duration, p.fk_default_warehouse, p.seuil_stock_alerte, p.canvas, p.net_measure, p.net_measure_units, p.weight, p.weight_units, " ;
$sql .= " p.length, p.length_units, p.width, p.width_units, p.height, p.height_units, " ;
$sql .= " p.surface, p.surface_units, p.volume, p.volume_units, p.barcode, p.fk_barcode_type, p.finished, " ;
if ( empty ( $conf -> global -> MAIN_PRODUCT_PERENTITY_SHARED )) {
$sql .= " p.accountancy_code_buy, p.accountancy_code_buy_intra, p.accountancy_code_buy_export, p.accountancy_code_sell, p.accountancy_code_sell_intra, p.accountancy_code_sell_export, " ;
} else {
$sql .= " ppe.accountancy_code_buy, ppe.accountancy_code_buy_intra, ppe.accountancy_code_buy_export, ppe.accountancy_code_sell, ppe.accountancy_code_sell_intra, ppe.accountancy_code_sell_export, " ;
}
if ( $separatedEntityPMP ) {
$sql .= " ppe.pmp, " ;
} else {
$sql .= " p.pmp, " ;
}
$sql .= " p.datec, p.tms, p.import_key, p.entity, p.desiredstock, p.tobatch, p.batch_mask, p.fk_unit, " ;
$sql .= " p.fk_price_expression, p.price_autogen, p.model_pdf " ;
if ( ! $separatedStock ) {
$sql .= " , p.stock " ;
2020-09-07 10:18:17 +02:00
}
}
$resql = $this -> db -> query ( $sql );
if ( $resql ) {
unset ( $this -> oldcopy );
if ( $this -> db -> num_rows ( $resql ) > 0 ) {
$obj = $this -> db -> fetch_object ( $resql );
$this -> id = $obj -> rowid ;
$this -> ref = $obj -> ref ;
$this -> ref_ext = $obj -> ref_ext ;
$this -> label = $obj -> label ;
$this -> description = $obj -> description ;
$this -> url = $obj -> url ;
$this -> note_public = $obj -> note_public ;
$this -> note_private = $obj -> note_private ;
$this -> note = $obj -> note_private ; // deprecated
$this -> type = $obj -> fk_product_type ;
$this -> status = $obj -> tosell ;
$this -> status_buy = $obj -> tobuy ;
$this -> status_batch = $obj -> tobatch ;
2021-04-08 18:03:46 +02:00
$this -> batch_mask = $obj -> batch_mask ;
2020-09-07 10:18:17 +02:00
$this -> customcode = $obj -> customcode ;
$this -> country_id = $obj -> fk_country ;
$this -> country_code = getCountry ( $this -> country_id , 2 , $this -> db );
2020-10-31 14:32:18 +01:00
$this -> state_id = $obj -> fk_state ;
2021-04-28 17:08:17 +02:00
$this -> lifetime = $obj -> lifetime ;
$this -> qc_frequency = $obj -> qc_frequency ;
2020-09-07 10:18:17 +02:00
$this -> price = $obj -> price ;
$this -> price_ttc = $obj -> price_ttc ;
$this -> price_min = $obj -> price_min ;
$this -> price_min_ttc = $obj -> price_min_ttc ;
$this -> price_base_type = $obj -> price_base_type ;
$this -> cost_price = $obj -> cost_price ;
$this -> default_vat_code = $obj -> default_vat_code ;
$this -> tva_tx = $obj -> tva_tx ;
//! French VAT NPR
$this -> tva_npr = $obj -> tva_npr ;
$this -> recuperableonly = $obj -> tva_npr ; // For backward compatibility
//! Local taxes
$this -> localtax1_tx = $obj -> localtax1_tx ;
$this -> localtax2_tx = $obj -> localtax2_tx ;
$this -> localtax1_type = $obj -> localtax1_type ;
$this -> localtax2_type = $obj -> localtax2_type ;
2021-08-11 21:36:18 +02:00
2020-09-07 10:18:17 +02:00
$this -> finished = $obj -> finished ;
2020-11-26 11:59:00 +01:00
$this -> fk_default_bom = $obj -> fk_default_bom ;
2020-09-07 10:18:17 +02:00
$this -> duration = $obj -> duration ;
$this -> duration_value = substr ( $obj -> duration , 0 , dol_strlen ( $obj -> duration ) - 1 );
$this -> duration_unit = substr ( $obj -> duration , - 1 );
$this -> canvas = $obj -> canvas ;
$this -> net_measure = $obj -> net_measure ;
$this -> net_measure_units = $obj -> net_measure_units ;
$this -> weight = $obj -> weight ;
$this -> weight_units = $obj -> weight_units ;
$this -> length = $obj -> length ;
$this -> length_units = $obj -> length_units ;
$this -> width = $obj -> width ;
$this -> width_units = $obj -> width_units ;
$this -> height = $obj -> height ;
$this -> height_units = $obj -> height_units ;
$this -> surface = $obj -> surface ;
$this -> surface_units = $obj -> surface_units ;
$this -> volume = $obj -> volume ;
$this -> volume_units = $obj -> volume_units ;
$this -> barcode = $obj -> barcode ;
$this -> barcode_type = $obj -> fk_barcode_type ;
$this -> accountancy_code_buy = $obj -> accountancy_code_buy ;
2021-04-14 04:45:17 +02:00
$this -> accountancy_code_buy_intra = $obj -> accountancy_code_buy_intra ;
2020-09-07 10:18:17 +02:00
$this -> accountancy_code_buy_export = $obj -> accountancy_code_buy_export ;
$this -> accountancy_code_sell = $obj -> accountancy_code_sell ;
$this -> accountancy_code_sell_intra = $obj -> accountancy_code_sell_intra ;
$this -> accountancy_code_sell_export = $obj -> accountancy_code_sell_export ;
$this -> fk_default_warehouse = $obj -> fk_default_warehouse ;
$this -> seuil_stock_alerte = $obj -> seuil_stock_alerte ;
$this -> desiredstock = $obj -> desiredstock ;
$this -> stock_reel = $obj -> stock ;
$this -> pmp = $obj -> pmp ;
$this -> date_creation = $obj -> datec ;
$this -> date_modification = $obj -> tms ;
$this -> import_key = $obj -> import_key ;
$this -> entity = $obj -> entity ;
$this -> ref_ext = $obj -> ref_ext ;
$this -> fk_price_expression = $obj -> fk_price_expression ;
$this -> fk_unit = $obj -> fk_unit ;
$this -> price_autogen = $obj -> price_autogen ;
$this -> model_pdf = $obj -> model_pdf ;
2021-07-19 14:03:03 +02:00
$this -> mandatory_period = $obj -> mandatory_period ;
2020-09-07 10:18:17 +02:00
$this -> db -> free ( $resql );
// fetch optionals attributes and labels
$this -> fetch_optionals ();
2021-05-12 11:04:22 +02:00
// Multilangs
2020-09-07 10:18:17 +02:00
if ( ! empty ( $conf -> global -> MAIN_MULTILANGS ) && empty ( $ignore_lang_load )) {
$this -> getMultiLangs ();
}
// Load multiprices array
2021-02-26 14:25:17 +01:00
if ( ! empty ( $conf -> global -> PRODUIT_MULTIPRICES ) && empty ( $ignore_price_load )) { // prices per segment
for ( $i = 1 ; $i <= $conf -> global -> PRODUIT_MULTIPRICES_LIMIT ; $i ++ ) {
2020-09-07 10:18:17 +02:00
$sql = " SELECT price, price_ttc, price_min, price_min_ttc, " ;
$sql .= " price_base_type, tva_tx, default_vat_code, tosell, price_by_qty, rowid, recuperableonly " ;
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " product_price " ;
2020-09-07 10:18:17 +02:00
$sql .= " WHERE entity IN ( " . getEntity ( 'productprice' ) . " ) " ;
2021-06-09 15:36:47 +02:00
$sql .= " AND price_level= " . (( int ) $i );
$sql .= " AND fk_product = " . (( int ) $this -> id );
2021-08-18 18:43:33 +02:00
$sql .= " ORDER BY date_price DESC, rowid DESC " ; // Get the most recent line
$sql .= " LIMIT 1 " ; // Only the first one
2020-09-07 10:18:17 +02:00
$resql = $this -> db -> query ( $sql );
if ( $resql ) {
$result = $this -> db -> fetch_array ( $resql );
2020-12-06 19:33:43 +01:00
$this -> multiprices [ $i ] = $result ? $result [ " price " ] : null ;
$this -> multiprices_ttc [ $i ] = $result ? $result [ " price_ttc " ] : null ;
$this -> multiprices_min [ $i ] = $result ? $result [ " price_min " ] : null ;
$this -> multiprices_min_ttc [ $i ] = $result ? $result [ " price_min_ttc " ] : null ;
$this -> multiprices_base_type [ $i ] = $result ? $result [ " price_base_type " ] : null ;
2020-09-07 10:18:17 +02:00
// Next two fields are used only if PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL is on
2020-12-06 19:33:43 +01:00
$this -> multiprices_tva_tx [ $i ] = $result ? $result [ " tva_tx " ] . ( $result ? ' (' . $result [ 'default_vat_code' ] . ')' : '' ) : null ;
$this -> multiprices_recuperableonly [ $i ] = $result ? $result [ " recuperableonly " ] : null ;
2020-09-07 10:18:17 +02:00
// Price by quantity
/*
2021-06-16 20:17:21 +02:00
$this -> prices_by_qty [ $i ] = $result [ " price_by_qty " ];
$this -> prices_by_qty_id [ $i ] = $result [ " rowid " ];
// Récuperation de la liste des prix selon qty si flag positionné
if ( $this -> prices_by_qty [ $i ] == 1 )
{
$sql = " SELECT rowid, price, unitprice, quantity, remise_percent, remise, price_base_type " ;
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " product_price_by_qty " ;
2021-08-27 18:18:50 +02:00
$sql .= " WHERE fk_product_price = " . (( int ) $this -> prices_by_qty_id [ $i ]);
2021-06-16 20:17:21 +02:00
$sql .= " ORDER BY quantity ASC " ;
$resultat = array ();
$resql = $this -> db -> query ( $sql );
if ( $resql )
{
$ii = 0 ;
while ( $result = $this -> db -> fetch_array ( $resql )) {
$resultat [ $ii ] = array ();
$resultat [ $ii ][ " rowid " ] = $result [ " rowid " ];
$resultat [ $ii ][ " price " ] = $result [ " price " ];
$resultat [ $ii ][ " unitprice " ] = $result [ " unitprice " ];
$resultat [ $ii ][ " quantity " ] = $result [ " quantity " ];
$resultat [ $ii ][ " remise_percent " ] = $result [ " remise_percent " ];
$resultat [ $ii ][ " remise " ] = $result [ " remise " ]; // deprecated
$resultat [ $ii ][ " price_base_type " ] = $result [ " price_base_type " ];
$ii ++ ;
}
$this -> prices_by_qty_list [ $i ] = $resultat ;
}
else
{
dol_print_error ( $this -> db );
return - 1 ;
}
} */
2020-09-07 10:18:17 +02:00
} else {
2020-09-08 21:27:28 +02:00
$this -> error = $this -> db -> lasterror ;
2020-09-07 10:18:17 +02:00
return - 1 ;
}
}
2021-02-26 14:25:17 +01:00
} elseif ( ! empty ( $conf -> global -> PRODUIT_CUSTOMER_PRICES ) && empty ( $ignore_price_load )) { // prices per customers
2020-09-07 10:18:17 +02:00
// Nothing loaded by default. List may be very long.
2021-02-26 14:25:17 +01:00
} elseif ( ! empty ( $conf -> global -> PRODUIT_CUSTOMER_PRICES_BY_QTY ) && empty ( $ignore_price_load )) { // prices per quantity
2020-09-07 10:18:17 +02:00
$sql = " SELECT price, price_ttc, price_min, price_min_ttc, " ;
$sql .= " price_base_type, tva_tx, default_vat_code, tosell, price_by_qty, rowid " ;
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " product_price " ;
2021-03-30 19:12:07 +02:00
$sql .= " WHERE fk_product = " . (( int ) $this -> id );
2020-09-07 10:18:17 +02:00
$sql .= " ORDER BY date_price DESC, rowid DESC " ;
$sql .= " LIMIT 1 " ;
$resql = $this -> db -> query ( $sql );
if ( $resql ) {
$result = $this -> db -> fetch_array ( $resql );
// Price by quantity
$this -> prices_by_qty [ 0 ] = $result [ " price_by_qty " ];
$this -> prices_by_qty_id [ 0 ] = $result [ " rowid " ];
// Récuperation de la liste des prix selon qty si flag positionné
if ( $this -> prices_by_qty [ 0 ] == 1 ) {
$sql = " SELECT rowid,price, unitprice, quantity, remise_percent, remise, remise, price_base_type " ;
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " product_price_by_qty " ;
2021-03-30 19:12:07 +02:00
$sql .= " WHERE fk_product_price = " . (( int ) $this -> prices_by_qty_id [ 0 ]);
2020-09-07 10:18:17 +02:00
$sql .= " ORDER BY quantity ASC " ;
$resultat = array ();
$resql = $this -> db -> query ( $sql );
if ( $resql ) {
$ii = 0 ;
while ( $result = $this -> db -> fetch_array ( $resql )) {
$resultat [ $ii ] = array ();
$resultat [ $ii ][ " rowid " ] = $result [ " rowid " ];
$resultat [ $ii ][ " price " ] = $result [ " price " ];
$resultat [ $ii ][ " unitprice " ] = $result [ " unitprice " ];
$resultat [ $ii ][ " quantity " ] = $result [ " quantity " ];
$resultat [ $ii ][ " remise_percent " ] = $result [ " remise_percent " ];
//$resultat[$ii]["remise"]= $result["remise"]; // deprecated
$resultat [ $ii ][ " price_base_type " ] = $result [ " price_base_type " ];
$ii ++ ;
}
$this -> prices_by_qty_list [ 0 ] = $resultat ;
} else {
2020-09-08 21:27:28 +02:00
$this -> error = $this -> db -> lasterror ;
2020-09-07 10:18:17 +02:00
return - 1 ;
}
}
} else {
2020-09-08 21:27:28 +02:00
$this -> error = $this -> db -> lasterror ;
2020-09-07 10:18:17 +02:00
return - 1 ;
}
2021-02-26 14:25:17 +01:00
} elseif ( ! empty ( $conf -> global -> PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES ) && empty ( $ignore_price_load )) { // prices per customer and quantity
for ( $i = 1 ; $i <= $conf -> global -> PRODUIT_MULTIPRICES_LIMIT ; $i ++ ) {
2020-09-07 10:18:17 +02:00
$sql = " SELECT price, price_ttc, price_min, price_min_ttc, " ;
$sql .= " price_base_type, tva_tx, default_vat_code, tosell, price_by_qty, rowid, recuperableonly " ;
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " product_price " ;
2020-09-07 10:18:17 +02:00
$sql .= " WHERE entity IN ( " . getEntity ( 'productprice' ) . " ) " ;
2021-06-09 15:36:47 +02:00
$sql .= " AND price_level= " . (( int ) $i );
2021-08-23 18:56:46 +02:00
$sql .= " AND fk_product = " . (( int ) $this -> id );
2020-09-07 10:18:17 +02:00
$sql .= " ORDER BY date_price DESC, rowid DESC " ;
$sql .= " LIMIT 1 " ;
$resql = $this -> db -> query ( $sql );
if ( $resql ) {
$result = $this -> db -> fetch_array ( $resql );
$this -> multiprices [ $i ] = $result [ " price " ];
$this -> multiprices_ttc [ $i ] = $result [ " price_ttc " ];
$this -> multiprices_min [ $i ] = $result [ " price_min " ];
$this -> multiprices_min_ttc [ $i ] = $result [ " price_min_ttc " ];
$this -> multiprices_base_type [ $i ] = $result [ " price_base_type " ];
// Next two fields are used only if PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL is on
$this -> multiprices_tva_tx [ $i ] = $result [ " tva_tx " ]; // TODO Add ' ('.$result['default_vat_code'].')'
$this -> multiprices_recuperableonly [ $i ] = $result [ " recuperableonly " ];
// Price by quantity
$this -> prices_by_qty [ $i ] = $result [ " price_by_qty " ];
$this -> prices_by_qty_id [ $i ] = $result [ " rowid " ];
// Récuperation de la liste des prix selon qty si flag positionné
if ( $this -> prices_by_qty [ $i ] == 1 ) {
$sql = " SELECT rowid, price, unitprice, quantity, remise_percent, remise, price_base_type " ;
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " product_price_by_qty " ;
2021-08-27 18:18:50 +02:00
$sql .= " WHERE fk_product_price = " . (( int ) $this -> prices_by_qty_id [ $i ]);
2020-09-07 10:18:17 +02:00
$sql .= " ORDER BY quantity ASC " ;
$resultat = array ();
$resql = $this -> db -> query ( $sql );
if ( $resql ) {
$ii = 0 ;
while ( $result = $this -> db -> fetch_array ( $resql )) {
$resultat [ $ii ] = array ();
$resultat [ $ii ][ " rowid " ] = $result [ " rowid " ];
$resultat [ $ii ][ " price " ] = $result [ " price " ];
$resultat [ $ii ][ " unitprice " ] = $result [ " unitprice " ];
$resultat [ $ii ][ " quantity " ] = $result [ " quantity " ];
$resultat [ $ii ][ " remise_percent " ] = $result [ " remise_percent " ];
$resultat [ $ii ][ " remise " ] = $result [ " remise " ]; // deprecated
$resultat [ $ii ][ " price_base_type " ] = $result [ " price_base_type " ];
$ii ++ ;
}
$this -> prices_by_qty_list [ $i ] = $resultat ;
} else {
2020-09-08 21:27:28 +02:00
$this -> error = $this -> db -> lasterror ;
2020-09-07 10:18:17 +02:00
return - 1 ;
}
}
} else {
2020-09-08 21:27:28 +02:00
$this -> error = $this -> db -> lasterror ;
2020-09-07 10:18:17 +02:00
return - 1 ;
}
}
}
if ( ! empty ( $conf -> dynamicprices -> enabled ) && ! empty ( $this -> fk_price_expression ) && empty ( $ignore_expression )) {
2021-06-16 20:17:21 +02:00
include_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php' ;
2020-09-07 10:18:17 +02:00
$priceparser = new PriceParser ( $this -> db );
2021-06-16 20:17:21 +02:00
$price_result = $priceparser -> parseProduct ( $this );
2020-09-07 10:18:17 +02:00
if ( $price_result >= 0 ) {
$this -> price = $price_result ;
// Calculate the VAT
$this -> price_ttc = price2num ( $this -> price ) * ( 1 + ( $this -> tva_tx / 100 ));
$this -> price_ttc = price2num ( $this -> price_ttc , 'MU' );
}
}
// We should not load stock during the fetch. If someone need stock of product, he must call load_stock after fetching product.
// Instead we just init the stock_warehouse array
$this -> stock_warehouse = array ();
return 1 ;
} else {
return 0 ;
}
} else {
2021-06-16 20:17:21 +02:00
$this -> error = $this -> db -> lasterror ();
2020-09-07 10:18:17 +02:00
return - 1 ;
}
}
2020-10-12 13:52:50 +02:00
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Charge tableau des stats OF pour le produit / service
*
* @ param int $socid Id societe
2020-10-22 22:33:35 +02:00
* @ return int Array of stats in $this -> stats_mo , < 0 if ko or > 0 if ok
2020-10-12 13:52:50 +02:00
*/
2020-10-14 08:47:16 +02:00
public function load_stats_mo ( $socid = 0 )
{
2020-10-12 13:52:50 +02:00
// phpcs:enable
2020-12-27 21:41:39 +01:00
global $user , $hookmanager , $action ;
2020-10-12 13:52:50 +02:00
2020-10-31 14:32:18 +01:00
$error = 0 ;
2020-10-12 13:52:50 +02:00
2020-10-31 14:32:18 +01:00
foreach ( array ( 'toconsume' , 'consumed' , 'toproduce' , 'produced' ) as $role ) {
2020-10-14 08:42:57 +02:00
$this -> stats_mo [ 'customers_' . $role ] = 0 ;
$this -> stats_mo [ 'nb_' . $role ] = 0 ;
$this -> stats_mo [ 'qty_' . $role ] = 0 ;
2020-10-12 13:52:50 +02:00
2020-10-14 08:42:57 +02:00
$sql = " SELECT COUNT(DISTINCT c.fk_soc) as nb_customers, COUNT(DISTINCT c.rowid) as nb, " ;
$sql .= " SUM(mp.qty) as qty " ;
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " mrp_mo as c " ;
$sql .= " INNER JOIN " . $this -> db -> prefix () . " mrp_production as mp ON mp.fk_mo=c.rowid " ;
2020-12-27 21:41:39 +01:00
if ( empty ( $user -> rights -> societe -> client -> voir ) && ! $socid ) {
2022-01-27 10:12:10 +01:00
$sql .= " INNER JOIN " . $this -> db -> prefix () . " societe_commerciaux as sc ON sc.fk_soc=c.fk_soc AND sc.fk_user = " . (( int ) $user -> id );
2020-10-14 08:42:57 +02:00
}
$sql .= " WHERE " ;
$sql .= " c.entity IN ( " . getEntity ( 'mo' ) . " ) " ;
2020-10-12 13:52:50 +02:00
2021-08-23 18:56:46 +02:00
$sql .= " AND mp.fk_product = " . (( int ) $this -> id );
2020-10-14 11:20:27 +02:00
$sql .= " AND mp.role =' " . $this -> db -> escape ( $role ) . " ' " ;
2020-10-14 08:42:57 +02:00
if ( $socid > 0 ) {
2021-06-09 15:36:47 +02:00
$sql .= " AND c.fk_soc = " . (( int ) $socid );
2020-10-14 08:42:57 +02:00
}
2020-10-12 13:52:50 +02:00
2020-10-14 08:42:57 +02:00
$result = $this -> db -> query ( $sql );
if ( $result ) {
$obj = $this -> db -> fetch_object ( $result );
$this -> stats_mo [ 'customers_' . $role ] = $obj -> nb_customers ? $obj -> nb_customers : 0 ;
$this -> stats_mo [ 'nb_' . $role ] = $obj -> nb ? $obj -> nb : 0 ;
2021-06-23 02:51:31 +02:00
$this -> stats_mo [ 'qty_' . $role ] = $obj -> qty ? price2num ( $obj -> qty , 'MS' ) : 0 ; // qty may be a float due to the SUM()
2020-10-14 08:42:57 +02:00
} else {
$this -> error = $this -> db -> error ();
$error ++ ;
}
}
if ( ! empty ( $error )) {
2020-10-12 13:52:50 +02:00
return - 1 ;
}
2020-10-14 08:42:57 +02:00
$parameters = array ( 'socid' => $socid );
$reshook = $hookmanager -> executeHooks ( 'loadStatsCustomerMO' , $parameters , $this , $action );
2021-02-26 14:25:17 +01:00
if ( $reshook > 0 ) {
$this -> stats_mo = $hookmanager -> resArray [ 'stats_mo' ];
}
2020-10-14 08:42:57 +02:00
return 1 ;
2020-10-12 13:52:50 +02:00
}
2020-09-07 10:18:17 +02:00
2020-10-14 10:30:39 +02:00
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Charge tableau des stats OF pour le produit / service
*
* @ param int $socid Id societe
2020-10-22 22:33:35 +02:00
* @ return int Array of stats in $this -> stats_bom , < 0 if ko or > 0 if ok
2020-10-14 10:30:39 +02:00
*/
2020-10-14 10:34:30 +02:00
public function load_stats_bom ( $socid = 0 )
{
2020-10-14 10:30:39 +02:00
// phpcs:enable
2022-05-18 12:40:44 +02:00
global $user , $hookmanager , $action ;
2020-10-14 10:30:39 +02:00
2020-10-31 14:32:18 +01:00
$error = 0 ;
2020-10-14 10:30:39 +02:00
$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, " ;
2021-02-26 11:27:03 +01:00
$sql .= " SUM(b.qty) as qty_toproduce " ;
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " bom_bom as b " ;
$sql .= " INNER JOIN " . $this -> db -> prefix () . " bom_bomline as bl ON bl.fk_bom=b.rowid " ;
2020-10-14 10:30:39 +02:00
$sql .= " WHERE " ;
$sql .= " b.entity IN ( " . getEntity ( 'bom' ) . " ) " ;
2021-06-12 07:12:45 +02:00
$sql .= " AND b.fk_product = " . (( int ) $this -> id );
2020-10-31 14:32:18 +01:00
$sql .= " GROUP BY b.rowid " ;
2020-10-14 10:30:39 +02:00
$result = $this -> db -> query ( $sql );
if ( $result ) {
$obj = $this -> db -> fetch_object ( $result );
2021-06-12 07:12:45 +02:00
$this -> stats_bom [ 'nb_toproduce' ] = ! empty ( $obj -> nb_toproduce ) ? $obj -> nb_toproduce : 0 ;
$this -> stats_bom [ 'qty_toproduce' ] = ! empty ( $obj -> qty_toproduce ) ? price2num ( $obj -> qty_toproduce ) : 0 ;
2020-10-14 10:30:39 +02:00
} else {
$this -> error = $this -> db -> error ();
$error ++ ;
}
$sql = " SELECT COUNT(DISTINCT bl.rowid) as nb_toconsume, " ;
$sql .= " SUM(bl.qty) as qty_toconsume " ;
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " bom_bom as b " ;
$sql .= " INNER JOIN " . $this -> db -> prefix () . " bom_bomline as bl ON bl.fk_bom=b.rowid " ;
2020-10-14 10:30:39 +02:00
$sql .= " WHERE " ;
$sql .= " b.entity IN ( " . getEntity ( 'bom' ) . " ) " ;
2021-06-12 07:12:45 +02:00
$sql .= " AND bl.fk_product = " . (( int ) $this -> id );
2020-10-14 10:30:39 +02:00
$result = $this -> db -> query ( $sql );
if ( $result ) {
$obj = $this -> db -> fetch_object ( $result );
2021-06-12 07:12:45 +02:00
$this -> stats_bom [ 'nb_toconsume' ] = ! empty ( $obj -> nb_toconsume ) ? $obj -> nb_toconsume : 0 ;
$this -> stats_bom [ 'qty_toconsume' ] = ! empty ( $obj -> qty_toconsume ) ? price2num ( $obj -> qty_toconsume ) : 0 ;
2020-10-14 10:30:39 +02:00
} else {
$this -> error = $this -> db -> error ();
$error ++ ;
}
if ( ! empty ( $error )) {
return - 1 ;
}
$parameters = array ( 'socid' => $socid );
$reshook = $hookmanager -> executeHooks ( 'loadStatsCustomerMO' , $parameters , $this , $action );
2021-02-26 14:25:17 +01:00
if ( $reshook > 0 ) {
$this -> stats_bom = $hookmanager -> resArray [ 'stats_bom' ];
}
2020-10-14 10:30:39 +02:00
return 1 ;
}
2020-09-07 10:18:17 +02:00
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Charge tableau des stats propale pour le produit / service
*
* @ param int $socid Id societe
2020-10-22 22:33:35 +02:00
* @ return int Array of stats in $this -> stats_propale , < 0 if ko or > 0 if ok
2020-09-07 10:18:17 +02:00
*/
public function load_stats_propale ( $socid = 0 )
{
// phpcs:enable
2022-05-18 12:40:44 +02:00
global $conf , $user , $hookmanager , $action ;
2020-09-07 10:18:17 +02:00
$sql = " SELECT COUNT(DISTINCT p.fk_soc) as nb_customers, COUNT(DISTINCT p.rowid) as nb, " ;
$sql .= " COUNT(pd.rowid) as nb_rows, SUM(pd.qty) as qty " ;
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " propaldet as pd " ;
$sql .= " , " . $this -> db -> prefix () . " propal as p " ;
$sql .= " , " . $this -> db -> prefix () . " societe as s " ;
2020-12-27 21:41:39 +01:00
if ( empty ( $user -> rights -> societe -> client -> voir ) && ! $socid ) {
2022-01-27 10:12:10 +01:00
$sql .= " , " . $this -> db -> prefix () . " societe_commerciaux as sc " ;
2020-09-07 10:18:17 +02:00
}
$sql .= " WHERE p.rowid = pd.fk_propal " ;
$sql .= " AND p.fk_soc = s.rowid " ;
$sql .= " AND p.entity IN ( " . getEntity ( 'propal' ) . " ) " ;
2021-08-23 18:56:46 +02:00
$sql .= " AND pd.fk_product = " . (( int ) $this -> id );
2020-12-27 21:41:39 +01:00
if ( empty ( $user -> rights -> societe -> client -> voir ) && ! $socid ) {
2021-08-23 17:41:11 +02:00
$sql .= " AND p.fk_soc = sc.fk_soc AND sc.fk_user = " . (( int ) $user -> id );
2020-09-07 10:18:17 +02:00
}
//$sql.= " AND pr.fk_statut != 0";
if ( $socid > 0 ) {
2021-06-09 15:36:47 +02:00
$sql .= " AND p.fk_soc = " . (( int ) $socid );
2020-09-07 10:18:17 +02:00
}
$result = $this -> db -> query ( $sql );
if ( $result ) {
$obj = $this -> db -> fetch_object ( $result );
$this -> stats_propale [ 'customers' ] = $obj -> nb_customers ;
$this -> stats_propale [ 'nb' ] = $obj -> nb ;
$this -> stats_propale [ 'rows' ] = $obj -> nb_rows ;
$this -> stats_propale [ 'qty' ] = $obj -> qty ? $obj -> qty : 0 ;
// if it's a virtual product, maybe it is in proposal by extension
if ( ! empty ( $conf -> global -> PRODUCT_STATS_WITH_PARENT_PROD_IF_INCDEC )) {
$TFather = $this -> getFather ();
if ( is_array ( $TFather ) && ! empty ( $TFather )) {
foreach ( $TFather as & $fatherData ) {
$pFather = new Product ( $this -> db );
$pFather -> id = $fatherData [ 'id' ];
$qtyCoef = $fatherData [ 'qty' ];
if ( $fatherData [ 'incdec' ]) {
$pFather -> load_stats_propale ( $socid );
$this -> stats_propale [ 'customers' ] += $pFather -> stats_propale [ 'customers' ];
$this -> stats_propale [ 'nb' ] += $pFather -> stats_propale [ 'nb' ];
$this -> stats_propale [ 'rows' ] += $pFather -> stats_propale [ 'rows' ];
$this -> stats_propale [ 'qty' ] += $pFather -> stats_propale [ 'qty' ] * $qtyCoef ;
}
}
}
}
$parameters = array ( 'socid' => $socid );
$reshook = $hookmanager -> executeHooks ( 'loadStatsCustomerProposal' , $parameters , $this , $action );
2021-02-26 14:25:17 +01:00
if ( $reshook > 0 ) {
$this -> stats_propale = $hookmanager -> resArray [ 'stats_propale' ];
}
2020-09-07 10:18:17 +02:00
return 1 ;
} else {
$this -> error = $this -> db -> error ();
return - 1 ;
}
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Charge tableau des stats propale pour le produit / service
*
* @ param int $socid Id thirdparty
2020-10-22 22:33:35 +02:00
* @ return int Array of stats in $this -> stats_proposal_supplier , < 0 if ko or > 0 if ok
2020-09-07 10:18:17 +02:00
*/
public function load_stats_proposal_supplier ( $socid = 0 )
{
// phpcs:enable
2020-12-27 21:41:39 +01:00
global $conf , $user , $hookmanager , $action ;
2020-09-07 10:18:17 +02:00
$sql = " SELECT COUNT(DISTINCT p.fk_soc) as nb_suppliers, COUNT(DISTINCT p.rowid) as nb, " ;
$sql .= " COUNT(pd.rowid) as nb_rows, SUM(pd.qty) as qty " ;
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " supplier_proposaldet as pd " ;
$sql .= " , " . $this -> db -> prefix () . " supplier_proposal as p " ;
$sql .= " , " . $this -> db -> prefix () . " societe as s " ;
2020-12-27 21:41:39 +01:00
if ( empty ( $user -> rights -> societe -> client -> voir ) && ! $socid ) {
2022-01-27 10:12:10 +01:00
$sql .= " , " . $this -> db -> prefix () . " societe_commerciaux as sc " ;
2020-09-07 10:18:17 +02:00
}
$sql .= " WHERE p.rowid = pd.fk_supplier_proposal " ;
$sql .= " AND p.fk_soc = s.rowid " ;
$sql .= " AND p.entity IN ( " . getEntity ( 'supplier_proposal' ) . " ) " ;
2021-08-23 18:56:46 +02:00
$sql .= " AND pd.fk_product = " . (( int ) $this -> id );
2020-12-27 21:41:39 +01:00
if ( empty ( $user -> rights -> societe -> client -> voir ) && ! $socid ) {
2021-08-23 17:41:11 +02:00
$sql .= " AND p.fk_soc = sc.fk_soc AND sc.fk_user = " . (( int ) $user -> id );
2020-09-07 10:18:17 +02:00
}
//$sql.= " AND pr.fk_statut != 0";
2020-12-27 21:41:39 +01:00
if ( $socid > 0 ) {
2021-06-09 15:36:47 +02:00
$sql .= " AND p.fk_soc = " . (( int ) $socid );
2020-09-07 10:18:17 +02:00
}
$result = $this -> db -> query ( $sql );
if ( $result ) {
$obj = $this -> db -> fetch_object ( $result );
$this -> stats_proposal_supplier [ 'suppliers' ] = $obj -> nb_suppliers ;
$this -> stats_proposal_supplier [ 'nb' ] = $obj -> nb ;
$this -> stats_proposal_supplier [ 'rows' ] = $obj -> nb_rows ;
$this -> stats_proposal_supplier [ 'qty' ] = $obj -> qty ? $obj -> qty : 0 ;
$parameters = array ( 'socid' => $socid );
$reshook = $hookmanager -> executeHooks ( 'loadStatsSupplierProposal' , $parameters , $this , $action );
2021-02-26 14:25:17 +01:00
if ( $reshook > 0 ) {
$this -> stats_proposal_supplier = $hookmanager -> resArray [ 'stats_proposal_supplier' ];
}
2020-09-07 10:18:17 +02:00
return 1 ;
} else {
$this -> error = $this -> db -> error ();
return - 1 ;
}
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Charge tableau des stats commande client pour le produit / service
*
* @ param int $socid Id societe pour filtrer sur une societe
* @ param string $filtrestatut Id statut pour filtrer sur un statut
* @ param int $forVirtualStock Ignore rights filter for virtual stock calculation .
* @ return integer Array of stats in $this -> stats_commande ( nb = nb of order , qty = qty ordered ), < 0 if ko or > 0 if ok
*/
public function load_stats_commande ( $socid = 0 , $filtrestatut = '' , $forVirtualStock = 0 )
{
// phpcs:enable
2022-05-18 12:40:44 +02:00
global $conf , $user , $hookmanager , $action ;
2020-09-07 10:18:17 +02:00
$sql = " SELECT COUNT(DISTINCT c.fk_soc) as nb_customers, COUNT(DISTINCT c.rowid) as nb, " ;
$sql .= " COUNT(cd.rowid) as nb_rows, SUM(cd.qty) as qty " ;
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " commandedet as cd " ;
$sql .= " , " . $this -> db -> prefix () . " commande as c " ;
$sql .= " , " . $this -> db -> prefix () . " societe as s " ;
2020-12-27 20:33:07 +01:00
if ( empty ( $user -> rights -> societe -> client -> voir ) && ! $socid && ! $forVirtualStock ) {
2022-01-27 10:12:10 +01:00
$sql .= " , " . $this -> db -> prefix () . " societe_commerciaux as sc " ;
2020-09-07 10:18:17 +02:00
}
$sql .= " WHERE c.rowid = cd.fk_commande " ;
$sql .= " AND c.fk_soc = s.rowid " ;
2020-10-20 15:52:29 +02:00
$sql .= " AND c.entity IN ( " . getEntity ( $forVirtualStock && ! empty ( $conf -> global -> STOCK_CALCULATE_VIRTUAL_STOCK_TRANSVERSE_MODE ) ? 'stock' : 'commande' ) . " ) " ;
2021-08-23 18:56:46 +02:00
$sql .= " AND cd.fk_product = " . (( int ) $this -> id );
2020-12-27 20:33:07 +01:00
if ( empty ( $user -> rights -> societe -> client -> voir ) && ! $socid && ! $forVirtualStock ) {
2021-08-23 17:41:11 +02:00
$sql .= " AND c.fk_soc = sc.fk_soc AND sc.fk_user = " . (( int ) $user -> id );
2020-09-07 10:18:17 +02:00
}
if ( $socid > 0 ) {
2021-06-09 15:36:47 +02:00
$sql .= " AND c.fk_soc = " . (( int ) $socid );
2020-09-07 10:18:17 +02:00
}
if ( $filtrestatut <> '' ) {
2020-12-27 21:41:39 +01:00
$sql .= " AND c.fk_statut in ( " . $this -> db -> sanitize ( $filtrestatut ) . " ) " ;
2020-09-07 10:18:17 +02:00
}
$result = $this -> db -> query ( $sql );
if ( $result ) {
$obj = $this -> db -> fetch_object ( $result );
$this -> stats_commande [ 'customers' ] = $obj -> nb_customers ;
$this -> stats_commande [ 'nb' ] = $obj -> nb ;
$this -> stats_commande [ 'rows' ] = $obj -> nb_rows ;
$this -> stats_commande [ 'qty' ] = $obj -> qty ? $obj -> qty : 0 ;
// if it's a virtual product, maybe it is in order by extension
if ( ! empty ( $conf -> global -> PRODUCT_STATS_WITH_PARENT_PROD_IF_INCDEC )) {
$TFather = $this -> getFather ();
if ( is_array ( $TFather ) && ! empty ( $TFather )) {
foreach ( $TFather as & $fatherData ) {
$pFather = new Product ( $this -> db );
$pFather -> id = $fatherData [ 'id' ];
$qtyCoef = $fatherData [ 'qty' ];
if ( $fatherData [ 'incdec' ]) {
$pFather -> load_stats_commande ( $socid , $filtrestatut );
$this -> stats_commande [ 'customers' ] += $pFather -> stats_commande [ 'customers' ];
$this -> stats_commande [ 'nb' ] += $pFather -> stats_commande [ 'nb' ];
$this -> stats_commande [ 'rows' ] += $pFather -> stats_commande [ 'rows' ];
$this -> stats_commande [ 'qty' ] += $pFather -> stats_commande [ 'qty' ] * $qtyCoef ;
}
}
}
}
// If stock decrease is on invoice validation, the theorical stock continue to
// count the orders to ship in theorical stock when some are already removed b invoice validation.
// If option DECREASE_ONLY_UNINVOICEDPRODUCTS is on, we make a compensation.
2023-06-10 22:55:56 +02:00
if ( ! empty ( $conf -> global -> STOCK_CALCULATE_ON_BILL ) && $forVirtualStock ) {
2020-09-07 10:18:17 +02:00
if ( ! empty ( $conf -> global -> DECREASE_ONLY_UNINVOICEDPRODUCTS )) {
$adeduire = 0 ;
2023-06-16 00:56:43 +02:00
$sql = " SELECT SUM( " . $this -> db -> ifsql ( 'f.type=2' , - 1 , 1 ) . " * fd.qty) as count FROM " . $this -> db -> prefix () . " facturedet fd " ;
2022-01-27 10:12:10 +01:00
$sql .= " JOIN " . $this -> db -> prefix () . " facture f ON fd.fk_facture = f.rowid " ;
$sql .= " JOIN " . $this -> db -> prefix () . " element_element el ON el.fk_target = f.rowid and el.targettype = 'facture' and sourcetype = 'commande' " ;
$sql .= " JOIN " . $this -> db -> prefix () . " commande c ON el.fk_source = c.rowid " ;
2021-03-22 11:30:18 +01:00
$sql .= " WHERE c.fk_statut IN ( " . $this -> db -> sanitize ( $filtrestatut ) . " ) AND c.facture = 0 AND fd.fk_product = " . (( int ) $this -> id );
2020-09-07 10:18:17 +02:00
dol_syslog ( __METHOD__ . " :: sql $sql " , LOG_NOTICE );
$resql = $this -> db -> query ( $sql );
if ( $resql ) {
if ( $this -> db -> num_rows ( $resql ) > 0 ) {
$obj = $this -> db -> fetch_object ( $resql );
$adeduire += $obj -> count ;
}
2023-06-10 22:55:56 +02:00
} else {
$this -> error = $this -> db -> error ();
return - 1 ;
2020-09-07 10:18:17 +02:00
}
$this -> stats_commande [ 'qty' ] -= $adeduire ;
}
}
$parameters = array ( 'socid' => $socid , 'filtrestatut' => $filtrestatut , 'forVirtualStock' => $forVirtualStock );
$reshook = $hookmanager -> executeHooks ( 'loadStatsCustomerOrder' , $parameters , $this , $action );
2021-02-26 14:25:17 +01:00
if ( $reshook > 0 ) {
$this -> stats_commande = $hookmanager -> resArray [ 'stats_commande' ];
}
2020-09-07 10:18:17 +02:00
return 1 ;
} else {
$this -> error = $this -> db -> error ();
return - 1 ;
}
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Charge tableau des stats commande fournisseur pour le produit / service
*
* @ param int $socid Id societe pour filtrer sur une societe
* @ param string $filtrestatut Id des statuts pour filtrer sur des statuts
* @ param int $forVirtualStock Ignore rights filter for virtual stock calculation .
2020-10-22 22:33:35 +02:00
* @ return int Array of stats in $this -> stats_commande_fournisseur , < 0 if ko or > 0 if ok
2020-09-07 10:18:17 +02:00
*/
public function load_stats_commande_fournisseur ( $socid = 0 , $filtrestatut = '' , $forVirtualStock = 0 )
{
// phpcs:enable
2020-12-27 21:41:39 +01:00
global $conf , $user , $hookmanager , $action ;
2020-09-07 10:18:17 +02:00
$sql = " SELECT COUNT(DISTINCT c.fk_soc) as nb_suppliers, COUNT(DISTINCT c.rowid) as nb, " ;
$sql .= " COUNT(cd.rowid) as nb_rows, SUM(cd.qty) as qty " ;
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " commande_fournisseurdet as cd " ;
$sql .= " , " . $this -> db -> prefix () . " commande_fournisseur as c " ;
$sql .= " , " . $this -> db -> prefix () . " societe as s " ;
2020-12-27 21:41:39 +01:00
if ( empty ( $user -> rights -> societe -> client -> voir ) && ! $socid && ! $forVirtualStock ) {
2022-01-27 10:12:10 +01:00
$sql .= " , " . $this -> db -> prefix () . " societe_commerciaux as sc " ;
2020-09-07 10:18:17 +02:00
}
$sql .= " WHERE c.rowid = cd.fk_commande " ;
$sql .= " AND c.fk_soc = s.rowid " ;
2020-10-20 15:52:29 +02:00
$sql .= " AND c.entity IN ( " . getEntity ( $forVirtualStock && ! empty ( $conf -> global -> STOCK_CALCULATE_VIRTUAL_STOCK_TRANSVERSE_MODE ) ? 'stock' : 'supplier_order' ) . " ) " ;
2021-08-23 18:56:46 +02:00
$sql .= " AND cd.fk_product = " . (( int ) $this -> id );
2020-12-27 21:41:39 +01:00
if ( empty ( $user -> rights -> societe -> client -> voir ) && ! $socid && ! $forVirtualStock ) {
2021-08-23 17:41:11 +02:00
$sql .= " AND c.fk_soc = sc.fk_soc AND sc.fk_user = " . (( int ) $user -> id );
2020-09-07 10:18:17 +02:00
}
if ( $socid > 0 ) {
2021-06-09 15:36:47 +02:00
$sql .= " AND c.fk_soc = " . (( int ) $socid );
2020-09-07 10:18:17 +02:00
}
if ( $filtrestatut != '' ) {
2020-12-27 21:41:39 +01:00
$sql .= " AND c.fk_statut in ( " . $this -> db -> sanitize ( $filtrestatut ) . " ) " ; // Peut valoir 0
2020-09-07 10:18:17 +02:00
}
$result = $this -> db -> query ( $sql );
if ( $result ) {
$obj = $this -> db -> fetch_object ( $result );
$this -> stats_commande_fournisseur [ 'suppliers' ] = $obj -> nb_suppliers ;
$this -> stats_commande_fournisseur [ 'nb' ] = $obj -> nb ;
$this -> stats_commande_fournisseur [ 'rows' ] = $obj -> nb_rows ;
$this -> stats_commande_fournisseur [ 'qty' ] = $obj -> qty ? $obj -> qty : 0 ;
$parameters = array ( 'socid' => $socid , 'filtrestatut' => $filtrestatut , 'forVirtualStock' => $forVirtualStock );
$reshook = $hookmanager -> executeHooks ( 'loadStatsSupplierOrder' , $parameters , $this , $action );
2021-02-26 14:25:17 +01:00
if ( $reshook > 0 ) {
$this -> stats_commande_fournisseur = $hookmanager -> resArray [ 'stats_commande_fournisseur' ];
}
2020-09-07 10:18:17 +02:00
return 1 ;
} else {
$this -> error = $this -> db -> error () . ' sql=' . $sql ;
return - 1 ;
}
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Charge tableau des stats expedition client pour le produit / service
*
* @ param int $socid Id societe pour filtrer sur une societe
* @ param string $filtrestatut [ = '' ] Ids order status separated by comma
* @ param int $forVirtualStock Ignore rights filter for virtual stock calculation .
* @ param string $filterShipmentStatus [ = '' ] Ids shipment status separated by comma
2020-10-22 22:33:35 +02:00
* @ return int Array of stats in $this -> stats_expedition , < 0 if ko or > 0 if ok
2020-09-07 10:18:17 +02:00
*/
public function load_stats_sending ( $socid = 0 , $filtrestatut = '' , $forVirtualStock = 0 , $filterShipmentStatus = '' )
{
// phpcs:enable
2022-05-18 12:40:44 +02:00
global $conf , $user , $hookmanager , $action ;
2020-09-07 10:18:17 +02:00
$sql = " SELECT COUNT(DISTINCT e.fk_soc) as nb_customers, COUNT(DISTINCT e.rowid) as nb, " ;
$sql .= " COUNT(ed.rowid) as nb_rows, SUM(ed.qty) as qty " ;
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " expeditiondet as ed " ;
$sql .= " , " . $this -> db -> prefix () . " commandedet as cd " ;
$sql .= " , " . $this -> db -> prefix () . " commande as c " ;
$sql .= " , " . $this -> db -> prefix () . " expedition as e " ;
$sql .= " , " . $this -> db -> prefix () . " societe as s " ;
2020-12-27 21:41:39 +01:00
if ( empty ( $user -> rights -> societe -> client -> voir ) && ! $socid && ! $forVirtualStock ) {
2022-01-27 10:12:10 +01:00
$sql .= " , " . $this -> db -> prefix () . " societe_commerciaux as sc " ;
2020-09-07 10:18:17 +02:00
}
$sql .= " WHERE e.rowid = ed.fk_expedition " ;
$sql .= " AND c.rowid = cd.fk_commande " ;
$sql .= " AND e.fk_soc = s.rowid " ;
2020-10-20 15:52:29 +02:00
$sql .= " AND e.entity IN ( " . getEntity ( $forVirtualStock && ! empty ( $conf -> global -> STOCK_CALCULATE_VIRTUAL_STOCK_TRANSVERSE_MODE ) ? 'stock' : 'expedition' ) . " ) " ;
2020-09-07 10:18:17 +02:00
$sql .= " AND ed.fk_origin_line = cd.rowid " ;
2021-08-23 18:56:46 +02:00
$sql .= " AND cd.fk_product = " . (( int ) $this -> id );
2020-12-27 21:41:39 +01:00
if ( empty ( $user -> rights -> societe -> client -> voir ) && ! $socid && ! $forVirtualStock ) {
2021-08-23 17:41:11 +02:00
$sql .= " AND e.fk_soc = sc.fk_soc AND sc.fk_user = " . (( int ) $user -> id );
2020-09-07 10:18:17 +02:00
}
if ( $socid > 0 ) {
2021-06-09 15:36:47 +02:00
$sql .= " AND e.fk_soc = " . (( int ) $socid );
2020-09-07 10:18:17 +02:00
}
if ( $filtrestatut <> '' ) {
2020-12-27 21:41:39 +01:00
$sql .= " AND c.fk_statut IN ( " . $this -> db -> sanitize ( $filtrestatut ) . " ) " ;
2020-09-07 10:18:17 +02:00
}
2021-02-26 14:25:17 +01:00
if ( ! empty ( $filterShipmentStatus )) {
$sql .= " AND e.fk_statut IN ( " . $this -> db -> sanitize ( $filterShipmentStatus ) . " ) " ;
2020-09-07 10:18:17 +02:00
}
$result = $this -> db -> query ( $sql );
if ( $result ) {
$obj = $this -> db -> fetch_object ( $result );
$this -> stats_expedition [ 'customers' ] = $obj -> nb_customers ;
$this -> stats_expedition [ 'nb' ] = $obj -> nb ;
$this -> stats_expedition [ 'rows' ] = $obj -> nb_rows ;
$this -> stats_expedition [ 'qty' ] = $obj -> qty ? $obj -> qty : 0 ;
// if it's a virtual product, maybe it is in sending by extension
if ( ! empty ( $conf -> global -> PRODUCT_STATS_WITH_PARENT_PROD_IF_INCDEC )) {
$TFather = $this -> getFather ();
if ( is_array ( $TFather ) && ! empty ( $TFather )) {
foreach ( $TFather as & $fatherData ) {
$pFather = new Product ( $this -> db );
$pFather -> id = $fatherData [ 'id' ];
$qtyCoef = $fatherData [ 'qty' ];
if ( $fatherData [ 'incdec' ]) {
$pFather -> load_stats_sending ( $socid , $filtrestatut , $forVirtualStock );
$this -> stats_expedition [ 'customers' ] += $pFather -> stats_expedition [ 'customers' ];
$this -> stats_expedition [ 'nb' ] += $pFather -> stats_expedition [ 'nb' ];
$this -> stats_expedition [ 'rows' ] += $pFather -> stats_expedition [ 'rows' ];
$this -> stats_expedition [ 'qty' ] += $pFather -> stats_expedition [ 'qty' ] * $qtyCoef ;
}
}
}
}
$parameters = array ( 'socid' => $socid , 'filtrestatut' => $filtrestatut , 'forVirtualStock' => $forVirtualStock , 'filterShipmentStatus' => $filterShipmentStatus );
$reshook = $hookmanager -> executeHooks ( 'loadStatsSending' , $parameters , $this , $action );
2021-02-26 14:25:17 +01:00
if ( $reshook > 0 ) {
$this -> stats_expedition = $hookmanager -> resArray [ 'stats_expedition' ];
}
2020-09-07 10:18:17 +02:00
return 1 ;
} else {
$this -> error = $this -> db -> error ();
return - 1 ;
}
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Charge tableau des stats réception fournisseur pour le produit / service
*
* @ param int $socid Id societe pour filtrer sur une societe
* @ param string $filtrestatut Id statut pour filtrer sur un statut
* @ param int $forVirtualStock Ignore rights filter for virtual stock calculation .
2020-10-22 22:33:35 +02:00
* @ return int Array of stats in $this -> stats_reception , < 0 if ko or > 0 if ok
2020-09-07 10:18:17 +02:00
*/
public function load_stats_reception ( $socid = 0 , $filtrestatut = '' , $forVirtualStock = 0 )
{
// phpcs:enable
2020-12-27 21:41:39 +01:00
global $conf , $user , $hookmanager , $action ;
2020-09-07 10:18:17 +02:00
$sql = " SELECT COUNT(DISTINCT cf.fk_soc) as nb_suppliers, COUNT(DISTINCT cf.rowid) as nb, " ;
$sql .= " COUNT(fd.rowid) as nb_rows, SUM(fd.qty) as qty " ;
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " commande_fournisseur_dispatch as fd " ;
$sql .= " , " . $this -> db -> prefix () . " commande_fournisseur as cf " ;
$sql .= " , " . $this -> db -> prefix () . " societe as s " ;
2020-12-27 21:41:39 +01:00
if ( empty ( $user -> rights -> societe -> client -> voir ) && ! $socid && ! $forVirtualStock ) {
2022-01-27 10:12:10 +01:00
$sql .= " , " . $this -> db -> prefix () . " societe_commerciaux as sc " ;
2020-09-07 10:18:17 +02:00
}
$sql .= " WHERE cf.rowid = fd.fk_commande " ;
$sql .= " AND cf.fk_soc = s.rowid " ;
2020-10-20 15:52:29 +02:00
$sql .= " AND cf.entity IN ( " . getEntity ( $forVirtualStock && ! empty ( $conf -> global -> STOCK_CALCULATE_VIRTUAL_STOCK_TRANSVERSE_MODE ) ? 'stock' : 'supplier_order' ) . " ) " ;
2021-08-23 18:56:46 +02:00
$sql .= " AND fd.fk_product = " . (( int ) $this -> id );
2020-12-27 21:41:39 +01:00
if ( empty ( $user -> rights -> societe -> client -> voir ) && ! $socid && ! $forVirtualStock ) {
2021-08-23 17:41:11 +02:00
$sql .= " AND cf.fk_soc = sc.fk_soc AND sc.fk_user = " . (( int ) $user -> id );
2020-09-07 10:18:17 +02:00
}
2020-12-27 21:41:39 +01:00
if ( $socid > 0 ) {
2021-06-09 15:36:47 +02:00
$sql .= " AND cf.fk_soc = " . (( int ) $socid );
2020-09-07 10:18:17 +02:00
}
2020-12-27 21:41:39 +01:00
if ( $filtrestatut <> '' ) {
$sql .= " AND cf.fk_statut IN ( " . $this -> db -> sanitize ( $filtrestatut ) . " ) " ;
2020-09-07 10:18:17 +02:00
}
$result = $this -> db -> query ( $sql );
if ( $result ) {
$obj = $this -> db -> fetch_object ( $result );
$this -> stats_reception [ 'suppliers' ] = $obj -> nb_suppliers ;
$this -> stats_reception [ 'nb' ] = $obj -> nb ;
$this -> stats_reception [ 'rows' ] = $obj -> nb_rows ;
$this -> stats_reception [ 'qty' ] = $obj -> qty ? $obj -> qty : 0 ;
$parameters = array ( 'socid' => $socid , 'filtrestatut' => $filtrestatut , 'forVirtualStock' => $forVirtualStock );
$reshook = $hookmanager -> executeHooks ( 'loadStatsReception' , $parameters , $this , $action );
2021-02-26 14:25:17 +01:00
if ( $reshook > 0 ) {
$this -> stats_reception = $hookmanager -> resArray [ 'stats_reception' ];
}
2020-09-07 10:18:17 +02:00
return 1 ;
} else {
$this -> error = $this -> db -> error ();
return - 1 ;
}
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
2020-10-22 22:33:35 +02:00
* Charge tableau des stats production pour le produit / service
2020-09-07 10:18:17 +02:00
*
* @ param int $socid Id societe pour filtrer sur une societe
* @ param string $filtrestatut Id statut pour filtrer sur un statut
* @ param int $forVirtualStock Ignore rights filter for virtual stock calculation .
2020-10-22 22:33:35 +02:00
* @ return integer Array of stats in $this -> stats_mrptoproduce ( nb = nb of order , qty = qty ordered ), < 0 if ko or > 0 if ok
2020-09-07 10:18:17 +02:00
*/
public function load_stats_inproduction ( $socid = 0 , $filtrestatut = '' , $forVirtualStock = 0 )
{
// phpcs:enable
2022-05-18 12:40:44 +02:00
global $conf , $user , $hookmanager , $action ;
2020-09-07 10:18:17 +02:00
$sql = " SELECT COUNT(DISTINCT m.fk_soc) as nb_customers, COUNT(DISTINCT m.rowid) as nb, " ;
$sql .= " COUNT(mp.rowid) as nb_rows, SUM(mp.qty) as qty, role " ;
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " mrp_production as mp " ;
$sql .= " , " . $this -> db -> prefix () . " mrp_mo as m " ;
$sql .= " LEFT JOIN " . $this -> db -> prefix () . " societe as s ON s.rowid = m.fk_soc " ;
2020-12-27 21:41:39 +01:00
if ( empty ( $user -> rights -> societe -> client -> voir ) && ! $socid && ! $forVirtualStock ) {
2022-01-27 10:12:10 +01:00
$sql .= " , " . $this -> db -> prefix () . " societe_commerciaux as sc " ;
2020-09-07 10:18:17 +02:00
}
$sql .= " WHERE m.rowid = mp.fk_mo " ;
2020-10-20 15:52:29 +02:00
$sql .= " AND m.entity IN ( " . getEntity ( $forVirtualStock && ! empty ( $conf -> global -> STOCK_CALCULATE_VIRTUAL_STOCK_TRANSVERSE_MODE ) ? 'stock' : 'mrp' ) . " ) " ;
2021-08-23 18:56:46 +02:00
$sql .= " AND mp.fk_product = " . (( int ) $this -> id );
2020-12-27 21:41:39 +01:00
if ( empty ( $user -> rights -> societe -> client -> voir ) && ! $socid && ! $forVirtualStock ) {
2021-08-23 17:41:11 +02:00
$sql .= " AND m.fk_soc = sc.fk_soc AND sc.fk_user = " . (( int ) $user -> id );
2020-09-07 10:18:17 +02:00
}
if ( $socid > 0 ) {
2021-06-09 15:36:47 +02:00
$sql .= " AND m.fk_soc = " . (( int ) $socid );
2020-09-07 10:18:17 +02:00
}
if ( $filtrestatut <> '' ) {
2020-12-27 21:41:39 +01:00
$sql .= " AND m.status IN ( " . $this -> db -> sanitize ( $filtrestatut ) . " ) " ;
2020-09-07 10:18:17 +02:00
}
$sql .= " GROUP BY role " ;
$this -> stats_mrptoconsume [ 'customers' ] = 0 ;
$this -> stats_mrptoconsume [ 'nb' ] = 0 ;
$this -> stats_mrptoconsume [ 'rows' ] = 0 ;
$this -> stats_mrptoconsume [ 'qty' ] = 0 ;
$this -> stats_mrptoproduce [ 'customers' ] = 0 ;
$this -> stats_mrptoproduce [ 'nb' ] = 0 ;
$this -> stats_mrptoproduce [ 'rows' ] = 0 ;
$this -> stats_mrptoproduce [ 'qty' ] = 0 ;
$result = $this -> db -> query ( $sql );
if ( $result ) {
while ( $obj = $this -> db -> fetch_object ( $result )) {
if ( $obj -> role == 'toconsume' ) {
$this -> stats_mrptoconsume [ 'customers' ] += $obj -> nb_customers ;
$this -> stats_mrptoconsume [ 'nb' ] += $obj -> nb ;
$this -> stats_mrptoconsume [ 'rows' ] += $obj -> nb_rows ;
$this -> stats_mrptoconsume [ 'qty' ] += ( $obj -> qty ? $obj -> qty : 0 );
}
if ( $obj -> role == 'consumed' ) {
//$this->stats_mrptoconsume['customers'] += $obj->nb_customers;
//$this->stats_mrptoconsume['nb'] += $obj->nb;
//$this->stats_mrptoconsume['rows'] += $obj->nb_rows;
$this -> stats_mrptoconsume [ 'qty' ] -= ( $obj -> qty ? $obj -> qty : 0 );
}
if ( $obj -> role == 'toproduce' ) {
$this -> stats_mrptoproduce [ 'customers' ] += $obj -> nb_customers ;
$this -> stats_mrptoproduce [ 'nb' ] += $obj -> nb ;
$this -> stats_mrptoproduce [ 'rows' ] += $obj -> nb_rows ;
$this -> stats_mrptoproduce [ 'qty' ] += ( $obj -> qty ? $obj -> qty : 0 );
}
if ( $obj -> role == 'produced' ) {
//$this->stats_mrptoproduce['customers'] += $obj->nb_customers;
//$this->stats_mrptoproduce['nb'] += $obj->nb;
//$this->stats_mrptoproduce['rows'] += $obj->nb_rows;
$this -> stats_mrptoproduce [ 'qty' ] -= ( $obj -> qty ? $obj -> qty : 0 );
}
}
// Clean data
2021-02-26 14:25:17 +01:00
if ( $this -> stats_mrptoconsume [ 'qty' ] < 0 ) {
$this -> stats_mrptoconsume [ 'qty' ] = 0 ;
}
if ( $this -> stats_mrptoproduce [ 'qty' ] < 0 ) {
$this -> stats_mrptoproduce [ 'qty' ] = 0 ;
}
2020-09-07 10:18:17 +02:00
$parameters = array ( 'socid' => $socid , 'filtrestatut' => $filtrestatut , 'forVirtualStock' => $forVirtualStock );
$reshook = $hookmanager -> executeHooks ( 'loadStatsInProduction' , $parameters , $this , $action );
2021-02-26 14:25:17 +01:00
if ( $reshook > 0 ) {
$this -> stats_mrptoproduce = $hookmanager -> resArray [ 'stats_mrptoproduce' ];
}
2020-09-07 10:18:17 +02:00
return 1 ;
} else {
$this -> error = $this -> db -> error ();
return - 1 ;
}
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Charge tableau des stats contrat pour le produit / service
*
* @ param int $socid Id societe
2020-10-22 22:33:35 +02:00
* @ return int Array of stats in $this -> stats_contrat , < 0 if ko or > 0 if ok
2020-09-07 10:18:17 +02:00
*/
public function load_stats_contrat ( $socid = 0 )
{
// phpcs:enable
2022-05-18 12:40:44 +02:00
global $conf , $user , $hookmanager , $action ;
2020-09-07 10:18:17 +02:00
$sql = " SELECT COUNT(DISTINCT c.fk_soc) as nb_customers, COUNT(DISTINCT c.rowid) as nb, " ;
$sql .= " COUNT(cd.rowid) as nb_rows, SUM(cd.qty) as qty " ;
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " contratdet as cd " ;
$sql .= " , " . $this -> db -> prefix () . " contrat as c " ;
$sql .= " , " . $this -> db -> prefix () . " societe as s " ;
2020-12-27 21:41:39 +01:00
if ( empty ( $user -> rights -> societe -> client -> voir ) && ! $socid ) {
2022-01-27 10:12:10 +01:00
$sql .= " , " . $this -> db -> prefix () . " societe_commerciaux as sc " ;
2020-09-07 10:18:17 +02:00
}
$sql .= " WHERE c.rowid = cd.fk_contrat " ;
$sql .= " AND c.fk_soc = s.rowid " ;
$sql .= " AND c.entity IN ( " . getEntity ( 'contract' ) . " ) " ;
2021-08-23 18:56:46 +02:00
$sql .= " AND cd.fk_product = " . (( int ) $this -> id );
2020-12-27 21:41:39 +01:00
if ( empty ( $user -> rights -> societe -> client -> voir ) && ! $socid ) {
2021-08-23 17:41:11 +02:00
$sql .= " AND c.fk_soc = sc.fk_soc AND sc.fk_user = " . (( int ) $user -> id );
2020-09-07 10:18:17 +02:00
}
//$sql.= " AND c.statut != 0";
if ( $socid > 0 ) {
2021-06-09 15:36:47 +02:00
$sql .= " AND c.fk_soc = " . (( int ) $socid );
2020-09-07 10:18:17 +02:00
}
$result = $this -> db -> query ( $sql );
if ( $result ) {
$obj = $this -> db -> fetch_object ( $result );
$this -> stats_contrat [ 'customers' ] = $obj -> nb_customers ;
$this -> stats_contrat [ 'nb' ] = $obj -> nb ;
$this -> stats_contrat [ 'rows' ] = $obj -> nb_rows ;
$this -> stats_contrat [ 'qty' ] = $obj -> qty ? $obj -> qty : 0 ;
// if it's a virtual product, maybe it is in contract by extension
if ( ! empty ( $conf -> global -> PRODUCT_STATS_WITH_PARENT_PROD_IF_INCDEC )) {
$TFather = $this -> getFather ();
if ( is_array ( $TFather ) && ! empty ( $TFather )) {
foreach ( $TFather as & $fatherData ) {
$pFather = new Product ( $this -> db );
$pFather -> id = $fatherData [ 'id' ];
$qtyCoef = $fatherData [ 'qty' ];
if ( $fatherData [ 'incdec' ]) {
$pFather -> load_stats_contrat ( $socid );
$this -> stats_contrat [ 'customers' ] += $pFather -> stats_contrat [ 'customers' ];
$this -> stats_contrat [ 'nb' ] += $pFather -> stats_contrat [ 'nb' ];
$this -> stats_contrat [ 'rows' ] += $pFather -> stats_contrat [ 'rows' ];
$this -> stats_contrat [ 'qty' ] += $pFather -> stats_contrat [ 'qty' ] * $qtyCoef ;
}
}
}
}
$parameters = array ( 'socid' => $socid );
$reshook = $hookmanager -> executeHooks ( 'loadStatsContract' , $parameters , $this , $action );
2021-02-26 14:25:17 +01:00
if ( $reshook > 0 ) {
$this -> stats_contrat = $hookmanager -> resArray [ 'stats_contrat' ];
}
2020-09-07 10:18:17 +02:00
return 1 ;
} else {
$this -> error = $this -> db -> error () . ' sql=' . $sql ;
return - 1 ;
}
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Charge tableau des stats facture pour le produit / service
*
* @ param int $socid Id societe
2020-10-22 22:33:35 +02:00
* @ return int Array of stats in $this -> stats_facture , < 0 if ko or > 0 if ok
2020-09-07 10:18:17 +02:00
*/
public function load_stats_facture ( $socid = 0 )
{
// phpcs:enable
2022-05-18 12:40:44 +02:00
global $db , $conf , $user , $hookmanager , $action ;
2020-09-07 10:18:17 +02:00
$sql = " SELECT COUNT(DISTINCT f.fk_soc) as nb_customers, COUNT(DISTINCT f.rowid) as nb, " ;
2020-09-20 02:57:15 +02:00
$sql .= " COUNT(fd.rowid) as nb_rows, SUM( " . $this -> db -> ifsql ( 'f.type != 2' , 'fd.qty' , 'fd.qty * -1' ) . " ) as qty " ;
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " facturedet as fd " ;
$sql .= " , " . $this -> db -> prefix () . " facture as f " ;
$sql .= " , " . $this -> db -> prefix () . " societe as s " ;
2020-12-27 21:41:39 +01:00
if ( empty ( $user -> rights -> societe -> client -> voir ) && ! $socid ) {
2022-01-27 10:12:10 +01:00
$sql .= " , " . $this -> db -> prefix () . " societe_commerciaux as sc " ;
2020-09-07 10:18:17 +02:00
}
$sql .= " WHERE f.rowid = fd.fk_facture " ;
$sql .= " AND f.fk_soc = s.rowid " ;
$sql .= " AND f.entity IN ( " . getEntity ( 'invoice' ) . " ) " ;
2021-08-23 18:56:46 +02:00
$sql .= " AND fd.fk_product = " . (( int ) $this -> id );
2020-12-27 21:41:39 +01:00
if ( empty ( $user -> rights -> societe -> client -> voir ) && ! $socid ) {
2021-08-23 17:41:11 +02:00
$sql .= " AND f.fk_soc = sc.fk_soc AND sc.fk_user = " . (( int ) $user -> id );
2020-09-07 10:18:17 +02:00
}
//$sql.= " AND f.fk_statut != 0";
if ( $socid > 0 ) {
2021-06-09 15:36:47 +02:00
$sql .= " AND f.fk_soc = " . (( int ) $socid );
2020-09-07 10:18:17 +02:00
}
$result = $this -> db -> query ( $sql );
if ( $result ) {
$obj = $this -> db -> fetch_object ( $result );
$this -> stats_facture [ 'customers' ] = $obj -> nb_customers ;
$this -> stats_facture [ 'nb' ] = $obj -> nb ;
$this -> stats_facture [ 'rows' ] = $obj -> nb_rows ;
$this -> stats_facture [ 'qty' ] = $obj -> qty ? $obj -> qty : 0 ;
// if it's a virtual product, maybe it is in invoice by extension
if ( ! empty ( $conf -> global -> PRODUCT_STATS_WITH_PARENT_PROD_IF_INCDEC )) {
$TFather = $this -> getFather ();
if ( is_array ( $TFather ) && ! empty ( $TFather )) {
foreach ( $TFather as & $fatherData ) {
$pFather = new Product ( $this -> db );
$pFather -> id = $fatherData [ 'id' ];
$qtyCoef = $fatherData [ 'qty' ];
if ( $fatherData [ 'incdec' ]) {
$pFather -> load_stats_facture ( $socid );
$this -> stats_facture [ 'customers' ] += $pFather -> stats_facture [ 'customers' ];
$this -> stats_facture [ 'nb' ] += $pFather -> stats_facture [ 'nb' ];
$this -> stats_facture [ 'rows' ] += $pFather -> stats_facture [ 'rows' ];
$this -> stats_facture [ 'qty' ] += $pFather -> stats_facture [ 'qty' ] * $qtyCoef ;
}
}
}
}
$parameters = array ( 'socid' => $socid );
$reshook = $hookmanager -> executeHooks ( 'loadStatsCustomerInvoice' , $parameters , $this , $action );
2021-02-26 14:25:17 +01:00
if ( $reshook > 0 ) {
$this -> stats_facture = $hookmanager -> resArray [ 'stats_facture' ];
}
2020-09-07 10:18:17 +02:00
2022-06-12 11:05:12 +02:00
return 1 ;
} else {
$this -> error = $this -> db -> error ();
return - 1 ;
}
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Charge tableau des stats facture recurrentes pour le produit / service
*
* @ param int $socid Id societe
* @ return int Array of stats in $this -> stats_facture , < 0 if ko or > 0 if ok
*/
public function load_stats_facturerec ( $socid = 0 )
{
// phpcs:enable
global $db , $conf , $user , $hookmanager ;
$sql = " SELECT COUNT(DISTINCT f.fk_soc) as nb_customers, COUNT(DISTINCT f.rowid) as nb, " ;
2022-10-13 08:13:58 +02:00
$sql .= " COUNT(fd.rowid) as nb_rows, SUM(fd.qty) as qty " ;
2022-06-12 11:05:12 +02:00
$sql .= " FROM " . MAIN_DB_PREFIX . " facturedet_rec as fd " ;
$sql .= " , " . MAIN_DB_PREFIX . " facture_rec as f " ;
$sql .= " , " . MAIN_DB_PREFIX . " societe as s " ;
if ( empty ( $user -> rights -> societe -> client -> voir ) && ! $socid ) {
$sql .= " , " . MAIN_DB_PREFIX . " societe_commerciaux as sc " ;
}
$sql .= " WHERE f.rowid = fd.fk_facture " ;
$sql .= " AND f.fk_soc = s.rowid " ;
$sql .= " AND f.entity IN ( " . getEntity ( 'invoice' ) . " ) " ;
$sql .= " AND fd.fk_product = " . (( int ) $this -> id );
if ( empty ( $user -> rights -> societe -> client -> voir ) && ! $socid ) {
$sql .= " AND f.fk_soc = sc.fk_soc AND sc.fk_user = " . (( int ) $user -> id );
}
//$sql.= " AND f.fk_statut != 0";
if ( $socid > 0 ) {
$sql .= " AND f.fk_soc = " . (( int ) $socid );
}
$result = $this -> db -> query ( $sql );
if ( $result ) {
$obj = $this -> db -> fetch_object ( $result );
$this -> stats_facturerec [ 'customers' ] = $obj -> nb_customers ;
$this -> stats_facturerec [ 'nb' ] = $obj -> nb ;
$this -> stats_facturerec [ 'rows' ] = $obj -> nb_rows ;
$this -> stats_facturerec [ 'qty' ] = $obj -> qty ? $obj -> qty : 0 ;
// if it's a virtual product, maybe it is in invoice by extension
if ( ! empty ( $conf -> global -> PRODUCT_STATS_WITH_PARENT_PROD_IF_INCDEC )) {
$TFather = $this -> getFather ();
if ( is_array ( $TFather ) && ! empty ( $TFather )) {
foreach ( $TFather as & $fatherData ) {
$pFather = new Product ( $this -> db );
$pFather -> id = $fatherData [ 'id' ];
$qtyCoef = $fatherData [ 'qty' ];
if ( $fatherData [ 'incdec' ]) {
$pFather -> load_stats_facture ( $socid );
$this -> stats_facturerec [ 'customers' ] += $pFather -> stats_facturerec [ 'customers' ];
$this -> stats_facturerec [ 'nb' ] += $pFather -> stats_facturerec [ 'nb' ];
$this -> stats_facturerec [ 'rows' ] += $pFather -> stats_facturerec [ 'rows' ];
$this -> stats_facturerec [ 'qty' ] += $pFather -> stats_facturerec [ 'qty' ] * $qtyCoef ;
}
}
}
}
$parameters = array ( 'socid' => $socid );
$reshook = $hookmanager -> executeHooks ( 'loadStatsCustomerInvoiceRec' , $parameters , $this , $action );
if ( $reshook > 0 ) {
$this -> stats_facturerec = $hookmanager -> resArray [ 'stats_facturerec' ];
}
2020-09-07 10:18:17 +02:00
return 1 ;
} else {
$this -> error = $this -> db -> error ();
return - 1 ;
}
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Charge tableau des stats facture pour le produit / service
*
* @ param int $socid Id societe
2020-10-22 22:33:35 +02:00
* @ return int Array of stats in $this -> stats_facture_fournisseur , < 0 if ko or > 0 if ok
2020-09-07 10:18:17 +02:00
*/
public function load_stats_facture_fournisseur ( $socid = 0 )
{
// phpcs:enable
2020-12-27 21:41:39 +01:00
global $conf , $user , $hookmanager , $action ;
2020-09-07 10:18:17 +02:00
$sql = " SELECT COUNT(DISTINCT f.fk_soc) as nb_suppliers, COUNT(DISTINCT f.rowid) as nb, " ;
$sql .= " COUNT(fd.rowid) as nb_rows, SUM(fd.qty) as qty " ;
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " facture_fourn_det as fd " ;
$sql .= " , " . $this -> db -> prefix () . " facture_fourn as f " ;
$sql .= " , " . $this -> db -> prefix () . " societe as s " ;
2020-12-27 21:41:39 +01:00
if ( empty ( $user -> rights -> societe -> client -> voir ) && ! $socid ) {
2022-01-27 10:12:10 +01:00
$sql .= " , " . $this -> db -> prefix () . " societe_commerciaux as sc " ;
2020-09-07 10:18:17 +02:00
}
$sql .= " WHERE f.rowid = fd.fk_facture_fourn " ;
$sql .= " AND f.fk_soc = s.rowid " ;
$sql .= " AND f.entity IN ( " . getEntity ( 'facture_fourn' ) . " ) " ;
2021-08-23 18:56:46 +02:00
$sql .= " AND fd.fk_product = " . (( int ) $this -> id );
2020-12-27 21:41:39 +01:00
if ( empty ( $user -> rights -> societe -> client -> voir ) && ! $socid ) {
2021-08-23 17:41:11 +02:00
$sql .= " AND f.fk_soc = sc.fk_soc AND sc.fk_user = " . (( int ) $user -> id );
2020-09-07 10:18:17 +02:00
}
//$sql.= " AND f.fk_statut != 0";
if ( $socid > 0 ) {
2021-06-09 15:36:47 +02:00
$sql .= " AND f.fk_soc = " . (( int ) $socid );
2020-09-07 10:18:17 +02:00
}
$result = $this -> db -> query ( $sql );
if ( $result ) {
$obj = $this -> db -> fetch_object ( $result );
$this -> stats_facture_fournisseur [ 'suppliers' ] = $obj -> nb_suppliers ;
$this -> stats_facture_fournisseur [ 'nb' ] = $obj -> nb ;
$this -> stats_facture_fournisseur [ 'rows' ] = $obj -> nb_rows ;
$this -> stats_facture_fournisseur [ 'qty' ] = $obj -> qty ? $obj -> qty : 0 ;
$parameters = array ( 'socid' => $socid );
$reshook = $hookmanager -> executeHooks ( 'loadStatsSupplierInvoice' , $parameters , $this , $action );
2021-02-26 14:25:17 +01:00
if ( $reshook > 0 ) {
$this -> stats_facture_fournisseur = $hookmanager -> resArray [ 'stats_facture_fournisseur' ];
}
2020-09-07 10:18:17 +02:00
return 1 ;
} else {
$this -> error = $this -> db -> error ();
return - 1 ;
}
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Return an array formated for showing graphs
*
2020-10-28 17:02:20 +01:00
* @ param string $sql Request to execute
* @ param string $mode 'byunit' = number of unit , 'bynumber' = nb of entities
* @ param int $year Year ( 0 = current year , - 1 = all years )
2020-10-28 22:47:29 +01:00
* @ return array | int < 0 if KO , result [ month ] = array ( valuex , valuey ) where month is 0 to 11
2020-09-07 10:18:17 +02:00
*/
private function _get_stats ( $sql , $mode , $year = 0 )
{
// phpcs:enable
2020-10-31 14:32:18 +01:00
$tab = array ();
2020-10-28 17:02:20 +01:00
2020-09-07 10:18:17 +02:00
$resql = $this -> db -> query ( $sql );
if ( $resql ) {
$num = $this -> db -> num_rows ( $resql );
$i = 0 ;
2021-02-26 14:25:17 +01:00
while ( $i < $num ) {
2020-09-07 10:18:17 +02:00
$arr = $this -> db -> fetch_array ( $resql );
2020-10-31 14:32:18 +01:00
$keyfortab = ( string ) $arr [ 1 ];
if ( $year == - 1 ) {
$keyfortab = substr ( $keyfortab , - 2 );
}
if ( $mode == 'byunit' ) {
$tab [ $keyfortab ] = ( empty ( $tab [ $keyfortab ]) ? 0 : $tab [ $keyfortab ]) + $arr [ 0 ]; // 1st field
} elseif ( $mode == 'bynumber' ) {
$tab [ $keyfortab ] = ( empty ( $tab [ $keyfortab ]) ? 0 : $tab [ $keyfortab ]) + $arr [ 2 ]; // 3rd field
}
2020-09-07 10:18:17 +02:00
$i ++ ;
}
} else {
$this -> error = $this -> db -> error () . ' sql=' . $sql ;
return - 1 ;
}
2020-10-31 14:32:18 +01:00
if ( empty ( $year )) {
$year = strftime ( '%Y' , time ());
$month = strftime ( '%m' , time ());
} elseif ( $year == - 1 ) {
$year = '' ;
$month = 12 ; // We imagine we are at end of year, so we get last 12 month before, so all correct year.
} else {
$month = 12 ; // We imagine we are at end of year, so we get last 12 month before, so all correct year.
}
2020-09-07 10:18:17 +02:00
2020-10-31 14:32:18 +01:00
$result = array ();
2020-09-07 10:18:17 +02:00
2021-02-26 14:25:17 +01:00
for ( $j = 0 ; $j < 12 ; $j ++ ) {
2020-10-28 17:02:20 +01:00
// $ids is 'D', 'N', 'O', 'S', ... (First letter of month in user language)
2020-10-31 14:32:18 +01:00
$idx = ucfirst ( dol_trunc ( dol_print_date ( dol_mktime ( 12 , 0 , 0 , $month , 1 , 1970 ), " %b " ), 1 , 'right' , 'UTF-8' , 1 ));
2020-10-28 17:02:20 +01:00
//print $idx.'-'.$year.'-'.$month.'<br>';
2020-10-31 14:32:18 +01:00
$result [ $j ] = array ( $idx , isset ( $tab [ $year . $month ]) ? $tab [ $year . $month ] : 0 );
// $result[$j] = array($monthnum,isset($tab[$year.$month])?$tab[$year.$month]:0);
2020-09-07 10:18:17 +02:00
$month = " 0 " . ( $month - 1 );
if ( dol_strlen ( $month ) == 3 ) {
$month = substr ( $month , 1 );
}
if ( $month == 0 ) {
$month = 12 ;
$year = $year - 1 ;
}
}
return array_reverse ( $result );
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Return nb of units or customers invoices in which product is included
*
* @ param int $socid Limit count on a particular third party id
* @ param string $mode 'byunit' = number of unit , 'bynumber' = nb of entities
* @ param int $filteronproducttype 0 = To filter on product only , 1 = To filter on services only
2020-10-28 17:02:20 +01:00
* @ param int $year Year ( 0 = last 12 month , - 1 = all years )
2020-09-07 10:18:17 +02:00
* @ param string $morefilter More sql filters
2020-10-28 17:02:20 +01:00
* @ return array < 0 if KO , result [ month ] = array ( valuex , valuey ) where month is 0 to 11
2020-09-07 10:18:17 +02:00
*/
public function get_nb_vente ( $socid , $mode , $filteronproducttype = - 1 , $year = 0 , $morefilter = '' )
{
// phpcs:enable
global $conf ;
global $user ;
$sql = " SELECT sum(d.qty), date_format(f.datef, '%Y%m') " ;
if ( $mode == 'bynumber' ) {
$sql .= " , count(DISTINCT f.rowid) " ;
}
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " facturedet as d, " . $this -> db -> prefix () . " facture as f, " . $this -> db -> prefix () . " societe as s " ;
2020-09-07 10:18:17 +02:00
if ( $filteronproducttype >= 0 ) {
2022-01-27 10:12:10 +01:00
$sql .= " , " . $this -> db -> prefix () . " product as p " ;
2020-09-07 10:18:17 +02:00
}
2020-12-27 21:41:39 +01:00
if ( empty ( $user -> rights -> societe -> client -> voir ) && ! $socid ) {
2022-01-27 10:12:10 +01:00
$sql .= " , " . $this -> db -> prefix () . " societe_commerciaux as sc " ;
2020-09-07 10:18:17 +02:00
}
$sql .= " WHERE f.rowid = d.fk_facture " ;
if ( $this -> id > 0 ) {
2021-08-23 18:56:46 +02:00
$sql .= " AND d.fk_product = " . (( int ) $this -> id );
2020-09-07 10:18:17 +02:00
} else {
$sql .= " AND d.fk_product > 0 " ;
}
if ( $filteronproducttype >= 0 ) {
2020-12-27 21:41:39 +01:00
$sql .= " AND p.rowid = d.fk_product AND p.fk_product_type = " . (( int ) $filteronproducttype );
2020-09-07 10:18:17 +02:00
}
$sql .= " AND f.fk_soc = s.rowid " ;
$sql .= " AND f.entity IN ( " . getEntity ( 'invoice' ) . " ) " ;
2020-12-27 21:41:39 +01:00
if ( empty ( $user -> rights -> societe -> client -> voir ) && ! $socid ) {
2021-08-23 17:41:11 +02:00
$sql .= " AND f.fk_soc = sc.fk_soc AND sc.fk_user = " . (( int ) $user -> id );
2020-09-07 10:18:17 +02:00
}
if ( $socid > 0 ) {
$sql .= " AND f.fk_soc = $socid " ;
}
$sql .= $morefilter ;
$sql .= " GROUP BY date_format(f.datef,'%Y%m') " ;
$sql .= " ORDER BY date_format(f.datef,'%Y%m') DESC " ;
return $this -> _get_stats ( $sql , $mode , $year );
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Return nb of units or supplier invoices in which product is included
*
* @ param int $socid Limit count on a particular third party id
* @ param string $mode 'byunit' = number of unit , 'bynumber' = nb of entities
* @ param int $filteronproducttype 0 = To filter on product only , 1 = To filter on services only
2020-10-28 17:02:20 +01:00
* @ param int $year Year ( 0 = last 12 month , - 1 = all years )
2020-09-07 10:18:17 +02:00
* @ param string $morefilter More sql filters
2020-10-28 17:02:20 +01:00
* @ return array < 0 if KO , result [ month ] = array ( valuex , valuey ) where month is 0 to 11
2020-09-07 10:18:17 +02:00
*/
public function get_nb_achat ( $socid , $mode , $filteronproducttype = - 1 , $year = 0 , $morefilter = '' )
{
// phpcs:enable
global $conf ;
global $user ;
$sql = " SELECT sum(d.qty), date_format(f.datef, '%Y%m') " ;
if ( $mode == 'bynumber' ) {
$sql .= " , count(DISTINCT f.rowid) " ;
}
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " facture_fourn_det as d, " . $this -> db -> prefix () . " facture_fourn as f, " . $this -> db -> prefix () . " societe as s " ;
2020-09-07 10:18:17 +02:00
if ( $filteronproducttype >= 0 ) {
2022-01-27 10:12:10 +01:00
$sql .= " , " . $this -> db -> prefix () . " product as p " ;
2020-09-07 10:18:17 +02:00
}
2020-12-27 21:41:39 +01:00
if ( empty ( $user -> rights -> societe -> client -> voir ) && ! $socid ) {
2022-01-27 10:12:10 +01:00
$sql .= " , " . $this -> db -> prefix () . " societe_commerciaux as sc " ;
2020-09-07 10:18:17 +02:00
}
$sql .= " WHERE f.rowid = d.fk_facture_fourn " ;
if ( $this -> id > 0 ) {
2021-08-23 18:56:46 +02:00
$sql .= " AND d.fk_product = " . (( int ) $this -> id );
2020-09-07 10:18:17 +02:00
} else {
$sql .= " AND d.fk_product > 0 " ;
}
if ( $filteronproducttype >= 0 ) {
2020-12-27 21:41:39 +01:00
$sql .= " AND p.rowid = d.fk_product AND p.fk_product_type = " . (( int ) $filteronproducttype );
2020-09-07 10:18:17 +02:00
}
$sql .= " AND f.fk_soc = s.rowid " ;
$sql .= " AND f.entity IN ( " . getEntity ( 'facture_fourn' ) . " ) " ;
2020-12-27 21:41:39 +01:00
if ( empty ( $user -> rights -> societe -> client -> voir ) && ! $socid ) {
2021-08-23 17:41:11 +02:00
$sql .= " AND f.fk_soc = sc.fk_soc AND sc.fk_user = " . (( int ) $user -> id );
2020-09-07 10:18:17 +02:00
}
if ( $socid > 0 ) {
$sql .= " AND f.fk_soc = $socid " ;
}
$sql .= $morefilter ;
$sql .= " GROUP BY date_format(f.datef,'%Y%m') " ;
$sql .= " ORDER BY date_format(f.datef,'%Y%m') DESC " ;
return $this -> _get_stats ( $sql , $mode , $year );
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
2020-10-28 17:02:20 +01:00
* Return nb of units in proposals in which product is included
2020-09-07 10:18:17 +02:00
*
* @ param int $socid Limit count on a particular third party id
* @ param string $mode 'byunit' = number of unit , 'bynumber' = nb of entities
* @ param int $filteronproducttype 0 = To filter on product only , 1 = To filter on services only
2020-10-28 17:02:20 +01:00
* @ param int $year Year ( 0 = last 12 month , - 1 = all years )
2020-09-07 10:18:17 +02:00
* @ param string $morefilter More sql filters
2020-10-28 17:02:20 +01:00
* @ return array < 0 if KO , result [ month ] = array ( valuex , valuey ) where month is 0 to 11
2020-09-07 10:18:17 +02:00
*/
public function get_nb_propal ( $socid , $mode , $filteronproducttype = - 1 , $year = 0 , $morefilter = '' )
{
// phpcs:enable
2020-10-28 17:02:20 +01:00
global $conf , $user ;
2020-09-07 10:18:17 +02:00
$sql = " SELECT sum(d.qty), date_format(p.datep, '%Y%m') " ;
if ( $mode == 'bynumber' ) {
$sql .= " , count(DISTINCT p.rowid) " ;
}
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " propaldet as d, " . $this -> db -> prefix () . " propal as p, " . $this -> db -> prefix () . " societe as s " ;
2020-09-07 10:18:17 +02:00
if ( $filteronproducttype >= 0 ) {
2022-01-27 10:12:10 +01:00
$sql .= " , " . $this -> db -> prefix () . " product as prod " ;
2020-09-07 10:18:17 +02:00
}
2020-12-27 21:41:39 +01:00
if ( empty ( $user -> rights -> societe -> client -> voir ) && ! $socid ) {
2022-01-27 10:12:10 +01:00
$sql .= " , " . $this -> db -> prefix () . " societe_commerciaux as sc " ;
2020-09-07 10:18:17 +02:00
}
$sql .= " WHERE p.rowid = d.fk_propal " ;
if ( $this -> id > 0 ) {
2021-08-23 18:56:46 +02:00
$sql .= " AND d.fk_product = " . (( int ) $this -> id );
2020-09-07 10:18:17 +02:00
} else {
$sql .= " AND d.fk_product > 0 " ;
}
if ( $filteronproducttype >= 0 ) {
2020-12-27 21:41:39 +01:00
$sql .= " AND prod.rowid = d.fk_product AND prod.fk_product_type = " . (( int ) $filteronproducttype );
2020-09-07 10:18:17 +02:00
}
$sql .= " AND p.fk_soc = s.rowid " ;
$sql .= " AND p.entity IN ( " . getEntity ( 'propal' ) . " ) " ;
2020-12-27 21:41:39 +01:00
if ( empty ( $user -> rights -> societe -> client -> voir ) && ! $socid ) {
2021-08-23 17:41:11 +02:00
$sql .= " AND p.fk_soc = sc.fk_soc AND sc.fk_user = " . (( int ) $user -> id );
2020-09-07 10:18:17 +02:00
}
if ( $socid > 0 ) {
2021-06-09 15:36:47 +02:00
$sql .= " AND p.fk_soc = " . (( int ) $socid );
2020-09-07 10:18:17 +02:00
}
$sql .= $morefilter ;
$sql .= " GROUP BY date_format(p.datep,'%Y%m') " ;
$sql .= " ORDER BY date_format(p.datep,'%Y%m') DESC " ;
return $this -> _get_stats ( $sql , $mode , $year );
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Return nb of units in proposals in which product is included
*
* @ param int $socid Limit count on a particular third party id
* @ param string $mode 'byunit' = number of unit , 'bynumber' = nb of entities
* @ param int $filteronproducttype 0 = To filter on product only , 1 = To filter on services only
2020-10-28 17:02:20 +01:00
* @ param int $year Year ( 0 = last 12 month , - 1 = all years )
2020-09-07 10:18:17 +02:00
* @ param string $morefilter More sql filters
2020-10-28 17:02:20 +01:00
* @ return array < 0 if KO , result [ month ] = array ( valuex , valuey ) where month is 0 to 11
2020-09-07 10:18:17 +02:00
*/
public function get_nb_propalsupplier ( $socid , $mode , $filteronproducttype = - 1 , $year = 0 , $morefilter = '' )
{
// phpcs:enable
global $conf ;
global $user ;
$sql = " SELECT sum(d.qty), date_format(p.date_valid, '%Y%m') " ;
if ( $mode == 'bynumber' ) {
$sql .= " , count(DISTINCT p.rowid) " ;
}
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " supplier_proposaldet as d, " . $this -> db -> prefix () . " supplier_proposal as p, " . $this -> db -> prefix () . " societe as s " ;
2020-09-07 10:18:17 +02:00
if ( $filteronproducttype >= 0 ) {
2022-01-27 10:12:10 +01:00
$sql .= " , " . $this -> db -> prefix () . " product as prod " ;
2020-09-07 10:18:17 +02:00
}
2020-12-27 21:41:39 +01:00
if ( empty ( $user -> rights -> societe -> client -> voir ) && ! $socid ) {
2022-01-27 10:12:10 +01:00
$sql .= " , " . $this -> db -> prefix () . " societe_commerciaux as sc " ;
2020-09-07 10:18:17 +02:00
}
$sql .= " WHERE p.rowid = d.fk_supplier_proposal " ;
if ( $this -> id > 0 ) {
2021-08-23 18:56:46 +02:00
$sql .= " AND d.fk_product = " . (( int ) $this -> id );
2020-09-07 10:18:17 +02:00
} else {
$sql .= " AND d.fk_product > 0 " ;
}
if ( $filteronproducttype >= 0 ) {
2020-12-27 21:41:39 +01:00
$sql .= " AND prod.rowid = d.fk_product AND prod.fk_product_type = " . (( int ) $filteronproducttype );
2020-09-07 10:18:17 +02:00
}
$sql .= " AND p.fk_soc = s.rowid " ;
$sql .= " AND p.entity IN ( " . getEntity ( 'supplier_proposal' ) . " ) " ;
2020-12-27 21:41:39 +01:00
if ( empty ( $user -> rights -> societe -> client -> voir ) && ! $socid ) {
2021-08-23 17:41:11 +02:00
$sql .= " AND p.fk_soc = sc.fk_soc AND sc.fk_user = " . (( int ) $user -> id );
2020-09-07 10:18:17 +02:00
}
if ( $socid > 0 ) {
2021-03-22 13:31:06 +01:00
$sql .= " AND p.fk_soc = " . (( int ) $socid );
2020-09-07 10:18:17 +02:00
}
$sql .= $morefilter ;
$sql .= " GROUP BY date_format(p.date_valid,'%Y%m') " ;
$sql .= " ORDER BY date_format(p.date_valid,'%Y%m') DESC " ;
return $this -> _get_stats ( $sql , $mode , $year );
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Return nb of units in orders in which product is included
*
* @ param int $socid Limit count on a particular third party id
* @ param string $mode 'byunit' = number of unit , 'bynumber' = nb of entities
* @ param int $filteronproducttype 0 = To filter on product only , 1 = To filter on services only
2020-10-28 17:02:20 +01:00
* @ param int $year Year ( 0 = last 12 month , - 1 = all years )
2020-09-07 10:18:17 +02:00
* @ param string $morefilter More sql filters
2020-10-28 17:02:20 +01:00
* @ return array < 0 if KO , result [ month ] = array ( valuex , valuey ) where month is 0 to 11
2020-09-07 10:18:17 +02:00
*/
public function get_nb_order ( $socid , $mode , $filteronproducttype = - 1 , $year = 0 , $morefilter = '' )
{
// phpcs:enable
global $conf , $user ;
$sql = " SELECT sum(d.qty), date_format(c.date_commande, '%Y%m') " ;
if ( $mode == 'bynumber' ) {
$sql .= " , count(DISTINCT c.rowid) " ;
}
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " commandedet as d, " . $this -> db -> prefix () . " commande as c, " . $this -> db -> prefix () . " societe as s " ;
2020-09-07 10:18:17 +02:00
if ( $filteronproducttype >= 0 ) {
2022-01-27 10:12:10 +01:00
$sql .= " , " . $this -> db -> prefix () . " product as p " ;
2020-09-07 10:18:17 +02:00
}
2020-12-27 21:41:39 +01:00
if ( empty ( $user -> rights -> societe -> client -> voir ) && ! $socid ) {
2022-01-27 10:12:10 +01:00
$sql .= " , " . $this -> db -> prefix () . " societe_commerciaux as sc " ;
2020-09-07 10:18:17 +02:00
}
$sql .= " WHERE c.rowid = d.fk_commande " ;
if ( $this -> id > 0 ) {
2021-08-23 18:56:46 +02:00
$sql .= " AND d.fk_product = " . (( int ) $this -> id );
2020-09-07 10:18:17 +02:00
} else {
$sql .= " AND d.fk_product > 0 " ;
}
if ( $filteronproducttype >= 0 ) {
2020-12-27 21:41:39 +01:00
$sql .= " AND p.rowid = d.fk_product AND p.fk_product_type = " . (( int ) $filteronproducttype );
2020-09-07 10:18:17 +02:00
}
$sql .= " AND c.fk_soc = s.rowid " ;
$sql .= " AND c.entity IN ( " . getEntity ( 'commande' ) . " ) " ;
2020-12-27 21:41:39 +01:00
if ( empty ( $user -> rights -> societe -> client -> voir ) && ! $socid ) {
2021-08-23 17:41:11 +02:00
$sql .= " AND c.fk_soc = sc.fk_soc AND sc.fk_user = " . (( int ) $user -> id );
2020-09-07 10:18:17 +02:00
}
if ( $socid > 0 ) {
2021-06-09 15:36:47 +02:00
$sql .= " AND c.fk_soc = " . (( int ) $socid );
2020-09-07 10:18:17 +02:00
}
$sql .= $morefilter ;
$sql .= " GROUP BY date_format(c.date_commande,'%Y%m') " ;
$sql .= " ORDER BY date_format(c.date_commande,'%Y%m') DESC " ;
return $this -> _get_stats ( $sql , $mode , $year );
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Return nb of units in orders in which product is included
*
* @ param int $socid Limit count on a particular third party id
* @ param string $mode 'byunit' = number of unit , 'bynumber' = nb of entities
* @ param int $filteronproducttype 0 = To filter on product only , 1 = To filter on services only
2020-10-28 17:02:20 +01:00
* @ param int $year Year ( 0 = last 12 month , - 1 = all years )
2020-09-07 10:18:17 +02:00
* @ param string $morefilter More sql filters
2020-10-28 17:02:20 +01:00
* @ return array < 0 if KO , result [ month ] = array ( valuex , valuey ) where month is 0 to 11
2020-09-07 10:18:17 +02:00
*/
public function get_nb_ordersupplier ( $socid , $mode , $filteronproducttype = - 1 , $year = 0 , $morefilter = '' )
{
// phpcs:enable
global $conf , $user ;
$sql = " SELECT sum(d.qty), date_format(c.date_commande, '%Y%m') " ;
if ( $mode == 'bynumber' ) {
$sql .= " , count(DISTINCT c.rowid) " ;
}
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " commande_fournisseurdet as d, " . $this -> db -> prefix () . " commande_fournisseur as c, " . $this -> db -> prefix () . " societe as s " ;
2020-09-07 10:18:17 +02:00
if ( $filteronproducttype >= 0 ) {
2022-01-27 10:12:10 +01:00
$sql .= " , " . $this -> db -> prefix () . " product as p " ;
2020-09-07 10:18:17 +02:00
}
2020-12-27 21:41:39 +01:00
if ( empty ( $user -> rights -> societe -> client -> voir ) && ! $socid ) {
2022-01-27 10:12:10 +01:00
$sql .= " , " . $this -> db -> prefix () . " societe_commerciaux as sc " ;
2020-09-07 10:18:17 +02:00
}
$sql .= " WHERE c.rowid = d.fk_commande " ;
if ( $this -> id > 0 ) {
2021-08-23 18:56:46 +02:00
$sql .= " AND d.fk_product = " . (( int ) $this -> id );
2020-09-07 10:18:17 +02:00
} else {
$sql .= " AND d.fk_product > 0 " ;
}
if ( $filteronproducttype >= 0 ) {
2020-12-27 21:41:39 +01:00
$sql .= " AND p.rowid = d.fk_product AND p.fk_product_type = " . (( int ) $filteronproducttype );
2020-09-07 10:18:17 +02:00
}
$sql .= " AND c.fk_soc = s.rowid " ;
$sql .= " AND c.entity IN ( " . getEntity ( 'supplier_order' ) . " ) " ;
2020-12-27 21:41:39 +01:00
if ( empty ( $user -> rights -> societe -> client -> voir ) && ! $socid ) {
2021-08-23 17:41:11 +02:00
$sql .= " AND c.fk_soc = sc.fk_soc AND sc.fk_user = " . (( int ) $user -> id );
2020-09-07 10:18:17 +02:00
}
if ( $socid > 0 ) {
2021-06-09 15:36:47 +02:00
$sql .= " AND c.fk_soc = " . (( int ) $socid );
2020-09-07 10:18:17 +02:00
}
$sql .= $morefilter ;
$sql .= " GROUP BY date_format(c.date_commande,'%Y%m') " ;
$sql .= " ORDER BY date_format(c.date_commande,'%Y%m') DESC " ;
return $this -> _get_stats ( $sql , $mode , $year );
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Return nb of units in orders in which product is included
*
* @ param int $socid Limit count on a particular third party id
* @ param string $mode 'byunit' = number of unit , 'bynumber' = nb of entities
* @ param int $filteronproducttype 0 = To filter on product only , 1 = To filter on services only
2020-10-28 17:02:20 +01:00
* @ param int $year Year ( 0 = last 12 month , - 1 = all years )
2020-09-07 10:18:17 +02:00
* @ param string $morefilter More sql filters
2020-10-28 17:02:20 +01:00
* @ return array < 0 if KO , result [ month ] = array ( valuex , valuey ) where month is 0 to 11
2020-09-07 10:18:17 +02:00
*/
public function get_nb_contract ( $socid , $mode , $filteronproducttype = - 1 , $year = 0 , $morefilter = '' )
{
// phpcs:enable
global $conf , $user ;
$sql = " SELECT sum(d.qty), date_format(c.date_contrat, '%Y%m') " ;
if ( $mode == 'bynumber' ) {
$sql .= " , count(DISTINCT c.rowid) " ;
}
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " contratdet as d, " . $this -> db -> prefix () . " contrat as c, " . $this -> db -> prefix () . " societe as s " ;
2020-09-07 10:18:17 +02:00
if ( $filteronproducttype >= 0 ) {
2022-01-27 10:12:10 +01:00
$sql .= " , " . $this -> db -> prefix () . " product as p " ;
2020-09-07 10:18:17 +02:00
}
2020-12-27 21:41:39 +01:00
if ( empty ( $user -> rights -> societe -> client -> voir ) && ! $socid ) {
2022-01-27 10:12:10 +01:00
$sql .= " , " . $this -> db -> prefix () . " societe_commerciaux as sc " ;
2020-09-07 10:18:17 +02:00
}
$sql .= " WHERE c.entity IN ( " . getEntity ( 'contract' ) . " ) " ;
$sql .= " AND c.rowid = d.fk_contrat " ;
if ( $this -> id > 0 ) {
2021-08-23 18:56:46 +02:00
$sql .= " AND d.fk_product = " . (( int ) $this -> id );
2020-09-07 10:18:17 +02:00
} else {
$sql .= " AND d.fk_product > 0 " ;
}
if ( $filteronproducttype >= 0 ) {
2020-12-27 21:41:39 +01:00
$sql .= " AND p.rowid = d.fk_product AND p.fk_product_type = " . (( int ) $filteronproducttype );
2020-09-07 10:18:17 +02:00
}
$sql .= " AND c.fk_soc = s.rowid " ;
2020-12-27 21:41:39 +01:00
if ( empty ( $user -> rights -> societe -> client -> voir ) && ! $socid ) {
2021-08-23 17:41:11 +02:00
$sql .= " AND c.fk_soc = sc.fk_soc AND sc.fk_user = " . (( int ) $user -> id );
2020-09-07 10:18:17 +02:00
}
if ( $socid > 0 ) {
2021-06-09 15:36:47 +02:00
$sql .= " AND c.fk_soc = " . (( int ) $socid );
2020-09-07 10:18:17 +02:00
}
$sql .= $morefilter ;
$sql .= " GROUP BY date_format(c.date_contrat,'%Y%m') " ;
$sql .= " ORDER BY date_format(c.date_contrat,'%Y%m') DESC " ;
return $this -> _get_stats ( $sql , $mode , $year );
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Return nb of units in orders in which product is included
*
* @ param int $socid Limit count on a particular third party id
* @ param string $mode 'byunit' = number of unit , 'bynumber' = nb of entities
* @ param int $filteronproducttype 0 = To filter on product only , 1 = To filter on services only
2020-10-28 17:02:20 +01:00
* @ param int $year Year ( 0 = last 12 month , - 1 = all years )
2020-09-07 10:18:17 +02:00
* @ param string $morefilter More sql filters
2020-10-28 17:02:20 +01:00
* @ return array < 0 if KO , result [ month ] = array ( valuex , valuey ) where month is 0 to 11
2020-09-07 10:18:17 +02:00
*/
public function get_nb_mos ( $socid , $mode , $filteronproducttype = - 1 , $year = 0 , $morefilter = '' )
{
// phpcs:enable
global $conf , $user ;
$sql = " SELECT sum(d.qty), date_format(d.date_valid, '%Y%m') " ;
if ( $mode == 'bynumber' ) {
$sql .= " , count(DISTINCT d.rowid) " ;
}
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " mrp_mo as d LEFT JOIN " . $this -> db -> prefix () . " societe as s ON d.fk_soc = s.rowid " ;
2020-09-07 10:18:17 +02:00
if ( $filteronproducttype >= 0 ) {
2022-01-27 10:12:10 +01:00
$sql .= " , " . $this -> db -> prefix () . " product as p " ;
2020-09-07 10:18:17 +02:00
}
2020-12-27 21:41:39 +01:00
if ( empty ( $user -> rights -> societe -> client -> voir ) && ! $socid ) {
2022-01-27 10:12:10 +01:00
$sql .= " , " . $this -> db -> prefix () . " societe_commerciaux as sc " ;
2020-09-07 10:18:17 +02:00
}
$sql .= " WHERE d.entity IN ( " . getEntity ( 'mo' ) . " ) " ;
$sql .= " AND d.status > 0 " ;
if ( $this -> id > 0 ) {
2021-08-23 18:56:46 +02:00
$sql .= " AND d.fk_product = " . (( int ) $this -> id );
2020-09-07 10:18:17 +02:00
} else {
$sql .= " AND d.fk_product > 0 " ;
}
if ( $filteronproducttype >= 0 ) {
2020-12-27 21:41:39 +01:00
$sql .= " AND p.rowid = d.fk_product AND p.fk_product_type = " . (( int ) $filteronproducttype );
2020-09-07 10:18:17 +02:00
}
2020-12-27 21:41:39 +01:00
if ( empty ( $user -> rights -> societe -> client -> voir ) && ! $socid ) {
2021-08-23 17:41:11 +02:00
$sql .= " AND d.fk_soc = sc.fk_soc AND sc.fk_user = " . (( int ) $user -> id );
2020-09-07 10:18:17 +02:00
}
if ( $socid > 0 ) {
2021-06-09 15:36:47 +02:00
$sql .= " AND d.fk_soc = " . (( int ) $socid );
2020-09-07 10:18:17 +02:00
}
$sql .= $morefilter ;
$sql .= " GROUP BY date_format(d.date_valid,'%Y%m') " ;
$sql .= " ORDER BY date_format(d.date_valid,'%Y%m') DESC " ;
return $this -> _get_stats ( $sql , $mode , $year );
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Link a product / service to a parent product / service
*
* @ param int $id_pere Id of parent product / service
* @ param int $id_fils Id of child product / service
* @ param int $qty Quantity
* @ param int $incdec 1 = Increase / decrease stock of child when parent stock increase / decrease
* @ return int < 0 if KO , > 0 if OK
*/
public function add_sousproduit ( $id_pere , $id_fils , $qty , $incdec = 1 )
{
// phpcs:enable
// Clean parameters
if ( ! is_numeric ( $id_pere )) {
$id_pere = 0 ;
}
if ( ! is_numeric ( $id_fils )) {
$id_fils = 0 ;
}
if ( ! is_numeric ( $incdec )) {
$incdec = 0 ;
}
$result = $this -> del_sousproduit ( $id_pere , $id_fils );
if ( $result < 0 ) {
return $result ;
}
// Check not already father of id_pere (to avoid father -> child -> father links)
2022-01-27 10:12:10 +01:00
$sql = " SELECT fk_product_pere from " . $this -> db -> prefix () . " product_association " ;
2021-09-30 14:30:50 +02:00
$sql .= " WHERE fk_product_pere = " . (( int ) $id_fils ) . " AND fk_product_fils = " . (( int ) $id_pere );
2020-09-07 10:18:17 +02:00
if ( ! $this -> db -> query ( $sql )) {
dol_print_error ( $this -> db );
return - 1 ;
} else {
2021-08-30 14:45:42 +02:00
//Selection of the highest row
2022-01-27 10:12:10 +01:00
$sql = " SELECT MAX(rang) as max_rank FROM " . $this -> db -> prefix () . " product_association " ;
2021-09-30 14:30:50 +02:00
$sql .= " WHERE fk_product_pere = " . (( int ) $id_pere );
2021-08-30 14:45:42 +02:00
$resql = $this -> db -> query ( $sql );
if ( $resql > 0 ) {
$obj = $this -> db -> fetch_object ( $resql );
$rank = $obj -> max_rank + 1 ;
//Addition of a product with the highest rank +1
2022-01-27 10:12:10 +01:00
$sql = " INSERT INTO " . $this -> db -> prefix () . " product_association(fk_product_pere,fk_product_fils,qty,incdec,rang) " ;
2021-09-30 14:51:38 +02:00
$sql .= " VALUES ( " . (( int ) $id_pere ) . " , " . (( int ) $id_fils ) . " , " . price2num ( $qty , 'MS' ) . " , " . price2num ( $incdec , 'MS' ) . " , " . (( int ) $rank ) . " ) " ;
2021-08-30 14:45:42 +02:00
if ( ! $this -> db -> query ( $sql )) {
dol_print_error ( $this -> db );
2020-09-07 10:18:17 +02:00
return - 1 ;
2021-08-30 15:51:59 +02:00
} else {
2021-08-30 14:45:42 +02:00
return 1 ;
}
} else {
dol_print_error ( $this -> db );
return - 1 ;
2020-09-07 10:18:17 +02:00
}
}
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Modify composed product
*
* @ param int $id_pere Id of parent product / service
* @ param int $id_fils Id of child product / service
* @ param int $qty Quantity
* @ param int $incdec 1 = Increase / decrease stock of child when parent stock increase / decrease
* @ return int < 0 if KO , > 0 if OK
*/
public function update_sousproduit ( $id_pere , $id_fils , $qty , $incdec = 1 )
{
// phpcs:enable
// Clean parameters
if ( ! is_numeric ( $id_pere )) {
$id_pere = 0 ;
}
if ( ! is_numeric ( $id_fils )) {
$id_fils = 0 ;
}
if ( ! is_numeric ( $incdec )) {
$incdec = 1 ;
}
if ( ! is_numeric ( $qty )) {
$qty = 1 ;
}
2022-01-27 10:12:10 +01:00
$sql = 'UPDATE ' . $this -> db -> prefix () . 'product_association SET ' ;
2021-08-27 18:18:50 +02:00
$sql .= 'qty = ' . price2num ( $qty , 'MS' );
$sql .= ',incdec = ' . price2num ( $incdec , 'MS' );
$sql .= ' WHERE fk_product_pere = ' . (( int ) $id_pere ) . ' AND fk_product_fils = ' . (( int ) $id_fils );
2020-09-07 10:18:17 +02:00
if ( ! $this -> db -> query ( $sql )) {
dol_print_error ( $this -> db );
return - 1 ;
} else {
return 1 ;
}
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
2021-09-30 14:51:38 +02:00
* Remove a link between a subproduct and a parent product / service
2020-09-07 10:18:17 +02:00
*
2021-09-30 14:51:38 +02:00
* @ param int $fk_parent Id of parent product ( child will no more be linked to it )
* @ param int $fk_child Id of child product
* @ return int < 0 if KO , > 0 if OK
2020-09-07 10:18:17 +02:00
*/
public function del_sousproduit ( $fk_parent , $fk_child )
{
// phpcs:enable
if ( ! is_numeric ( $fk_parent )) {
$fk_parent = 0 ;
}
if ( ! is_numeric ( $fk_child )) {
$fk_child = 0 ;
}
2022-01-27 10:12:10 +01:00
$sql = " DELETE FROM " . $this -> db -> prefix () . " product_association " ;
2021-03-30 19:12:07 +02:00
$sql .= " WHERE fk_product_pere = " . (( int ) $fk_parent );
$sql .= " AND fk_product_fils = " . (( int ) $fk_child );
2020-09-07 10:18:17 +02:00
dol_syslog ( get_class ( $this ) . '::del_sousproduit' , LOG_DEBUG );
if ( ! $this -> db -> query ( $sql )) {
dol_print_error ( $this -> db );
return - 1 ;
}
2021-09-30 14:51:38 +02:00
// Updated ranks so that none are missing
2022-01-27 10:12:10 +01:00
$sqlrank = " SELECT rowid, rang FROM " . $this -> db -> prefix () . " product_association " ;
2021-09-30 14:51:38 +02:00
$sqlrank .= " WHERE fk_product_pere = " . (( int ) $fk_parent );
$sqlrank .= " ORDER BY rang " ;
2021-08-30 14:45:42 +02:00
$resqlrank = $this -> db -> query ( $sqlrank );
if ( $resqlrank ) {
$cpt = 0 ;
2021-08-30 15:38:55 +02:00
while ( $objrank = $this -> db -> fetch_object ( $resqlrank )) {
2021-08-30 14:45:42 +02:00
$cpt ++ ;
2022-01-27 10:12:10 +01:00
$sql = " UPDATE " . $this -> db -> prefix () . " product_association " ;
2021-09-30 14:51:38 +02:00
$sql .= " SET rang = " . (( int ) $cpt );
$sql .= " WHERE rowid = " . (( int ) $objrank -> rowid );
2021-08-30 14:45:42 +02:00
if ( ! $this -> db -> query ( $sql )) {
dol_print_error ( $this -> db );
return - 1 ;
}
}
}
2020-09-07 10:18:17 +02:00
return 1 ;
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
2020-11-16 16:31:05 +01:00
* Check if it is a sub - product into a kit
2020-09-07 10:18:17 +02:00
*
2020-11-16 16:31:05 +01:00
* @ param int $fk_parent Id of parent kit product
* @ param int $fk_child Id of child product
* @ return int < 0 if KO , > 0 if OK
2020-09-07 10:18:17 +02:00
*/
public function is_sousproduit ( $fk_parent , $fk_child )
{
// phpcs:enable
$sql = " SELECT fk_product_pere, qty, incdec " ;
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " product_association " ;
2020-09-20 02:57:15 +02:00
$sql .= " WHERE fk_product_pere = " . (( int ) $fk_parent );
$sql .= " AND fk_product_fils = " . (( int ) $fk_child );
2020-09-07 10:18:17 +02:00
$result = $this -> db -> query ( $sql );
if ( $result ) {
$num = $this -> db -> num_rows ( $result );
if ( $num > 0 ) {
$obj = $this -> db -> fetch_object ( $result );
2020-11-16 16:31:05 +01:00
2020-09-07 10:18:17 +02:00
$this -> is_sousproduit_qty = $obj -> qty ;
$this -> is_sousproduit_incdec = $obj -> incdec ;
return true ;
} else {
return false ;
}
} else {
dol_print_error ( $this -> db );
return - 1 ;
}
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Add a supplier price for the product .
* Note : Duplicate ref is accepted for different quantity only , or for different companies .
*
* @ param User $user User that make link
* @ param int $id_fourn Supplier id
* @ param string $ref_fourn Supplier ref
* @ param float $quantity Quantity minimum for price
* @ return int < 0 if KO , 0 if link already exists for this product , > 0 if OK
*/
public function add_fournisseur ( $user , $id_fourn , $ref_fourn , $quantity )
{
// phpcs:enable
global $conf ;
$now = dol_now ();
dol_syslog ( get_class ( $this ) . " ::add_fournisseur id_fourn = " . $id_fourn . " ref_fourn= " . $ref_fourn . " quantity= " . $quantity , LOG_DEBUG );
// Clean parameters
$quantity = price2num ( $quantity , 'MS' );
if ( $ref_fourn ) {
$sql = " SELECT rowid, fk_product " ;
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " product_fournisseur_price " ;
2020-12-17 18:08:56 +01:00
$sql .= " WHERE fk_soc = " . (( int ) $id_fourn );
2020-09-07 10:18:17 +02:00
$sql .= " AND ref_fourn = ' " . $this -> db -> escape ( $ref_fourn ) . " ' " ;
2020-12-17 18:08:56 +01:00
$sql .= " AND fk_product <> " . (( int ) $this -> id );
2020-09-07 10:18:17 +02:00
$sql .= " AND entity IN ( " . getEntity ( 'productsupplierprice' ) . " ) " ;
$resql = $this -> db -> query ( $sql );
if ( $resql ) {
$obj = $this -> db -> fetch_object ( $resql );
if ( $obj ) {
// If the supplier ref already exists but for another product (duplicate ref is accepted for different quantity only or different companies)
$this -> product_id_already_linked = $obj -> fk_product ;
return - 3 ;
}
$this -> db -> free ( $resql );
}
}
$sql = " SELECT rowid " ;
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " product_fournisseur_price " ;
2021-03-30 19:12:07 +02:00
$sql .= " WHERE fk_soc = " . (( int ) $id_fourn );
2021-02-26 14:25:17 +01:00
if ( $ref_fourn ) {
$sql .= " AND ref_fourn = ' " . $this -> db -> escape ( $ref_fourn ) . " ' " ;
} else {
$sql .= " AND (ref_fourn = '' OR ref_fourn IS NULL) " ;
2020-09-07 10:18:17 +02:00
}
2021-03-30 19:12:07 +02:00
$sql .= " AND quantity = " . (( float ) $quantity );
$sql .= " AND fk_product = " . (( int ) $this -> id );
2020-09-07 10:18:17 +02:00
$sql .= " AND entity IN ( " . getEntity ( 'productsupplierprice' ) . " ) " ;
$resql = $this -> db -> query ( $sql );
if ( $resql ) {
$obj = $this -> db -> fetch_object ( $resql );
// The reference supplier does not exist, we create it for this product.
2020-12-17 18:08:56 +01:00
if ( empty ( $obj )) {
2022-01-27 10:12:10 +01:00
$sql = " INSERT INTO " . $this -> db -> prefix () . " product_fournisseur_price( " ;
2020-09-07 10:18:17 +02:00
$sql .= " datec " ;
$sql .= " , entity " ;
$sql .= " , fk_product " ;
$sql .= " , fk_soc " ;
$sql .= " , ref_fourn " ;
$sql .= " , quantity " ;
$sql .= " , fk_user " ;
$sql .= " , tva_tx " ;
$sql .= " ) VALUES ( " ;
$sql .= " ' " . $this -> db -> idate ( $now ) . " ' " ;
$sql .= " , " . $conf -> entity ;
$sql .= " , " . $this -> id ;
$sql .= " , " . $id_fourn ;
$sql .= " , ' " . $this -> db -> escape ( $ref_fourn ) . " ' " ;
$sql .= " , " . $quantity ;
$sql .= " , " . $user -> id ;
$sql .= " , 0 " ;
$sql .= " ) " ;
if ( $this -> db -> query ( $sql )) {
2022-01-27 10:12:10 +01:00
$this -> product_fourn_price_id = $this -> db -> last_insert_id ( $this -> db -> prefix () . " product_fournisseur_price " );
2020-09-07 10:18:17 +02:00
return 1 ;
} else {
$this -> error = $this -> db -> lasterror ();
return - 1 ;
}
} else {
2021-06-16 20:17:21 +02:00
// If the supplier price already exists for this product and quantity
2020-09-07 10:18:17 +02:00
$this -> product_fourn_price_id = $obj -> rowid ;
return 0 ;
}
} else {
$this -> error = $this -> db -> lasterror ();
return - 2 ;
}
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
2020-12-17 18:08:56 +01:00
* Return list of suppliers providing the product or service
2020-09-07 10:18:17 +02:00
*
2020-12-17 18:08:56 +01:00
* @ return array Array of vendor ids
2020-09-07 10:18:17 +02:00
*/
public function list_suppliers ()
{
// phpcs:enable
global $conf ;
$list = array ();
$sql = " SELECT DISTINCT p.fk_soc " ;
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " product_fournisseur_price as p " ;
2021-08-23 18:56:46 +02:00
$sql .= " WHERE p.fk_product = " . (( int ) $this -> id );
$sql .= " AND p.entity = " . (( int ) $conf -> entity );
2020-09-07 10:18:17 +02:00
$result = $this -> db -> query ( $sql );
if ( $result ) {
$num = $this -> db -> num_rows ( $result );
$i = 0 ;
2021-02-26 14:25:17 +01:00
while ( $i < $num ) {
2020-09-07 10:18:17 +02:00
$obj = $this -> db -> fetch_object ( $result );
$list [ $i ] = $obj -> fk_soc ;
$i ++ ;
}
}
return $list ;
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Recopie les prix d ' un produit / service sur un autre
*
* @ param int $fromId Id product source
* @ param int $toId Id product target
* @ return int < 0 if KO , > 0 if OK
*/
public function clone_price ( $fromId , $toId )
{
global $conf , $user ;
$now = dol_now ();
$this -> db -> begin ();
// prices
2022-01-27 10:12:10 +01:00
$sql = " INSERT INTO " . $this -> db -> prefix () . " product_price ( " ;
2020-09-07 10:18:17 +02:00
$sql .= " entity " ;
$sql .= " , fk_product " ;
$sql .= " , date_price " ;
$sql .= " , price_level " ;
$sql .= " , price " ;
$sql .= " , price_ttc " ;
$sql .= " , price_min " ;
$sql .= " , price_min_ttc " ;
$sql .= " , price_base_type " ;
$sql .= " , default_vat_code " ;
$sql .= " , tva_tx " ;
$sql .= " , recuperableonly " ;
$sql .= " , localtax1_tx " ;
$sql .= " , localtax1_type " ;
$sql .= " , localtax2_tx " ;
$sql .= " , localtax2_type " ;
$sql .= " , fk_user_author " ;
$sql .= " , tosell " ;
$sql .= " , price_by_qty " ;
$sql .= " , fk_price_expression " ;
$sql .= " , fk_multicurrency " ;
$sql .= " , multicurrency_code " ;
$sql .= " , multicurrency_tx " ;
$sql .= " , multicurrency_price " ;
$sql .= " , multicurrency_price_ttc " ;
$sql .= " ) " ;
$sql .= " SELECT " ;
$sql .= " entity " ;
$sql .= " , " . $toId ;
$sql .= " , ' " . $this -> db -> idate ( $now ) . " ' " ;
$sql .= " , price_level " ;
$sql .= " , price " ;
$sql .= " , price_ttc " ;
$sql .= " , price_min " ;
$sql .= " , price_min_ttc " ;
$sql .= " , price_base_type " ;
$sql .= " , default_vat_code " ;
$sql .= " , tva_tx " ;
$sql .= " , recuperableonly " ;
$sql .= " , localtax1_tx " ;
$sql .= " , localtax1_type " ;
$sql .= " , localtax2_tx " ;
$sql .= " , localtax2_type " ;
$sql .= " , " . $user -> id ;
$sql .= " , tosell " ;
$sql .= " , price_by_qty " ;
$sql .= " , fk_price_expression " ;
$sql .= " , fk_multicurrency " ;
$sql .= " , multicurrency_code " ;
$sql .= " , multicurrency_tx " ;
$sql .= " , multicurrency_price " ;
$sql .= " , multicurrency_price_ttc " ;
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " product_price " ;
2021-03-30 19:12:07 +02:00
$sql .= " WHERE fk_product = " . (( int ) $fromId );
2020-09-07 10:18:17 +02:00
$sql .= " ORDER BY date_price DESC " ;
if ( $conf -> global -> PRODUIT_MULTIPRICES_LIMIT > 0 ) {
$sql .= " LIMIT " . $conf -> global -> PRODUIT_MULTIPRICES_LIMIT ;
}
dol_syslog ( __METHOD__ , LOG_DEBUG );
$resql = $this -> db -> query ( $sql );
if ( ! $resql ) {
$this -> db -> rollback ();
return - 1 ;
}
$this -> db -> commit ();
return 1 ;
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Clone links between products
*
* @ param int $fromId Product id
* @ param int $toId Product id
* @ return int < 0 if KO , > 0 if OK
*/
public function clone_associations ( $fromId , $toId )
{
// phpcs:enable
$this -> db -> begin ();
2022-01-27 10:12:10 +01:00
$sql = 'INSERT INTO ' . $this -> db -> prefix () . 'product_association (fk_product_pere, fk_product_fils, qty)' ;
$sql .= " SELECT " . $toId . " , fk_product_fils, qty FROM " . $this -> db -> prefix () . " product_association " ;
2021-03-30 19:12:07 +02:00
$sql .= " WHERE fk_product_pere = " . (( int ) $fromId );
2020-09-07 10:18:17 +02:00
dol_syslog ( get_class ( $this ) . '::clone_association' , LOG_DEBUG );
if ( ! $this -> db -> query ( $sql )) {
$this -> db -> rollback ();
return - 1 ;
}
$this -> db -> commit ();
return 1 ;
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Recopie les fournisseurs et prix fournisseurs d ' un produit / service sur un autre
*
* @ param int $fromId Id produit source
* @ param int $toId Id produit cible
* @ return int < 0 si erreur , > 0 si ok
*/
public function clone_fournisseurs ( $fromId , $toId )
{
// phpcs:enable
$this -> db -> begin ();
$now = dol_now ();
// les fournisseurs
2022-01-27 10:12:10 +01:00
/* $sql = " INSERT " . $this -> db -> prefix () . " product_fournisseur ( "
2021-06-16 20:17:21 +02:00
. " datec, fk_product, fk_soc, ref_fourn, fk_user_author ) "
. " SELECT ' " . $this -> db -> idate ( $now ) . " ', " . $toId . " , fk_soc, ref_fourn, fk_user_author "
2022-01-27 10:12:10 +01:00
. " FROM " . $this -> db -> prefix () . " product_fournisseur "
2021-06-16 20:17:21 +02:00
. " WHERE fk_product = " . (( int ) $fromId );
2020-09-07 10:18:17 +02:00
2021-06-16 20:17:21 +02:00
if ( ! $this -> db -> query ( $sql ) )
{
$this -> db -> rollback ();
return - 1 ;
} */
2020-09-07 10:18:17 +02:00
// les prix de fournisseurs.
2022-01-27 10:12:10 +01:00
$sql = " INSERT " . $this -> db -> prefix () . " product_fournisseur_price ( " ;
2020-09-07 10:18:17 +02:00
$sql .= " datec, fk_product, fk_soc, price, quantity, fk_user) " ;
2021-03-30 19:12:07 +02:00
$sql .= " SELECT ' " . $this -> db -> idate ( $now ) . " ', " . (( int ) $toId ) . " , fk_soc, price, quantity, fk_user " ;
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " product_fournisseur_price " ;
2021-03-30 19:12:07 +02:00
$sql .= " WHERE fk_product = " . (( int ) $fromId );
2020-09-07 10:18:17 +02:00
dol_syslog ( get_class ( $this ) . '::clone_fournisseurs' , LOG_DEBUG );
$resql = $this -> db -> query ( $sql );
if ( ! $resql ) {
$this -> db -> rollback ();
return - 1 ;
} else {
$this -> db -> commit ();
return 1 ;
}
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Fonction recursive uniquement utilisee par get_arbo_each_prod , recompose l ' arborescence des sousproduits
* Define value of this -> res
*
2021-03-14 23:44:20 +01:00
* @ param array $prod Products array
* @ param string $compl_path Directory path of parents to add before
* @ param int $multiply Because each sublevel must be multiplicated by parent nb
* @ param int $level Init level
* @ param int $id_parent Id parent
* @ param int $ignore_stock_load Ignore stock load
2020-09-07 10:18:17 +02:00
* @ return void
*/
2021-03-14 23:44:20 +01:00
public function fetch_prod_arbo ( $prod , $compl_path = '' , $multiply = 1 , $level = 1 , $id_parent = 0 , $ignore_stock_load = 0 )
2020-09-07 10:18:17 +02:00
{
// phpcs:enable
global $conf , $langs ;
$tmpproduct = null ;
//var_dump($prod);
2021-02-26 14:25:17 +01:00
foreach ( $prod as $id_product => $desc_pere ) { // $id_product is 0 (first call starting with root top) or an id of a sub_product
if ( is_array ( $desc_pere )) { // If desc_pere is an array, this means it's a child
2020-09-07 10:18:17 +02:00
$id = ( ! empty ( $desc_pere [ 0 ]) ? $desc_pere [ 0 ] : '' );
$nb = ( ! empty ( $desc_pere [ 1 ]) ? $desc_pere [ 1 ] : '' );
$type = ( ! empty ( $desc_pere [ 2 ]) ? $desc_pere [ 2 ] : '' );
$label = ( ! empty ( $desc_pere [ 3 ]) ? $desc_pere [ 3 ] : '' );
2021-03-14 23:44:20 +01:00
$incdec = ( ! empty ( $desc_pere [ 4 ]) ? $desc_pere [ 4 ] : 0 );
2020-09-07 10:18:17 +02:00
2021-02-26 14:25:17 +01:00
if ( $multiply < 1 ) {
$multiply = 1 ;
2020-09-07 10:18:17 +02:00
}
//print "XXX We add id=".$id." - label=".$label." - nb=".$nb." - multiply=".$multiply." fullpath=".$compl_path.$label."\n";
2021-02-26 14:25:17 +01:00
if ( is_null ( $tmpproduct )) {
$tmpproduct = new Product ( $this -> db ); // So we initialize tmpproduct only once for all loop.
}
2020-09-07 10:18:17 +02:00
$tmpproduct -> fetch ( $id ); // Load product to get ->ref
2021-03-14 23:44:20 +01:00
if ( empty ( $ignore_stock_load ) && ( $tmpproduct -> isProduct () || ! empty ( $conf -> global -> STOCK_SUPPORTS_SERVICES ))) {
$tmpproduct -> load_stock ( 'nobatch,novirtual' ); // Load stock to get true ->stock_reel
}
2020-09-07 10:18:17 +02:00
$this -> res [] = array (
'id' => $id , // Id product
'id_parent' => $id_parent ,
'ref' => $tmpproduct -> ref , // Ref product
'nb' => $nb , // Nb of units that compose parent product
'nb_total' => $nb * $multiply , // Nb of units for all nb of product
'stock' => $tmpproduct -> stock_reel , // Stock
'stock_alert' => $tmpproduct -> seuil_stock_alerte , // Stock alert
'label' => $label ,
'fullpath' => $compl_path . $label , // Label
'type' => $type , // Nb of units that compose parent product
'desiredstock' => $tmpproduct -> desiredstock ,
'level' => $level ,
'incdec' => $incdec ,
'entity' => $tmpproduct -> entity
);
// Recursive call if there is childs to child
2022-06-27 11:11:43 +02:00
if ( isset ( $desc_pere [ 'childs' ]) && is_array ( $desc_pere [ 'childs' ])) {
2021-06-16 20:17:21 +02:00
//print 'YYY We go down for '.$desc_pere[3]." -> \n";
2021-03-14 23:44:20 +01:00
$this -> fetch_prod_arbo ( $desc_pere [ 'childs' ], $compl_path . $desc_pere [ 3 ] . " -> " , $desc_pere [ 1 ] * $multiply , $level + 1 , $id , $ignore_stock_load );
2020-09-07 10:18:17 +02:00
}
}
}
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
2021-03-14 23:44:20 +01:00
* Build the tree of subproducts into an array -> res and return it .
* this -> sousprods must have been loaded by this -> get_sousproduits_arbo ()
2020-09-07 10:18:17 +02:00
*
2021-03-14 23:44:20 +01:00
* @ param int $multiply Because each sublevel must be multiplicated by parent nb
* @ param int $ignore_stock_load Ignore stock load
* @ return array $this -> res
2020-09-07 10:18:17 +02:00
*/
2021-03-14 23:44:20 +01:00
public function get_arbo_each_prod ( $multiply = 1 , $ignore_stock_load = 0 )
2020-09-07 10:18:17 +02:00
{
// phpcs:enable
$this -> res = array ();
if ( isset ( $this -> sousprods ) && is_array ( $this -> sousprods )) {
2020-12-15 15:18:01 +01:00
foreach ( $this -> sousprods as $prod_name => $desc_product ) {
if ( is_array ( $desc_product )) {
2021-03-14 23:44:20 +01:00
$this -> fetch_prod_arbo ( $desc_product , " " , $multiply , 1 , $this -> id , $ignore_stock_load );
2020-09-07 10:18:17 +02:00
}
}
}
//var_dump($this->res);
return $this -> res ;
}
/**
2020-11-16 16:31:05 +01:00
* Count all parent and children products for current product ( first level only )
2020-09-07 10:18:17 +02:00
*
2020-11-16 16:31:05 +01:00
* @ param int $mode 0 = Both parent and child , - 1 = Parents only , 1 = Children only
* @ return int Nb of father + child
* @ see getFather (), get_sousproduits_arbo ()
2020-09-07 10:18:17 +02:00
*/
2020-11-16 16:31:05 +01:00
public function hasFatherOrChild ( $mode = 0 )
2020-09-07 10:18:17 +02:00
{
$nb = 0 ;
$sql = " SELECT COUNT(pa.rowid) as nb " ;
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " product_association as pa " ;
2020-11-16 16:31:05 +01:00
if ( $mode == 0 ) {
2021-08-23 18:56:46 +02:00
$sql .= " WHERE pa.fk_product_fils = " . (( int ) $this -> id ) . " OR pa.fk_product_pere = " . (( int ) $this -> id );
2020-11-16 16:31:05 +01:00
} elseif ( $mode == - 1 ) {
2021-08-23 18:56:46 +02:00
$sql .= " WHERE pa.fk_product_fils = " . (( int ) $this -> id ); // We are a child, so we found lines that link to parents (can have several parents)
2020-11-16 16:31:05 +01:00
} elseif ( $mode == 1 ) {
2021-08-23 18:56:46 +02:00
$sql .= " WHERE pa.fk_product_pere = " . (( int ) $this -> id ); // We are a parent, so we found lines that link to children (can have several children)
2020-11-16 16:31:05 +01:00
}
2020-09-07 10:18:17 +02:00
$resql = $this -> db -> query ( $sql );
if ( $resql ) {
$obj = $this -> db -> fetch_object ( $resql );
2021-02-26 14:25:17 +01:00
if ( $obj ) {
$nb = $obj -> nb ;
}
2020-09-07 10:18:17 +02:00
} else {
return - 1 ;
}
return $nb ;
}
/**
* Return if a product has variants or not
*
* @ return int Number of variants
*/
public function hasVariants ()
{
$nb = 0 ;
2022-01-27 10:12:10 +01:00
$sql = " SELECT count(rowid) as nb FROM " . $this -> db -> prefix () . " product_attribute_combination WHERE fk_product_parent = " . (( int ) $this -> id );
2020-09-07 10:18:17 +02:00
$sql .= " AND entity IN ( " . getEntity ( 'product' ) . " ) " ;
$resql = $this -> db -> query ( $sql );
if ( $resql ) {
$obj = $this -> db -> fetch_object ( $resql );
2021-02-26 14:25:17 +01:00
if ( $obj ) {
$nb = $obj -> nb ;
2020-09-07 10:18:17 +02:00
}
}
return $nb ;
}
/**
* Return if loaded product is a variant
*
* @ return int
*/
public function isVariant ()
{
global $conf ;
if ( ! empty ( $conf -> variants -> enabled )) {
2022-01-27 10:12:10 +01:00
$sql = " SELECT rowid FROM " . $this -> db -> prefix () . " product_attribute_combination WHERE fk_product_child = " . (( int ) $this -> id ) . " AND entity IN ( " . getEntity ( 'product' ) . " ) " ;
2020-09-07 10:18:17 +02:00
$query = $this -> db -> query ( $sql );
if ( $query ) {
if ( ! $this -> db -> num_rows ( $query )) {
return false ;
}
return true ;
} else {
dol_print_error ( $this -> db );
return - 1 ;
}
} else {
return false ;
}
}
/**
* Return all parent products for current product ( first level only )
*
* @ return array Array of product
2020-11-16 16:31:05 +01:00
* @ see hasFatherOrChild ()
2020-09-07 10:18:17 +02:00
*/
public function getFather ()
{
$sql = " SELECT p.rowid, p.label as label, p.ref as ref, pa.fk_product_pere as id, p.fk_product_type, pa.qty, pa.incdec, p.entity " ;
2021-01-19 18:01:30 +01:00
$sql .= " , p.tosell as status, p.tobuy as status_buy " ;
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " product_association as pa, " ;
$sql .= " " . $this -> db -> prefix () . " product as p " ;
2020-09-07 10:18:17 +02:00
$sql .= " WHERE p.rowid = pa.fk_product_pere " ;
2021-08-23 18:56:46 +02:00
$sql .= " AND pa.fk_product_fils = " . (( int ) $this -> id );
2020-09-07 10:18:17 +02:00
$res = $this -> db -> query ( $sql );
if ( $res ) {
$prods = array ();
2021-01-19 18:01:30 +01:00
while ( $record = $this -> db -> fetch_array ( $res )) {
2020-09-07 10:18:17 +02:00
// $record['id'] = $record['rowid'] = id of father
$prods [ $record [ 'id' ]][ 'id' ] = $record [ 'rowid' ];
$prods [ $record [ 'id' ]][ 'ref' ] = $record [ 'ref' ];
$prods [ $record [ 'id' ]][ 'label' ] = $record [ 'label' ];
$prods [ $record [ 'id' ]][ 'qty' ] = $record [ 'qty' ];
$prods [ $record [ 'id' ]][ 'incdec' ] = $record [ 'incdec' ];
$prods [ $record [ 'id' ]][ 'fk_product_type' ] = $record [ 'fk_product_type' ];
$prods [ $record [ 'id' ]][ 'entity' ] = $record [ 'entity' ];
2021-01-19 18:01:30 +01:00
$prods [ $record [ 'id' ]][ 'status' ] = $record [ 'status' ];
$prods [ $record [ 'id' ]][ 'status_buy' ] = $record [ 'status_buy' ];
2020-09-07 10:18:17 +02:00
}
return $prods ;
} else {
dol_print_error ( $this -> db );
return - 1 ;
}
}
/**
* Return childs of product $id
*
2020-12-08 21:56:12 +01:00
* @ param int $id Id of product to search childs of
* @ param int $firstlevelonly Return only direct child
* @ param int $level Level of recursing call ( start to 1 )
2022-07-27 11:03:29 +02:00
* @ param array $parents Array of all parents of $id
2020-12-08 21:56:12 +01:00
* @ return array Return array ( prodid => array ( 0 = prodid , 1 => qty , 2 => product type , 3 => label , 4 => incdec , 5 => product ref )
2020-09-07 10:18:17 +02:00
*/
2022-07-27 11:03:29 +02:00
public function getChildsArbo ( $id , $firstlevelonly = 0 , $level = 1 , $parents = array ())
2020-09-07 10:18:17 +02:00
{
global $alreadyfound ;
2021-03-01 23:08:03 +01:00
if ( empty ( $id )) {
return array ();
}
2020-12-08 21:56:12 +01:00
$sql = " SELECT p.rowid, p.ref, p.label as label, p.fk_product_type, " ;
2021-08-30 14:45:42 +02:00
$sql .= " pa.qty as qty, pa.fk_product_fils as id, pa.incdec, " ;
$sql .= " pa.rowid as fk_association, pa.rang " ;
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " product as p, " ;
$sql .= " " . $this -> db -> prefix () . " product_association as pa " ;
2020-09-07 10:18:17 +02:00
$sql .= " WHERE p.rowid = pa.fk_product_fils " ;
2021-04-25 15:55:36 +02:00
$sql .= " AND pa.fk_product_pere = " . (( int ) $id );
$sql .= " AND pa.fk_product_fils <> " . (( int ) $id ); // This should not happens, it is to avoid infinite loop if it happens
2021-08-30 14:45:42 +02:00
$sql .= " ORDER BY pa.rang " ;
2020-09-07 10:18:17 +02:00
2022-10-03 22:01:04 +02:00
dol_syslog ( get_class ( $this ) . '::getChildsArbo id=' . $id . ' level=' . $level . ' parents=' . ( is_array ( $parents ) ? implode ( ',' , $parents ) : $parents ), LOG_DEBUG );
2020-09-07 10:18:17 +02:00
2021-02-26 14:25:17 +01:00
if ( $level == 1 ) {
$alreadyfound = array ( $id => 1 ); // We init array of found object to start of tree, so if we found it later (should not happened), we stop immediatly
2020-09-07 10:18:17 +02:00
}
// Protection against infinite loop
2021-02-26 14:25:17 +01:00
if ( $level > 30 ) {
return array ();
2020-09-07 10:18:17 +02:00
}
$res = $this -> db -> query ( $sql );
if ( $res ) {
$prods = array ();
2021-02-26 14:25:17 +01:00
while ( $rec = $this -> db -> fetch_array ( $res )) {
2020-09-07 10:18:17 +02:00
if ( ! empty ( $alreadyfound [ $rec [ 'rowid' ]])) {
dol_syslog ( get_class ( $this ) . '::getChildsArbo the product id=' . $rec [ 'rowid' ] . ' was already found at a higher level in tree. We discard to avoid infinite loop' , LOG_WARNING );
2022-07-27 11:07:28 +02:00
if ( in_array ( $rec [ 'id' ], $parents )) {
continue ; // We discard this child if it is already found at a higher level in tree in the same branch.
2022-07-11 14:04:31 +02:00
}
2020-09-07 10:18:17 +02:00
}
$alreadyfound [ $rec [ 'rowid' ]] = 1 ;
$prods [ $rec [ 'rowid' ]] = array (
2021-06-16 20:17:21 +02:00
0 => $rec [ 'rowid' ],
1 => $rec [ 'qty' ],
2 => $rec [ 'fk_product_type' ],
3 => $this -> db -> escape ( $rec [ 'label' ]),
4 => $rec [ 'incdec' ],
2021-08-30 14:45:42 +02:00
5 => $rec [ 'ref' ],
6 => $rec [ 'fk_association' ],
7 => $rec [ 'rang' ]
2020-09-07 10:18:17 +02:00
);
//$prods[$this->db->escape($rec['label'])]= array(0=>$rec['id'],1=>$rec['qty'],2=>$rec['fk_product_type']);
//$prods[$this->db->escape($rec['label'])]= array(0=>$rec['id'],1=>$rec['qty']);
if ( empty ( $firstlevelonly )) {
2022-07-27 11:03:29 +02:00
$listofchilds = $this -> getChildsArbo ( $rec [ 'rowid' ], 0 , $level + 1 , array_push ( $parents , $rec [ 'rowid' ]));
2021-02-26 14:25:17 +01:00
foreach ( $listofchilds as $keyChild => $valueChild ) {
2020-09-07 10:18:17 +02:00
$prods [ $rec [ 'rowid' ]][ 'childs' ][ $keyChild ] = $valueChild ;
}
}
}
return $prods ;
} else {
dol_print_error ( $this -> db );
return - 1 ;
}
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
2020-12-08 21:56:12 +01:00
* Return tree of all subproducts for product . Tree contains array of array ( 0 = prodid , 1 => qty , 2 => product type , 3 => label , 4 => incdec , 5 => product ref )
2020-09-07 10:18:17 +02:00
* Set this -> sousprods
*
* @ return void
*/
public function get_sousproduits_arbo ()
{
// phpcs:enable
$parent = array ();
2021-02-26 14:25:17 +01:00
foreach ( $this -> getChildsArbo ( $this -> id ) as $keyChild => $valueChild ) { // Warning. getChildsArbo can call getChildsArbo recursively. Starting point is $value[0]=id of product
2020-09-07 10:18:17 +02:00
$parent [ $this -> label ][ $keyChild ] = $valueChild ;
}
2021-02-26 14:25:17 +01:00
foreach ( $parent as $key => $value ) { // key=label, value is array of childs
2020-09-07 10:18:17 +02:00
$this -> sousprods [ $key ] = $value ;
}
}
/**
* Return clicable link of object ( with eventually picto )
*
* @ param int $withpicto Add picto into link
* @ param string $option Where point the link ( 'stock' , 'composition' , 'category' , 'supplier' , '' )
* @ param int $maxlength Maxlength of ref
* @ param int $save_lastsearch_value - 1 = Auto , 0 = No save of lastsearch_values when clicking , 1 = Save lastsearch_values whenclicking
* @ param int $notooltip No tooltip
* @ return string String with URL
*/
public function getNomUrl ( $withpicto = 0 , $option = '' , $maxlength = 0 , $save_lastsearch_value = - 1 , $notooltip = 0 )
{
global $conf , $langs , $hookmanager ;
include_once DOL_DOCUMENT_ROOT . '/core/lib/product.lib.php' ;
2020-11-02 11:41:07 +01:00
$result = '' ; $label = '' ;
2020-09-07 10:18:17 +02:00
$newref = $this -> ref ;
2020-11-02 11:41:07 +01:00
if ( $maxlength ) {
$newref = dol_trunc ( $newref , $maxlength , 'middle' );
2020-09-07 10:18:17 +02:00
}
2020-11-02 11:41:07 +01:00
if ( ! empty ( $this -> entity )) {
$tmpphoto = $this -> show_photos ( 'product' , $conf -> product -> multidir_output [ $this -> entity ], 1 , 1 , 0 , 0 , 0 , 80 );
if ( $this -> nbphoto > 0 ) {
2022-01-05 10:43:05 +01:00
$label .= '<div class="photointooltip floatright">' ;
2020-11-02 11:41:07 +01:00
$label .= $tmpphoto ;
2022-01-05 10:43:05 +01:00
$label .= '</div>' ;
//$label .= '<div style="clear: both;"></div>';
2020-11-02 11:41:07 +01:00
}
2020-09-07 10:18:17 +02:00
}
2020-11-02 11:41:07 +01:00
if ( $this -> type == Product :: TYPE_PRODUCT ) {
$label .= img_picto ( '' , 'product' ) . ' <u class="paddingrightonly">' . $langs -> trans ( " Product " ) . '</u>' ;
} elseif ( $this -> type == Product :: TYPE_SERVICE ) {
$label .= img_picto ( '' , 'service' ) . ' <u class="paddingrightonly">' . $langs -> trans ( " Service " ) . '</u>' ;
2020-09-07 10:18:17 +02:00
}
2020-11-02 11:41:07 +01:00
if ( isset ( $this -> status ) && isset ( $this -> status_buy )) {
$label .= ' ' . $this -> getLibStatut ( 5 , 0 );
$label .= ' ' . $this -> getLibStatut ( 5 , 1 );
}
2020-09-07 10:18:17 +02:00
if ( ! empty ( $this -> ref )) {
$label .= '<br><b>' . $langs -> trans ( 'ProductRef' ) . ':</b> ' . $this -> ref ;
}
if ( ! empty ( $this -> label )) {
$label .= '<br><b>' . $langs -> trans ( 'ProductLabel' ) . ':</b> ' . $this -> label ;
}
if ( $this -> type == Product :: TYPE_PRODUCT || ! empty ( $conf -> global -> STOCK_SUPPORTS_SERVICES )) {
if ( ! empty ( $conf -> productbatch -> enabled )) {
$langs -> load ( " productbatch " );
$label .= " <br><b> " . $langs -> trans ( " ManageLotSerial " ) . '</b>: ' . $this -> getLibStatut ( 0 , 2 );
}
}
if ( ! empty ( $conf -> barcode -> enabled )) {
$label .= '<br><b>' . $langs -> trans ( 'BarCode' ) . ':</b> ' . $this -> barcode ;
}
2021-02-26 14:25:17 +01:00
if ( $this -> type == Product :: TYPE_PRODUCT ) {
2020-09-07 10:18:17 +02:00
if ( $this -> weight ) {
$label .= " <br><b> " . $langs -> trans ( " Weight " ) . '</b>: ' . $this -> weight . ' ' . measuringUnitString ( 0 , " weight " , $this -> weight_units );
}
$labelsize = " " ;
if ( $this -> length ) {
$labelsize .= ( $labelsize ? " - " : " " ) . " <b> " . $langs -> trans ( " Length " ) . '</b>: ' . $this -> length . ' ' . measuringUnitString ( 0 , 'size' , $this -> length_units );
}
if ( $this -> width ) {
$labelsize .= ( $labelsize ? " - " : " " ) . " <b> " . $langs -> trans ( " Width " ) . '</b>: ' . $this -> width . ' ' . measuringUnitString ( 0 , 'size' , $this -> width_units );
}
if ( $this -> height ) {
$labelsize .= ( $labelsize ? " - " : " " ) . " <b> " . $langs -> trans ( " Height " ) . '</b>: ' . $this -> height . ' ' . measuringUnitString ( 0 , 'size' , $this -> height_units );
}
2021-02-26 14:25:17 +01:00
if ( $labelsize ) {
$label .= " <br> " . $labelsize ;
}
2020-09-07 10:18:17 +02:00
$labelsurfacevolume = " " ;
if ( $this -> surface ) {
$labelsurfacevolume .= ( $labelsurfacevolume ? " - " : " " ) . " <b> " . $langs -> trans ( " Surface " ) . '</b>: ' . $this -> surface . ' ' . measuringUnitString ( 0 , 'surface' , $this -> surface_units );
}
if ( $this -> volume ) {
$labelsurfacevolume .= ( $labelsurfacevolume ? " - " : " " ) . " <b> " . $langs -> trans ( " Volume " ) . '</b>: ' . $this -> volume . ' ' . measuringUnitString ( 0 , 'volume' , $this -> volume_units );
}
2021-02-26 14:25:17 +01:00
if ( $labelsurfacevolume ) {
$label .= " <br> " . $labelsurfacevolume ;
}
2020-09-07 10:18:17 +02:00
}
2021-02-27 00:31:29 +01:00
if ( ! empty ( $this -> pmp ) && $this -> pmp ) {
$label .= " <br><b> " . $langs -> trans ( " PMPValue " ) . '</b>: ' . price ( $this -> pmp , 0 , '' , 1 , - 1 , - 1 , $conf -> currency );
2020-09-07 10:18:17 +02:00
}
2022-05-16 19:54:49 +02:00
if ( ! empty ( $conf -> accounting -> enabled )) {
2022-05-28 12:50:25 +02:00
if ( $this -> status && isset ( $this -> accountancy_code_sell )) {
2022-05-16 19:54:49 +02:00
include_once DOL_DOCUMENT_ROOT . '/core/lib/accounting.lib.php' ;
$label .= '<br>' ;
$label .= '<br><b>' . $langs -> trans ( 'ProductAccountancySellCode' ) . ':</b> ' . length_accountg ( $this -> accountancy_code_sell );
$label .= '<br><b>' . $langs -> trans ( 'ProductAccountancySellIntraCode' ) . ':</b> ' . length_accountg ( $this -> accountancy_code_sell_intra );
$label .= '<br><b>' . $langs -> trans ( 'ProductAccountancySellExportCode' ) . ':</b> ' . length_accountg ( $this -> accountancy_code_sell_export );
}
2022-05-28 12:50:25 +02:00
if ( $this -> status_buy && isset ( $this -> accountancy_code_buy )) {
2022-05-16 19:54:49 +02:00
include_once DOL_DOCUMENT_ROOT . '/core/lib/accounting.lib.php' ;
if ( empty ( $this -> status )) {
$label .= '<br>' ;
}
$label .= '<br><b>' . $langs -> trans ( 'ProductAccountancyBuyCode' ) . ':</b> ' . length_accountg ( $this -> accountancy_code_buy );
$label .= '<br><b>' . $langs -> trans ( 'ProductAccountancyBuyIntraCode' ) . ':</b> ' . length_accountg ( $this -> accountancy_code_buy_intra );
$label .= '<br><b>' . $langs -> trans ( 'ProductAccountancyBuyExportCode' ) . ':</b> ' . length_accountg ( $this -> accountancy_code_buy_export );
}
2020-09-07 10:18:17 +02:00
}
$linkclose = '' ;
if ( empty ( $notooltip )) {
if ( ! empty ( $conf -> global -> MAIN_OPTIMIZEFORTEXTBROWSER )) {
$label = $langs -> trans ( " ShowProduct " );
$linkclose .= ' alt="' . dol_escape_htmltag ( $label , 1 ) . '"' ;
}
$linkclose .= ' title="' . dol_escape_htmltag ( $label , 1 , 1 ) . '"' ;
$linkclose .= ' class="nowraponall classfortooltip"' ;
} else {
$linkclose = ' class="nowraponall"' ;
}
if ( $option == 'supplier' || $option == 'category' ) {
$url = DOL_URL_ROOT . '/product/fournisseurs.php?id=' . $this -> id ;
} elseif ( $option == 'stock' ) {
$url = DOL_URL_ROOT . '/product/stock/product.php?id=' . $this -> id ;
} elseif ( $option == 'composition' ) {
$url = DOL_URL_ROOT . '/product/composition/card.php?id=' . $this -> id ;
} else {
$url = DOL_URL_ROOT . '/product/card.php?id=' . $this -> id ;
}
if ( $option !== 'nolink' ) {
// Add param to save lastsearch_values or not
$add_save_lastsearch_values = ( $save_lastsearch_value == 1 ? 1 : 0 );
2021-02-26 14:25:17 +01:00
if ( $save_lastsearch_value == - 1 && preg_match ( '/list\.php/' , $_SERVER [ " PHP_SELF " ])) {
$add_save_lastsearch_values = 1 ;
2020-09-07 10:18:17 +02:00
}
2021-02-26 14:25:17 +01:00
if ( $add_save_lastsearch_values ) {
$url .= '&save_lastsearch_values=1' ;
2020-09-07 10:18:17 +02:00
}
}
$linkstart = '<a href="' . $url . '"' ;
$linkstart .= $linkclose . '>' ;
$linkend = '</a>' ;
$result .= $linkstart ;
2021-02-26 14:25:17 +01:00
if ( $withpicto ) {
2020-09-07 10:18:17 +02:00
if ( $this -> type == Product :: TYPE_PRODUCT ) {
$result .= ( img_object (( $notooltip ? '' : $label ), 'product' , ( $notooltip ? 'class="paddingright"' : 'class="paddingright classfortooltip"' ), 0 , 0 , $notooltip ? 0 : 1 ));
}
if ( $this -> type == Product :: TYPE_SERVICE ) {
2021-02-24 14:38:11 +01:00
$result .= ( img_object (( $notooltip ? '' : $label ), 'service' , ( $notooltip ? 'class="paddingright"' : 'class="paddingright classfortooltip"' ), 0 , 0 , $notooltip ? 0 : 1 ));
2020-09-07 10:18:17 +02:00
}
}
$result .= $newref ;
$result .= $linkend ;
2018-08-29 21:25:22 +02:00
2020-09-07 10:18:17 +02:00
global $action ;
$hookmanager -> initHooks ( array ( 'productdao' ));
2022-02-15 18:31:05 +01:00
$parameters = array ( 'id' => $this -> id , 'getnomurl' => & $result );
2020-09-07 10:18:17 +02:00
$reshook = $hookmanager -> executeHooks ( 'getNomUrl' , $parameters , $this , $action ); // Note that $action and $object may have been modified by some hooks
if ( $reshook > 0 ) {
$result = $hookmanager -> resPrint ;
} else {
$result .= $hookmanager -> resPrint ;
}
2018-09-01 15:27:55 +02:00
2020-09-07 10:18:17 +02:00
return $result ;
}
2018-10-06 12:14:47 +02:00
2007-11-05 23:37:41 +01:00
2020-09-07 10:18:17 +02:00
/**
* Create a document onto disk according to template module .
*
* @ param string $modele Force model to use ( '' to not force )
* @ param Translate $outputlangs Object langs to use for output
* @ param int $hidedetails Hide details of lines
* @ param int $hidedesc Hide description
* @ param int $hideref Hide ref
* @ return int 0 if KO , 1 if OK
*/
public function generateDocument ( $modele , $outputlangs , $hidedetails = 0 , $hidedesc = 0 , $hideref = 0 )
{
global $conf , $user , $langs ;
2014-12-25 21:47:39 +01:00
2020-09-07 10:18:17 +02:00
$langs -> load ( " products " );
$outputlangs -> load ( " products " );
2020-04-24 23:56:57 +02:00
2020-09-07 10:18:17 +02:00
// Positionne le modele sur le nom du modele a utiliser
if ( ! dol_strlen ( $modele )) {
if ( ! empty ( $conf -> global -> PRODUCT_ADDON_PDF )) {
$modele = $conf -> global -> PRODUCT_ADDON_PDF ;
} else {
$modele = 'strato' ;
}
}
2020-04-24 23:56:57 +02:00
2020-09-07 10:18:17 +02:00
$modelpath = " core/modules/product/doc/ " ;
2014-10-18 14:56:06 +02:00
2020-09-07 10:18:17 +02:00
return $this -> commonGenerateDocument ( $modelpath , $modele , $outputlangs , $hidedetails , $hidedesc , $hideref );
}
2017-06-09 12:50:56 +02:00
2020-09-07 10:18:17 +02:00
/**
* Return label of status of object
*
* @ param int $mode 0 = long label , 1 = short label , 2 = Picto + short label , 3 = Picto , 4 = Picto + long label , 5 = Short label + Picto
* @ param int $type 0 = Sell , 1 = Buy , 2 = Batch Number management
* @ return string Label of status
*/
public function getLibStatut ( $mode = 0 , $type = 0 )
{
2021-02-26 14:25:17 +01:00
switch ( $type ) {
2020-09-07 10:18:17 +02:00
case 0 :
2021-02-26 14:25:17 +01:00
return $this -> LibStatut ( $this -> status , $mode , $type );
2020-09-07 10:18:17 +02:00
case 1 :
2021-02-26 14:25:17 +01:00
return $this -> LibStatut ( $this -> status_buy , $mode , $type );
2020-09-07 10:18:17 +02:00
case 2 :
2021-02-26 14:25:17 +01:00
return $this -> LibStatut ( $this -> status_batch , $mode , $type );
2020-09-07 10:18:17 +02:00
default :
//Simulate previous behavior but should return an error string
2021-02-26 14:25:17 +01:00
return $this -> LibStatut ( $this -> status_buy , $mode , $type );
2020-09-07 10:18:17 +02:00
}
}
2017-06-09 12:50:56 +02:00
2020-09-07 10:18:17 +02:00
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2019-10-24 16:09:35 +02:00
/**
2020-09-07 10:18:17 +02:00
* Return label of a given status
*
* @ param int $status Statut
* @ param int $mode 0 = long label , 1 = short label , 2 = Picto + short label , 3 = Picto , 4 = Picto + long label , 5 = Short label + Picto , 6 = Long label + Picto
* @ param int $type 0 = Status " to sell " , 1 = Status " to buy " , 2 = Status " to Batch "
* @ return string Label of status
*/
public function LibStatut ( $status , $mode = 0 , $type = 0 )
{
// phpcs:enable
global $conf , $langs ;
2008-07-27 22:47:13 +02:00
2020-09-07 10:18:17 +02:00
$labelStatus = $labelStatusShort = '' ;
2008-07-27 22:47:13 +02:00
2020-09-07 10:18:17 +02:00
$langs -> load ( 'products' );
2021-02-26 14:25:17 +01:00
if ( ! empty ( $conf -> productbatch -> enabled )) {
$langs -> load ( " productbatch " );
2020-09-07 10:18:17 +02:00
}
if ( $type == 2 ) {
2021-02-26 14:25:17 +01:00
switch ( $mode ) {
2020-09-07 10:18:17 +02:00
case 0 :
2021-10-16 19:37:57 +02:00
$label = ( $status == 0 ? $langs -> transnoentitiesnoconv ( 'ProductStatusNotOnBatch' ) : ( $status == 1 ? $langs -> transnoentitiesnoconv ( 'ProductStatusOnBatch' ) : $langs -> transnoentitiesnoconv ( 'ProductStatusOnSerial' )));
2020-09-07 10:18:17 +02:00
return dolGetStatus ( $label );
case 1 :
2021-10-16 19:37:57 +02:00
$label = ( $status == 0 ? $langs -> transnoentitiesnoconv ( 'ProductStatusNotOnBatchShort' ) : ( $status == 1 ? $langs -> transnoentitiesnoconv ( 'ProductStatusOnBatchShort' ) : $langs -> transnoentitiesnoconv ( 'ProductStatusOnSerialShort' )));
2020-09-07 10:18:17 +02:00
return dolGetStatus ( $label );
case 2 :
return $this -> LibStatut ( $status , 3 , 2 ) . ' ' . $this -> LibStatut ( $status , 1 , 2 );
case 3 :
2021-10-16 19:37:57 +02:00
return dolGetStatus ( $langs -> transnoentitiesnoconv ( 'ProductStatusNotOnBatch' ), '' , '' , empty ( $status ) ? 'status5' : 'status4' , 3 , 'dot' );
2020-09-07 10:18:17 +02:00
case 4 :
return $this -> LibStatut ( $status , 3 , 2 ) . ' ' . $this -> LibStatut ( $status , 0 , 2 );
case 5 :
return $this -> LibStatut ( $status , 1 , 2 ) . ' ' . $this -> LibStatut ( $status , 3 , 2 );
default :
2021-10-16 19:37:57 +02:00
return dolGetStatus ( $langs -> transnoentitiesnoconv ( 'Unknown' ));
2019-10-20 11:59:21 +02:00
}
2020-09-07 10:18:17 +02:00
}
2019-10-20 11:59:21 +02:00
2020-09-07 10:18:17 +02:00
$statuttrans = empty ( $status ) ? 'status5' : 'status4' ;
if ( $status == 0 ) {
// $type 0=Status "to sell", 1=Status "to buy", 2=Status "to Batch"
if ( $type == 0 ) {
2021-10-16 19:37:57 +02:00
$labelStatus = $langs -> transnoentitiesnoconv ( 'ProductStatusNotOnSellShort' );
$labelStatusShort = $langs -> transnoentitiesnoconv ( 'ProductStatusNotOnSell' );
2020-09-07 10:18:17 +02:00
} elseif ( $type == 1 ) {
2021-10-16 19:37:57 +02:00
$labelStatus = $langs -> transnoentitiesnoconv ( 'ProductStatusNotOnBuyShort' );
$labelStatusShort = $langs -> transnoentitiesnoconv ( 'ProductStatusNotOnBuy' );
2020-09-07 10:18:17 +02:00
} elseif ( $type == 2 ) {
2021-10-16 19:37:57 +02:00
$labelStatus = $langs -> transnoentitiesnoconv ( 'ProductStatusNotOnBatch' );
$labelStatusShort = $langs -> transnoentitiesnoconv ( 'ProductStatusNotOnBatchShort' );
2019-10-20 11:59:21 +02:00
}
2020-09-07 10:18:17 +02:00
} elseif ( $status == 1 ) {
// $type 0=Status "to sell", 1=Status "to buy", 2=Status "to Batch"
if ( $type == 0 ) {
2021-10-16 19:37:57 +02:00
$labelStatus = $langs -> transnoentitiesnoconv ( 'ProductStatusOnSellShort' );
$labelStatusShort = $langs -> transnoentitiesnoconv ( 'ProductStatusOnSell' );
2020-09-07 10:18:17 +02:00
} elseif ( $type == 1 ) {
2021-10-16 19:37:57 +02:00
$labelStatus = $langs -> transnoentitiesnoconv ( 'ProductStatusOnBuyShort' );
$labelStatusShort = $langs -> transnoentitiesnoconv ( 'ProductStatusOnBuy' );
2020-09-07 10:18:17 +02:00
} elseif ( $type == 2 ) {
2021-10-16 19:37:57 +02:00
$labelStatus = ( $status == 1 ? $langs -> transnoentitiesnoconv ( 'ProductStatusOnBatch' ) : $langs -> transnoentitiesnoconv ( 'ProductStatusOnSerial' ));
$labelStatusShort = ( $status == 1 ? $langs -> transnoentitiesnoconv ( 'ProductStatusOnBatchShort' ) : $langs -> transnoentitiesnoconv ( 'ProductStatusOnSerialShort' ));
2019-10-20 11:59:21 +02:00
}
2021-03-04 15:40:23 +01:00
} elseif ( $type == 2 && $status == 2 ) {
2021-10-16 19:37:57 +02:00
$labelStatus = $langs -> transnoentitiesnoconv ( 'ProductStatusOnSerial' );
$labelStatusShort = $langs -> transnoentitiesnoconv ( 'ProductStatusOnSerialShort' );
2020-09-07 10:18:17 +02:00
}
if ( $mode > 6 ) {
2021-10-16 19:37:57 +02:00
return dolGetStatus ( $langs -> transnoentitiesnoconv ( 'Unknown' ), '' , '' , 'status0' , 0 );
2020-09-07 10:18:17 +02:00
} else {
return dolGetStatus ( $labelStatus , $labelStatusShort , '' , $statuttrans , $mode );
}
}
/**
* Retour label of nature of product
*
* @ return string Label
*/
public function getLibFinished ()
{
global $langs ;
$langs -> load ( 'products' );
2020-10-31 14:32:18 +01:00
if ( isset ( $this -> finished ) && $this -> finished >= 0 ) {
2022-01-27 10:12:10 +01:00
$sql = " SELECT label, code FROM " . $this -> db -> prefix () . " c_product_nature where code = " . (( int ) $this -> finished ) . " AND active=1 " ;
2020-10-14 15:54:48 +02:00
$resql = $this -> db -> query ( $sql );
if ( $resql && $this -> db -> num_rows ( $resql ) > 0 ) {
$res = $this -> db -> fetch_array ( $resql );
$label = $langs -> trans ( $res [ 'label' ]);
$this -> db -> free ( $resql );
return $label ;
} else {
$this -> error = $this -> db -> error () . ' sql=' . $sql ;
2020-10-15 08:36:04 +02:00
dol_syslog ( __METHOD__ . ' Error ' . $this -> error , LOG_ERR );
2020-10-14 15:54:48 +02:00
return - 1 ;
}
2020-09-07 10:18:17 +02:00
}
2020-10-14 15:54:48 +02:00
2020-09-07 10:18:17 +02:00
return '' ;
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Adjust stock in a warehouse for product
*
* @ param User $user user asking change
* @ param int $id_entrepot id of warehouse
2021-10-05 18:43:35 +02:00
* @ param double $nbpiece nb of units ( should be always positive , use $movement to decide if we add or remove )
2020-09-07 10:18:17 +02:00
* @ param int $movement 0 = add , 1 = remove
* @ param string $label Label of stock movement
* @ param double $price Unit price HT of product , used to calculate average weighted price ( PMP in french ) . If 0 , average weighted price is not changed .
* @ param string $inventorycode Inventory code
* @ param string $origin_element Origin element type
* @ param int $origin_id Origin id of element
2020-12-15 15:18:01 +01:00
* @ param int $disablestockchangeforsubproduct Disable stock change for sub - products of kit ( usefull only if product is a subproduct )
2022-01-28 18:07:34 +01:00
* @ param array $extrafields Array of extrafields
* @ return int < 0 if KO , > 0 if OK
2020-09-07 10:18:17 +02:00
*/
2022-01-28 17:36:15 +01:00
public function correct_stock ( $user , $id_entrepot , $nbpiece , $movement , $label = '' , $price = 0 , $inventorycode = '' , $origin_element = '' , $origin_id = null , $disablestockchangeforsubproduct = 0 , $extrafields = null )
2020-09-07 10:18:17 +02:00
{
// phpcs:enable
if ( $id_entrepot ) {
$this -> db -> begin ();
include_once DOL_DOCUMENT_ROOT . '/product/stock/class/mouvementstock.class.php' ;
2021-10-05 00:25:52 +02:00
if ( $nbpiece < 0 ) {
if ( ! $movement ) {
$movement = 1 ;
}
2021-10-05 18:43:35 +02:00
$nbpiece = abs ( $nbpiece );
2021-10-05 00:25:52 +02:00
}
2020-09-07 10:18:17 +02:00
$op [ 0 ] = " + " . trim ( $nbpiece );
$op [ 1 ] = " - " . trim ( $nbpiece );
$movementstock = new MouvementStock ( $this -> db );
2021-10-02 19:16:42 +02:00
$movementstock -> setOrigin ( $origin_element , $origin_id ); // Set ->origin_type and ->origin_id
2020-12-15 15:18:01 +01:00
$result = $movementstock -> _create ( $user , $this -> id , $id_entrepot , $op [ $movement ], $movement , $price , $label , $inventorycode , '' , '' , '' , '' , false , 0 , $disablestockchangeforsubproduct );
2020-09-07 10:18:17 +02:00
if ( $result >= 0 ) {
2022-01-28 17:36:15 +01:00
if ( $extrafields ) {
2022-01-28 14:57:36 +01:00
$array_options = $extrafields -> getOptionalsFromPost ( 'stock_mouvement' );
$movementstock -> array_options = $array_options ;
$movementstock -> insertExtraFields ();
}
2020-09-07 10:18:17 +02:00
$this -> db -> commit ();
return 1 ;
} else {
$this -> error = $movementstock -> error ;
$this -> errors = $movementstock -> errors ;
$this -> db -> rollback ();
return - 1 ;
2019-10-20 11:59:21 +02:00
}
}
2020-09-07 10:18:17 +02:00
}
2019-01-26 15:20:36 +01:00
2020-09-07 10:18:17 +02:00
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Adjust stock in a warehouse for product with batch number
*
* @ param User $user user asking change
* @ param int $id_entrepot id of warehouse
2021-10-05 18:43:35 +02:00
* @ param double $nbpiece nb of units ( should be always positive , use $movement to decide if we add or remove )
2020-09-07 10:18:17 +02:00
* @ param int $movement 0 = add , 1 = remove
* @ param string $label Label of stock movement
* @ param double $price Price to use for stock eval
* @ param integer $dlc eat - by date
* @ param integer $dluo sell - by date
* @ param string $lot Lot number
* @ param string $inventorycode Inventory code
* @ param string $origin_element Origin element type
* @ param int $origin_id Origin id of element
2020-12-15 15:18:01 +01:00
* @ param int $disablestockchangeforsubproduct Disable stock change for sub - products of kit ( usefull only if product is a subproduct )
2022-01-28 18:07:34 +01:00
* @ param array $extrafields Array of extrafields
2020-09-07 10:18:17 +02:00
* @ return int < 0 if KO , > 0 if OK
*/
2022-01-28 17:36:15 +01:00
public function correct_stock_batch ( $user , $id_entrepot , $nbpiece , $movement , $label = '' , $price = 0 , $dlc = '' , $dluo = '' , $lot = '' , $inventorycode = '' , $origin_element = '' , $origin_id = null , $disablestockchangeforsubproduct = 0 , $extrafields = null )
2020-09-07 10:18:17 +02:00
{
// phpcs:enable
if ( $id_entrepot ) {
$this -> db -> begin ();
2020-01-24 14:51:45 +01:00
2020-09-07 10:18:17 +02:00
include_once DOL_DOCUMENT_ROOT . '/product/stock/class/mouvementstock.class.php' ;
2020-01-24 14:51:45 +01:00
2021-10-05 00:25:52 +02:00
if ( $nbpiece < 0 ) {
if ( ! $movement ) {
$movement = 1 ;
}
2021-10-05 18:43:35 +02:00
$nbpiece = abs ( $nbpiece );
2021-10-05 00:25:52 +02:00
}
2020-09-07 10:18:17 +02:00
$op [ 0 ] = " + " . trim ( $nbpiece );
$op [ 1 ] = " - " . trim ( $nbpiece );
2019-09-10 20:46:54 +02:00
2020-09-07 10:18:17 +02:00
$movementstock = new MouvementStock ( $this -> db );
2021-10-02 19:16:42 +02:00
$movementstock -> setOrigin ( $origin_element , $origin_id ); // Set ->origin_type and ->fk_origin
2020-12-15 15:18:01 +01:00
$result = $movementstock -> _create ( $user , $this -> id , $id_entrepot , $op [ $movement ], $movement , $price , $label , $inventorycode , '' , $dlc , $dluo , $lot , false , 0 , $disablestockchangeforsubproduct );
2019-09-10 20:46:54 +02:00
2020-09-07 10:18:17 +02:00
if ( $result >= 0 ) {
2022-01-28 17:36:15 +01:00
if ( $extrafields ) {
2022-01-28 14:57:36 +01:00
$array_options = $extrafields -> getOptionalsFromPost ( 'stock_mouvement' );
$movementstock -> array_options = $array_options ;
$movementstock -> insertExtraFields ();
}
2020-09-07 10:18:17 +02:00
$this -> db -> commit ();
return 1 ;
} else {
$this -> error = $movementstock -> error ;
$this -> errors = $movementstock -> errors ;
2010-02-21 02:45:58 +01:00
2020-09-07 10:18:17 +02:00
$this -> db -> rollback ();
return - 1 ;
}
}
}
2018-11-06 12:13:16 +01:00
2020-09-07 10:18:17 +02:00
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Load information about stock of a product into -> stock_reel , -> stock_warehouse [] ( including stock_warehouse [ idwarehouse ] -> detail_batch for batch products )
* This function need a lot of load . If you use it on list , use a cache to execute it once for each product id .
2021-06-24 11:51:10 +02:00
* If ENTREPOT_EXTRA_STATUS is set , filtering on warehouse status is possible .
2020-09-07 10:18:17 +02:00
*
* @ param string $option '' = Load all stock info , also from closed and internal warehouses , 'nobatch' , 'novirtual'
2021-06-24 11:51:10 +02:00
* You can also filter on 'warehouseclosed' , 'warehouseopen' , 'warehouseinternal'
2020-09-07 10:18:17 +02:00
* @ param int $includedraftpoforvirtual Include draft status of PO for virtual stock calculation
* @ return int < 0 if KO , > 0 if OK
* @ see load_virtual_stock (), loadBatchInfo ()
*/
public function load_stock ( $option = '' , $includedraftpoforvirtual = null )
{
// phpcs:enable
global $conf ;
$this -> stock_reel = 0 ;
$this -> stock_warehouse = array ();
$this -> stock_theorique = 0 ;
2021-06-24 11:51:10 +02:00
// Set filter on warehouse status
2020-09-07 10:18:17 +02:00
$warehouseStatus = array ();
if ( preg_match ( '/warehouseclosed/' , $option )) {
2021-06-24 11:51:10 +02:00
$warehouseStatus [ Entrepot :: STATUS_CLOSED ] = Entrepot :: STATUS_CLOSED ;
2020-09-07 10:18:17 +02:00
}
if ( preg_match ( '/warehouseopen/' , $option )) {
2021-06-24 11:51:10 +02:00
$warehouseStatus [ Entrepot :: STATUS_OPEN_ALL ] = Entrepot :: STATUS_OPEN_ALL ;
2020-09-07 10:18:17 +02:00
}
if ( preg_match ( '/warehouseinternal/' , $option )) {
2021-06-24 11:51:10 +02:00
if ( ! empty ( $conf -> global -> ENTREPOT_EXTRA_STATUS )) {
$warehouseStatus [ Entrepot :: STATUS_OPEN_INTERNAL ] = Entrepot :: STATUS_OPEN_INTERNAL ;
} else {
$warehouseStatus [ Entrepot :: STATUS_OPEN_ALL ] = Entrepot :: STATUS_OPEN_ALL ;
}
2020-03-01 04:52:12 +01:00
}
2017-01-06 16:44:09 +01:00
2020-09-07 10:18:17 +02:00
$sql = " SELECT ps.rowid, ps.reel, ps.fk_entrepot " ;
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " product_stock as ps " ;
$sql .= " , " . $this -> db -> prefix () . " entrepot as w " ;
2020-09-07 10:18:17 +02:00
$sql .= " WHERE w.entity IN ( " . getEntity ( 'stock' ) . " ) " ;
$sql .= " AND w.rowid = ps.fk_entrepot " ;
2021-08-23 18:56:46 +02:00
$sql .= " AND ps.fk_product = " . (( int ) $this -> id );
2021-06-24 11:51:10 +02:00
if ( count ( $warehouseStatus )) {
2021-03-22 11:30:18 +01:00
$sql .= " AND w.statut IN ( " . $this -> db -> sanitize ( implode ( ',' , $warehouseStatus )) . " ) " ;
2020-09-07 10:18:17 +02:00
}
2021-06-01 21:43:05 +02:00
$sql .= " ORDER BY ps.reel " . ( ! empty ( $conf -> global -> DO_NOT_TRY_TO_DEFRAGMENT_STOCKS_WAREHOUSE ) ? 'DESC' : 'ASC' ); // Note : qty ASC is important for expedition card, to avoid stock fragmentation;
2020-09-07 10:18:17 +02:00
dol_syslog ( get_class ( $this ) . " ::load_stock " , LOG_DEBUG );
$result = $this -> db -> query ( $sql );
if ( $result ) {
$num = $this -> db -> num_rows ( $result );
$i = 0 ;
if ( $num > 0 ) {
2021-02-26 14:25:17 +01:00
while ( $i < $num ) {
2020-09-07 10:18:17 +02:00
$row = $this -> db -> fetch_object ( $result );
$this -> stock_warehouse [ $row -> fk_entrepot ] = new stdClass ();
$this -> stock_warehouse [ $row -> fk_entrepot ] -> real = $row -> reel ;
$this -> stock_warehouse [ $row -> fk_entrepot ] -> id = $row -> rowid ;
if (( ! preg_match ( '/nobatch/' , $option )) && $this -> hasbatch ()) {
2019-11-29 11:42:08 +01:00
$this -> stock_warehouse [ $row -> fk_entrepot ] -> detail_batch = Productbatch :: findAll ( $this -> db , $row -> rowid , 1 , $this -> id );
2020-09-07 10:18:17 +02:00
}
$this -> stock_reel += $row -> reel ;
$i ++ ;
}
}
$this -> db -> free ( $result );
2018-11-06 12:13:16 +01:00
2020-09-07 10:18:17 +02:00
if ( ! preg_match ( '/novirtual/' , $option )) {
2020-11-19 19:56:00 +01:00
$this -> load_virtual_stock ( $includedraftpoforvirtual ); // This also load all arrays stats_xxx...
2020-09-07 10:18:17 +02:00
}
2018-11-06 12:13:16 +01:00
2020-09-07 10:18:17 +02:00
return 1 ;
} else {
$this -> error = $this -> db -> lasterror ();
return - 1 ;
}
}
2008-07-27 22:47:13 +02:00
2017-12-01 13:40:24 +01:00
2019-02-24 23:32:09 +01:00
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2014-07-03 10:24:29 +02:00
/**
2020-04-16 16:02:06 +02:00
* Load value -> stock_theorique of a product . Property this -> id must be defined .
* This function need a lot of load . If you use it on list , use a cache to execute it one for each product id .
2014-07-03 10:24:29 +02:00
*
2021-05-25 21:53:44 +02:00
* @ param int $includedraftpoforvirtual Include draft status and not yet approved Purchase Orders for virtual stock calculation
2020-04-16 16:02:06 +02:00
* @ return int < 0 if KO , > 0 if OK
* @ see load_stock (), loadBatchInfo ()
2014-07-03 10:24:29 +02:00
*/
2020-04-15 16:05:50 +02:00
public function load_virtual_stock ( $includedraftpoforvirtual = null )
2018-10-15 13:26:18 +02:00
{
// phpcs:enable
global $conf , $hookmanager , $action ;
2014-07-03 10:24:29 +02:00
2019-11-13 19:37:08 +01:00
$stock_commande_client = 0 ;
$stock_commande_fournisseur = 0 ;
$stock_sending_client = 0 ;
$stock_reception_fournisseur = 0 ;
2020-01-23 20:59:18 +01:00
$stock_inproduction = 0 ;
2014-07-04 15:06:45 +02:00
2020-02-28 16:12:42 +01:00
//dol_syslog("load_virtual_stock");
2021-02-26 14:25:17 +01:00
if ( ! empty ( $conf -> commande -> enabled )) {
2019-11-13 19:37:08 +01:00
$result = $this -> load_stats_commande ( 0 , '1,2' , 1 );
2021-02-26 14:25:17 +01:00
if ( $result < 0 ) {
dol_print_error ( $this -> db , $this -> error );
}
2019-11-13 19:37:08 +01:00
$stock_commande_client = $this -> stats_commande [ 'qty' ];
2018-10-15 13:26:18 +02:00
}
2021-02-26 14:25:17 +01:00
if ( ! empty ( $conf -> expedition -> enabled )) {
2020-09-07 10:18:17 +02:00
require_once DOL_DOCUMENT_ROOT . '/expedition/class/expedition.class.php' ;
$filterShipmentStatus = '' ;
if ( ! empty ( $conf -> global -> STOCK_CALCULATE_ON_SHIPMENT )) {
$filterShipmentStatus = Expedition :: STATUS_VALIDATED . ',' . Expedition :: STATUS_CLOSED ;
} elseif ( ! empty ( $conf -> global -> STOCK_CALCULATE_ON_SHIPMENT_CLOSE )) {
$filterShipmentStatus = Expedition :: STATUS_CLOSED ;
}
2019-11-14 09:52:13 +01:00
$result = $this -> load_stats_sending ( 0 , '1,2' , 1 , $filterShipmentStatus );
2021-02-26 14:25:17 +01:00
if ( $result < 0 ) {
dol_print_error ( $this -> db , $this -> error );
}
2019-11-13 19:37:08 +01:00
$stock_sending_client = $this -> stats_expedition [ 'qty' ];
2018-10-15 13:26:18 +02:00
}
2021-04-11 04:24:21 +02:00
if (( ! empty ( $conf -> fournisseur -> enabled ) && empty ( $conf -> global -> MAIN_USE_NEW_SUPPLIERMOD )) || ! empty ( $conf -> supplier_order -> enabled )) {
2021-10-11 15:24:56 +02:00
$filterStatus = empty ( $conf -> global -> SUPPLIER_ORDER_STATUS_FOR_VIRTUAL_STOCK ) ? '3,4' : $conf -> global -> SUPPLIER_ORDER_STATUS_FOR_VIRTUAL_STOCK ;
2021-02-26 14:25:17 +01:00
if ( isset ( $includedraftpoforvirtual )) {
2021-05-25 21:53:44 +02:00
$filterStatus = '0,1,2,' . $filterStatus ; // 1,2 may have already been inside $filterStatus but it is better to have twice than missing $filterStatus does not include them
2021-02-26 14:25:17 +01:00
}
2020-04-15 16:05:50 +02:00
$result = $this -> load_stats_commande_fournisseur ( 0 , $filterStatus , 1 );
2021-02-26 14:25:17 +01:00
if ( $result < 0 ) {
dol_print_error ( $this -> db , $this -> error );
}
2019-11-13 19:37:08 +01:00
$stock_commande_fournisseur = $this -> stats_commande_fournisseur [ 'qty' ];
2020-01-23 20:59:18 +01:00
}
2022-02-04 13:02:45 +01:00
if ((( ! empty ( $conf -> fournisseur -> enabled ) && empty ( $conf -> global -> MAIN_USE_NEW_SUPPLIERMOD )) || ! empty ( $conf -> supplier_order -> enabled ) || ! empty ( $conf -> supplier_invoice -> enabled )) && empty ( $conf -> reception -> enabled )) {
2022-02-04 12:55:31 +01:00
// Case module reception is not used
2020-09-07 10:18:17 +02:00
$filterStatus = '4' ;
2022-02-04 13:02:45 +01:00
if ( isset ( $includedraftpoforvirtual )) {
$filterStatus = '0,' . $filterStatus ;
}
2020-04-15 16:05:50 +02:00
$result = $this -> load_stats_reception ( 0 , $filterStatus , 1 );
2022-02-04 19:05:11 +01:00
if ( $result < 0 ) {
dol_print_error ( $this -> db , $this -> error );
}
2019-11-13 19:37:08 +01:00
$stock_reception_fournisseur = $this -> stats_reception [ 'qty' ];
2018-10-15 13:26:18 +02:00
}
2021-09-30 12:09:17 +02:00
if ((( ! empty ( $conf -> fournisseur -> enabled ) && empty ( $conf -> global -> MAIN_USE_NEW_SUPPLIERMOD )) || ! empty ( $conf -> supplier_order -> enabled ) || ! empty ( $conf -> supplier_invoice -> enabled )) && ! empty ( $conf -> reception -> enabled )) {
2022-02-04 12:55:31 +01:00
// Case module reception is used
2020-09-07 10:18:17 +02:00
$filterStatus = '4' ;
2021-02-26 14:25:17 +01:00
if ( isset ( $includedraftpoforvirtual )) {
$filterStatus = '0,' . $filterStatus ;
}
2020-04-23 18:55:10 +02:00
$result = $this -> load_stats_reception ( 0 , $filterStatus , 1 ); // Use same tables than when module reception is not used.
2021-02-26 14:25:17 +01:00
if ( $result < 0 ) {
dol_print_error ( $this -> db , $this -> error );
}
2020-01-23 20:59:18 +01:00
$stock_reception_fournisseur = $this -> stats_reception [ 'qty' ];
}
2021-02-26 14:25:17 +01:00
if ( ! empty ( $conf -> mrp -> enabled )) {
2020-01-24 14:51:45 +01:00
$result = $this -> load_stats_inproduction ( 0 , '1,2' , 1 );
2021-02-26 14:25:17 +01:00
if ( $result < 0 ) {
dol_print_error ( $this -> db , $this -> error );
}
2020-01-24 14:51:45 +01:00
$stock_inproduction = $this -> stats_mrptoproduce [ 'qty' ] - $this -> stats_mrptoconsume [ 'qty' ];
2020-01-23 20:59:18 +01:00
}
$this -> stock_theorique = $this -> stock_reel + $stock_inproduction ;
2014-07-03 10:24:29 +02:00
2018-10-15 13:26:18 +02:00
// Stock decrease mode
2019-11-13 19:37:08 +01:00
if ( ! empty ( $conf -> global -> STOCK_CALCULATE_ON_SHIPMENT ) || ! empty ( $conf -> global -> STOCK_CALCULATE_ON_SHIPMENT_CLOSE )) {
2020-01-23 20:59:18 +01:00
$this -> stock_theorique -= ( $stock_commande_client - $stock_sending_client );
2020-05-21 15:05:19 +02:00
} elseif ( ! empty ( $conf -> global -> STOCK_CALCULATE_ON_VALIDATE_ORDER )) {
2020-01-23 20:59:18 +01:00
$this -> stock_theorique += 0 ;
2020-05-21 15:05:19 +02:00
} elseif ( ! empty ( $conf -> global -> STOCK_CALCULATE_ON_BILL )) {
2020-01-23 20:59:18 +01:00
$this -> stock_theorique -= $stock_commande_client ;
2018-10-15 13:26:18 +02:00
}
// Stock Increase mode
2020-09-07 10:18:17 +02:00
if ( ! empty ( $conf -> global -> STOCK_CALCULATE_ON_RECEPTION ) || ! empty ( $conf -> global -> STOCK_CALCULATE_ON_RECEPTION_CLOSE )) {
$this -> stock_theorique += ( $stock_commande_fournisseur - $stock_reception_fournisseur );
} elseif ( ! empty ( $conf -> global -> STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER )) {
2020-01-23 20:59:18 +01:00
$this -> stock_theorique += ( $stock_commande_fournisseur - $stock_reception_fournisseur );
2020-05-21 15:05:19 +02:00
} elseif ( ! empty ( $conf -> global -> STOCK_CALCULATE_ON_SUPPLIER_VALIDATE_ORDER )) {
2019-11-13 19:37:08 +01:00
$this -> stock_theorique -= $stock_reception_fournisseur ;
2020-05-21 15:05:19 +02:00
} elseif ( ! empty ( $conf -> global -> STOCK_CALCULATE_ON_SUPPLIER_BILL )) {
2020-01-23 20:59:18 +01:00
$this -> stock_theorique += ( $stock_commande_fournisseur - $stock_reception_fournisseur );
2018-10-15 13:26:18 +02:00
}
2018-05-02 17:29:31 +02:00
2019-11-13 19:37:08 +01:00
if ( ! is_object ( $hookmanager )) {
2018-10-15 13:26:18 +02:00
include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php' ;
2019-11-13 19:37:08 +01:00
$hookmanager = new HookManager ( $this -> db );
2018-10-15 13:26:18 +02:00
}
$hookmanager -> initHooks ( array ( 'productdao' ));
2020-04-15 16:05:50 +02:00
$parameters = array ( 'id' => $this -> id , 'includedraftpoforvirtual' => $includedraftpoforvirtual );
2018-10-15 13:26:18 +02:00
// Note that $action and $object may have been modified by some hooks
2019-11-13 19:37:08 +01:00
$reshook = $hookmanager -> executeHooks ( 'loadvirtualstock' , $parameters , $this , $action );
2021-02-26 14:25:17 +01:00
if ( $reshook > 0 ) {
$this -> stock_theorique = $hookmanager -> resArray [ 'stock_theorique' ];
}
2018-05-02 17:29:31 +02:00
2018-10-15 13:26:18 +02:00
return 1 ;
}
2017-06-09 11:02:34 +02:00
2016-07-14 18:40:49 +02:00
2020-09-07 10:18:17 +02:00
/**
* Load existing information about a serial
*
* @ param string $batch Lot / serial number
* @ return array Array with record into product_batch
* @ see load_stock (), load_virtual_stock ()
*/
public function loadBatchInfo ( $batch )
{
$result = array ();
2022-01-27 10:12:10 +01:00
$sql = " SELECT pb.batch, pb.eatby, pb.sellby, SUM(pb.qty) AS qty FROM " . $this -> db -> prefix () . " product_batch as pb, " . $this -> db -> prefix () . " product_stock as ps " ;
2021-08-23 18:56:46 +02:00
$sql .= " WHERE pb.fk_product_stock = ps.rowid AND ps.fk_product = " . (( int ) $this -> id ) . " AND pb.batch = ' " . $this -> db -> escape ( $batch ) . " ' " ;
2020-09-07 10:18:17 +02:00
$sql .= " GROUP BY pb.batch, pb.eatby, pb.sellby " ;
dol_syslog ( get_class ( $this ) . " ::loadBatchInfo load first entry found for lot/serial = " . $batch , LOG_DEBUG );
$resql = $this -> db -> query ( $sql );
if ( $resql ) {
$num = $this -> db -> num_rows ( $resql );
$i = 0 ;
2021-02-26 14:25:17 +01:00
while ( $i < $num ) {
2020-09-07 10:18:17 +02:00
$obj = $this -> db -> fetch_object ( $resql );
$result [] = array ( 'batch' => $batch , 'eatby' => $this -> db -> jdate ( $obj -> eatby ), 'sellby' => $this -> db -> jdate ( $obj -> sellby ), 'qty' => $obj -> qty );
$i ++ ;
}
return $result ;
} else {
dol_print_error ( $this -> db );
$this -> db -> rollback ();
return array ();
}
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Move an uploaded file described into $file array into target directory $sdir .
*
* @ param string $sdir Target directory
* @ param string $file Array of file info of file to upload : array ( 'name' =>... , 'tmp_name' =>... )
* @ return int < 0 if KO , > 0 if OK
*/
public function add_photo ( $sdir , $file )
{
// phpcs:enable
global $conf ;
include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php' ;
$result = 0 ;
$dir = $sdir ;
if ( ! empty ( $conf -> global -> PRODUCT_USE_OLD_PATH_FOR_PHOTO )) {
$dir .= '/' . get_exdir ( $this -> id , 2 , 0 , 0 , $this , 'product' ) . $this -> id . " /photos " ;
} else {
$dir .= '/' . get_exdir ( 0 , 0 , 0 , 0 , $this , 'product' ) . dol_sanitizeFileName ( $this -> ref );
}
dol_mkdir ( $dir );
$dir_osencoded = $dir ;
if ( is_dir ( $dir_osencoded )) {
$originImage = $dir . '/' . $file [ 'name' ];
// Cree fichier en taille origine
$result = dol_move_uploaded_file ( $file [ 'tmp_name' ], $originImage , 1 );
if ( file_exists ( dol_osencode ( $originImage ))) {
// Create thumbs
$this -> addThumbs ( $originImage );
}
}
if ( is_numeric ( $result ) && $result > 0 ) {
return 1 ;
} else {
return - 1 ;
}
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Return if at least one photo is available
*
* @ param string $sdir Directory to scan
* @ return boolean True if at least one photo is available , False if not
*/
public function is_photo_available ( $sdir )
{
// phpcs:enable
include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php' ;
include_once DOL_DOCUMENT_ROOT . '/core/lib/images.lib.php' ;
global $conf ;
$dir = $sdir ;
if ( ! empty ( $conf -> global -> PRODUCT_USE_OLD_PATH_FOR_PHOTO )) {
$dir .= '/' . get_exdir ( $this -> id , 2 , 0 , 0 , $this , 'product' ) . $this -> id . " /photos/ " ;
} else {
2020-12-17 11:57:12 +01:00
$dir .= '/' . get_exdir ( 0 , 0 , 0 , 0 , $this , 'product' );
2020-09-07 10:18:17 +02:00
}
$nbphoto = 0 ;
$dir_osencoded = dol_osencode ( $dir );
if ( file_exists ( $dir_osencoded )) {
$handle = opendir ( $dir_osencoded );
if ( is_resource ( $handle )) {
2021-02-26 14:25:17 +01:00
while (( $file = readdir ( $handle )) !== false ) {
2020-09-07 10:18:17 +02:00
if ( ! utf8_check ( $file )) {
$file = utf8_encode ( $file ); // To be sure data is stored in UTF8 in memory
}
if ( dol_is_file ( $dir . $file ) && image_format_supported ( $file ) >= 0 ) {
return true ;
}
}
}
}
return false ;
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
2021-05-28 18:04:45 +02:00
* Return an array with all photos of product found on disk . There is no sorting criteria .
2020-09-07 10:18:17 +02:00
*
2021-05-28 18:04:45 +02:00
* @ param string $dir Directory to scan
* @ param int $nbmax Number maxium of photos ( 0 = no maximum )
* @ return array Array of photos
2020-09-07 10:18:17 +02:00
*/
public function liste_photos ( $dir , $nbmax = 0 )
{
// phpcs:enable
include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php' ;
include_once DOL_DOCUMENT_ROOT . '/core/lib/images.lib.php' ;
$nbphoto = 0 ;
$tabobj = array ();
$dir_osencoded = dol_osencode ( $dir );
$handle = @ opendir ( $dir_osencoded );
if ( is_resource ( $handle )) {
2021-02-26 14:25:17 +01:00
while (( $file = readdir ( $handle )) !== false ) {
if ( ! utf8_check ( $file )) {
$file = utf8_encode ( $file ); // readdir returns ISO
2020-09-07 10:18:17 +02:00
}
if ( dol_is_file ( $dir . $file ) && image_format_supported ( $file ) >= 0 ) {
$nbphoto ++ ;
2021-05-28 18:04:45 +02:00
// We forge name of thumb.
2020-09-07 10:18:17 +02:00
$photo = $file ;
$photo_vignette = '' ;
2021-05-28 18:04:45 +02:00
$regs = array ();
2020-09-07 10:18:17 +02:00
if ( preg_match ( '/(' . $this -> regeximgext . ')$/i' , $photo , $regs )) {
$photo_vignette = preg_replace ( '/' . $regs [ 0 ] . '/i' , '' , $photo ) . '_small' . $regs [ 0 ];
}
$dirthumb = $dir . 'thumbs/' ;
// Objet
$obj = array ();
$obj [ 'photo' ] = $photo ;
2021-02-26 14:25:17 +01:00
if ( $photo_vignette && dol_is_file ( $dirthumb . $photo_vignette )) {
$obj [ 'photo_vignette' ] = 'thumbs/' . $photo_vignette ;
} else {
$obj [ 'photo_vignette' ] = " " ;
2020-09-07 10:18:17 +02:00
}
$tabobj [ $nbphoto - 1 ] = $obj ;
2021-05-28 18:04:45 +02:00
// Do we have to continue with next photo ?
2021-02-26 14:25:17 +01:00
if ( $nbmax && $nbphoto >= $nbmax ) {
break ;
2020-09-07 10:18:17 +02:00
}
}
}
closedir ( $handle );
}
return $tabobj ;
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
2021-05-28 18:04:45 +02:00
* Delete a photo and its thumbs
2020-09-07 10:18:17 +02:00
*
2021-05-28 18:04:45 +02:00
* @ param string $file Path to image file
2020-09-07 10:18:17 +02:00
* @ return void
*/
public function delete_photo ( $file )
{
// phpcs:enable
include_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php' ;
include_once DOL_DOCUMENT_ROOT . '/core/lib/images.lib.php' ;
$dir = dirname ( $file ) . '/' ; // Chemin du dossier contenant l'image d'origine
$dirthumb = $dir . '/thumbs/' ; // Chemin du dossier contenant la vignette
$filename = preg_replace ( '/' . preg_quote ( $dir , '/' ) . '/i' , '' , $file ); // Nom du fichier
// On efface l'image d'origine
dol_delete_file ( $file , 0 , 0 , 0 , $this ); // For triggers
// Si elle existe, on efface la vignette
if ( preg_match ( '/(' . $this -> regeximgext . ')$/i' , $filename , $regs )) {
$photo_vignette = preg_replace ( '/' . $regs [ 0 ] . '/i' , '' , $filename ) . '_small' . $regs [ 0 ];
if ( file_exists ( dol_osencode ( $dirthumb . $photo_vignette ))) {
dol_delete_file ( $dirthumb . $photo_vignette );
}
$photo_vignette = preg_replace ( '/' . $regs [ 0 ] . '/i' , '' , $filename ) . '_mini' . $regs [ 0 ];
if ( file_exists ( dol_osencode ( $dirthumb . $photo_vignette ))) {
dol_delete_file ( $dirthumb . $photo_vignette );
}
}
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Load size of image file
*
* @ param string $file Path to file
* @ return void
*/
public function get_image_size ( $file )
{
// phpcs:enable
$file_osencoded = dol_osencode ( $file );
$infoImg = getimagesize ( $file_osencoded ); // Get information on image
$this -> imgWidth = $infoImg [ 0 ]; // Largeur de l'image
$this -> imgHeight = $infoImg [ 1 ]; // Hauteur de l'image
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Load indicators this -> nb for the dashboard
*
* @ return int < 0 if KO , > 0 if OK
*/
public function load_state_board ()
{
// phpcs:enable
2022-03-22 16:49:29 +01:00
global $hookmanager ;
2020-09-07 10:18:17 +02:00
$this -> nb = array ();
$sql = " SELECT count(p.rowid) as nb, fk_product_type " ;
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . " product as p " ;
2020-09-07 10:18:17 +02:00
$sql .= ' WHERE p.entity IN (' . getEntity ( $this -> element , 1 ) . ')' ;
// Add where from hooks
if ( is_object ( $hookmanager )) {
$parameters = array ();
2022-03-22 15:51:30 +01:00
$reshook = $hookmanager -> executeHooks ( 'printFieldListWhere' , $parameters , $this ); // Note that $action and $object may have been modified by hook
2020-09-07 10:18:17 +02:00
$sql .= $hookmanager -> resPrint ;
}
$sql .= ' GROUP BY fk_product_type' ;
$resql = $this -> db -> query ( $sql );
if ( $resql ) {
2021-02-26 14:25:17 +01:00
while ( $obj = $this -> db -> fetch_object ( $resql )) {
if ( $obj -> fk_product_type == 1 ) {
$this -> nb [ " services " ] = $obj -> nb ;
} else {
$this -> nb [ " products " ] = $obj -> nb ;
2020-09-07 10:18:17 +02:00
}
}
$this -> db -> free ( $resql );
return 1 ;
} else {
dol_print_error ( $this -> db );
$this -> error = $this -> db -> error ();
return - 1 ;
}
}
/**
* Return if object is a product
*
* @ return boolean True if it ' s a product
*/
public function isProduct ()
{
return ( $this -> type == Product :: TYPE_PRODUCT ? true : false );
}
/**
* Return if object is a product
*
* @ return boolean True if it ' s a service
*/
public function isService ()
{
return ( $this -> type == Product :: TYPE_SERVICE ? true : false );
}
2021-07-20 11:23:35 +02:00
/**
* Return if object have a constraint on mandatory_period
*
* @ return boolean True if mandatory_period setted to 1
*/
public function isMandatoryPeriod ()
{
return ( $this -> mandatory_period == 1 ? true : false );
}
2020-09-07 10:18:17 +02:00
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Get a barcode from the module to generate barcode values .
* Return value is stored into this -> barcode
*
* @ param Product $object Object product or service
* @ param string $type Barcode type ( ean , isbn , ... )
* @ return string
*/
public function get_barcode ( $object , $type = '' )
{
// phpcs:enable
global $conf ;
$result = '' ;
if ( ! empty ( $conf -> global -> BARCODE_PRODUCT_ADDON_NUM )) {
$dirsociete = array_merge ( array ( '/core/modules/barcode/' ), $conf -> modules_parts [ 'barcode' ]);
2021-02-26 14:25:17 +01:00
foreach ( $dirsociete as $dirroot ) {
2020-09-07 10:18:17 +02:00
$res = dol_include_once ( $dirroot . $conf -> global -> BARCODE_PRODUCT_ADDON_NUM . '.php' );
2021-02-26 14:25:17 +01:00
if ( $res ) {
break ;
2020-09-07 10:18:17 +02:00
}
}
$var = $conf -> global -> BARCODE_PRODUCT_ADDON_NUM ;
$mod = new $var ;
$result = $mod -> getNextValue ( $object , $type );
dol_syslog ( get_class ( $this ) . " ::get_barcode barcode= " . $result . " module= " . $var );
}
return $result ;
}
/**
* Initialise an instance with random values .
* Used to build previews or test instances .
* id must be 0 if object instance is a specimen .
*
* @ return void
*/
public function initAsSpecimen ()
{
global $user , $langs , $conf , $mysoc ;
$now = dol_now ();
// Initialize parameters
$this -> specimen = 1 ;
$this -> id = 0 ;
$this -> ref = 'PRODUCT_SPEC' ;
$this -> label = 'PRODUCT SPECIMEN' ;
$this -> description = 'This is description of this product specimen that was created the ' . dol_print_date ( $now , 'dayhourlog' ) . '.' ;
$this -> specimen = 1 ;
$this -> country_id = 1 ;
$this -> tosell = 1 ;
$this -> tobuy = 1 ;
$this -> tobatch = 0 ;
$this -> note = 'This is a comment (private)' ;
$this -> date_creation = $now ;
$this -> date_modification = $now ;
$this -> weight = 4 ;
2020-12-15 11:39:18 +01:00
$this -> weight_units = 3 ;
2020-09-07 10:18:17 +02:00
$this -> length = 5 ;
2020-12-15 11:39:18 +01:00
$this -> length_units = 1 ;
2020-09-07 10:18:17 +02:00
$this -> width = 6 ;
2020-12-15 11:39:18 +01:00
$this -> width_units = 0 ;
2020-09-07 10:18:17 +02:00
$this -> height = null ;
2020-12-15 11:39:18 +01:00
$this -> height_units = null ;
2020-09-07 10:18:17 +02:00
$this -> surface = 30 ;
2020-12-15 11:39:18 +01:00
$this -> surface_units = 0 ;
2020-09-07 10:18:17 +02:00
$this -> volume = 300 ;
2020-12-15 11:39:18 +01:00
$this -> volume_units = 0 ;
2020-09-07 10:18:17 +02:00
$this -> barcode = - 1 ; // Create barcode automatically
}
2020-09-14 10:59:09 +02:00
/**
* Returns the text label from units dictionary
*
* @ param string $type Label type ( long or short )
* @ return string | int < 0 if ko , label if ok
*/
public function getLabelOfUnit ( $type = 'long' )
{
global $langs ;
2020-09-07 10:18:17 +02:00
if ( ! $this -> fk_unit ) {
return '' ;
}
$langs -> load ( 'products' );
2020-09-14 10:59:09 +02:00
$label_type = 'label' ;
if ( $type == 'short' ) {
$label_type = 'short_label' ;
}
2022-01-27 10:12:10 +01:00
$sql = " SELECT " . $label_type . " , code from " . $this -> db -> prefix () . " c_units where rowid = " . (( int ) $this -> fk_unit );
2021-08-28 03:09:18 +02:00
2020-09-14 10:59:09 +02:00
$resql = $this -> db -> query ( $sql );
if ( $resql && $this -> db -> num_rows ( $resql ) > 0 ) {
$res = $this -> db -> fetch_array ( $resql );
$label = ( $label_type == 'short_label' ? $res [ $label_type ] : 'unit' . $res [ 'code' ]);
$this -> db -> free ( $resql );
return $label ;
} else {
2021-08-28 03:09:18 +02:00
$this -> error = $this -> db -> error ();
2020-09-14 10:59:09 +02:00
dol_syslog ( get_class ( $this ) . " ::getLabelOfUnit Error " . $this -> error , LOG_ERR );
return - 1 ;
}
}
2020-09-07 10:18:17 +02:00
/**
* Return if object has a sell - by date or eat - by date
*
* @ return boolean True if it ' s has
*/
public function hasbatch ()
{
2021-02-25 15:35:45 +01:00
return ( $this -> status_batch > 0 ? true : false );
2020-09-07 10:18:17 +02:00
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Return minimum product recommended price
*
* @ return int Minimum recommanded price that is higher price among all suppliers * PRODUCT_MINIMUM_RECOMMENDED_PRICE
*/
public function min_recommended_price ()
{
// phpcs:enable
global $conf ;
$maxpricesupplier = 0 ;
if ( ! empty ( $conf -> global -> PRODUCT_MINIMUM_RECOMMENDED_PRICE )) {
include_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.product.class.php' ;
$product_fourn = new ProductFournisseur ( $this -> db );
$product_fourn_list = $product_fourn -> list_product_fournisseur_price ( $this -> id , '' , '' );
if ( is_array ( $product_fourn_list ) && count ( $product_fourn_list ) > 0 ) {
2021-02-26 14:25:17 +01:00
foreach ( $product_fourn_list as $productfourn ) {
2020-09-07 10:18:17 +02:00
if ( $productfourn -> fourn_unitprice > $maxpricesupplier ) {
$maxpricesupplier = $productfourn -> fourn_unitprice ;
}
}
$maxpricesupplier *= $conf -> global -> PRODUCT_MINIMUM_RECOMMENDED_PRICE ;
}
}
return $maxpricesupplier ;
}
/**
* Sets object to supplied categories .
*
* Deletes object from existing categories not supplied .
* Adds it to non existing supplied categories .
* Existing categories are left untouch .
*
* @ param int [] | int $categories Category or categories IDs
* @ return void
*/
public function setCategories ( $categories )
{
2020-12-23 23:32:50 +01:00
require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php' ;
return parent :: setCategoriesCommon ( $categories , Categorie :: TYPE_PRODUCT );
2020-09-07 10:18:17 +02:00
}
/**
* Function used to replace a thirdparty id with another one .
*
* @ param DoliDB $db Database handler
* @ param int $origin_id Old thirdparty id
* @ param int $dest_id New thirdparty id
* @ return bool
*/
public static function replaceThirdparty ( DoliDB $db , $origin_id , $dest_id )
{
$tables = array (
2021-06-16 20:17:21 +02:00
'product_customer_price' ,
'product_customer_price_log'
2020-09-07 10:18:17 +02:00
);
return CommonObject :: commonReplaceThirdparty ( $db , $origin_id , $dest_id , $tables );
}
/**
* Generates prices for a product based on product multiprice generation rules
*
* @ param User $user User that updates the prices
* @ param float $baseprice Base price
* @ param string $price_type Base price type
* @ param float $price_vat VAT % tax
* @ param int $npr NPR
* @ param string $psq ¿ ?
* @ return int - 1 KO , 1 OK
*/
public function generateMultiprices ( User $user , $baseprice , $price_type , $price_vat , $npr , $psq )
{
global $conf , $db ;
2022-01-27 10:12:10 +01:00
$sql = " SELECT rowid, level, fk_level, var_percent, var_min_percent FROM " . $this -> db -> prefix () . " product_pricerules " ;
2020-09-20 02:57:15 +02:00
$query = $this -> db -> query ( $sql );
2020-09-07 10:18:17 +02:00
$rules = array ();
2020-09-20 02:57:15 +02:00
while ( $result = $this -> db -> fetch_object ( $query )) {
2020-09-07 10:18:17 +02:00
$rules [ $result -> level ] = $result ;
}
//Because prices can be based on other level's prices, we temporarily store them
$prices = array (
2020-09-20 02:57:15 +02:00
1 => $baseprice
2020-09-07 10:18:17 +02:00
);
for ( $i = 1 ; $i <= $conf -> global -> PRODUIT_MULTIPRICES_LIMIT ; $i ++ ) {
$price = $baseprice ;
$price_min = $baseprice ;
//We have to make sure it does exist and it is > 0
//First price level only allows changing min_price
if ( $i > 1 && isset ( $rules [ $i ] -> var_percent ) && $rules [ $i ] -> var_percent ) {
$price = $prices [ $rules [ $i ] -> fk_level ] * ( 1 + ( $rules [ $i ] -> var_percent / 100 ));
}
$prices [ $i ] = $price ;
//We have to make sure it does exist and it is > 0
if ( isset ( $rules [ $i ] -> var_min_percent ) && $rules [ $i ] -> var_min_percent ) {
$price_min = $price * ( 1 - ( $rules [ $i ] -> var_min_percent / 100 ));
}
//Little check to make sure the price is modified before triggering generation
$check_amount = (( $price == $this -> multiprices [ $i ]) && ( $price_min == $this -> multiprices_min [ $i ]));
$check_type = ( $baseprice == $this -> multiprices_base_type [ $i ]);
if ( $check_amount && $check_type ) {
continue ;
}
if ( $this -> updatePrice ( $price , $price_type , $user , $price_vat , $price_min , $i , $npr , $psq , true ) < 0 ) {
return - 1 ;
}
}
return 1 ;
}
/**
* Returns the rights used for this class
*
* @ return Object
*/
public function getRights ()
{
global $user ;
if ( $this -> isProduct ()) {
return $user -> rights -> produit ;
} else {
return $user -> rights -> service ;
}
}
/**
* Load information for tab info
*
* @ param int $id Id of thirdparty to load
* @ return void
*/
public function info ( $id )
{
$sql = " SELECT p.rowid, p.ref, p.datec as date_creation, p.tms as date_modification, " ;
$sql .= " p.fk_user_author, p.fk_user_modif " ;
2022-01-27 10:12:10 +01:00
$sql .= " FROM " . $this -> db -> prefix () . $this -> table_element . " as p " ;
2021-03-14 11:48:39 +01:00
$sql .= " WHERE p.rowid = " . (( int ) $id );
2020-09-07 10:18:17 +02:00
$result = $this -> db -> query ( $sql );
if ( $result ) {
if ( $this -> db -> num_rows ( $result )) {
$obj = $this -> db -> fetch_object ( $result );
$this -> id = $obj -> rowid ;
if ( $obj -> fk_user_author ) {
$cuser = new User ( $this -> db );
$cuser -> fetch ( $obj -> fk_user_author );
$this -> user_creation = $cuser ;
}
if ( $obj -> fk_user_modif ) {
$muser = new User ( $this -> db );
$muser -> fetch ( $obj -> fk_user_modif );
$this -> user_modification = $muser ;
}
$this -> ref = $obj -> ref ;
$this -> date_creation = $this -> db -> jdate ( $obj -> date_creation );
$this -> date_modification = $this -> db -> jdate ( $obj -> date_modification );
}
$this -> db -> free ( $result );
} else {
dol_print_error ( $this -> db );
}
}
2002-04-30 17:52:56 +02:00
}
2020-04-24 23:56:57 +02:00
/**
2020-05-02 03:29:33 +02:00
* Class to manage products or services .
* Do not use 'Service' as class name since it is already used by APIs .
2020-04-24 23:56:57 +02:00
*/
2020-05-02 03:29:33 +02:00
class ProductService extends Product
2020-04-24 23:56:57 +02:00
{
public $picto = 'service' ;
}