2016-07-23 16:37:21 +02:00
< ? php
2024-04-20 23:45:52 +02:00
/* Copyright ( C ) 2016 Marcos García < marcosgdf @ gmail . com >
* Copyright ( C ) 2018 Juanjo Menent < jmenent @ 2 byte . es >
* Copyright ( C ) 2022 Open - Dsi < support @ open - dsi . fr >
2025-01-20 16:23:29 +01:00
* Copyright ( C ) 2024 - 2025 MDW < mdeweerd @ users . noreply . github . com >
2024-04-20 23:45:52 +02:00
* Copyright ( C ) 2024 Frédéric France < frederic . france @ free . fr >
2016-07-23 16:37:21 +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
* the Free Software Foundation ; either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
2019-09-23 21:55:30 +02:00
* along with this program . If not , see < https :// www . gnu . org / licenses />.
2016-07-23 16:37:21 +02:00
*/
2024-04-27 04:20:26 +02:00
/**
* \file htdocs / variants / class / ProductCombination . class . php
* \ingroup variants
* \brief File of the ProductCombination class
*/
2016-07-25 10:37:39 +02:00
/**
* Class ProductCombination
2024-04-27 04:20:26 +02:00
* Used to represent the relation between a product and one of its variants .
*
* Example : a product " shirt " has two variants " shirt XL white " and " shirt XL grey " .
* This is represented with two ProductCombination objects :
* - One for " shirt XL white " :
* * $object -> fk_product_parent ID of the Product object " shirt "
* * $object -> fk_product_child ID of the Product object " shirt XL white "
* - Another for " shirt XL grey " :
* * $object -> fk_product_parent ID of the Product object " shirt "
* * $object -> fk_product_child ID of the Product object " shirt XL grey "
2016-07-25 10:37:39 +02:00
*/
2016-07-23 16:37:21 +02:00
class ProductCombination
{
/**
* Database handler
* @ var DoliDB
*/
2020-09-19 23:30:29 +02:00
public $db ;
2016-07-23 16:37:21 +02:00
/**
2024-04-27 04:20:26 +02:00
* Rowid of this ProductCombination
2016-07-23 16:37:21 +02:00
* @ var int
*/
public $id ;
/**
2024-04-27 04:20:26 +02:00
* Rowid of the parent Product
2016-07-23 16:37:21 +02:00
* @ var int
*/
public $fk_product_parent ;
/**
2024-04-27 04:20:26 +02:00
* Rowid of the variant Product
2016-07-23 16:37:21 +02:00
* @ var int
*/
public $fk_product_child ;
/**
* Price variation
* @ var float
*/
public $variation_price ;
/**
2024-04-27 04:20:26 +02:00
* Is the price variation a relative variation ?
* Can be an array if multiprice feature per level is enabled .
2020-09-07 02:49:39 +02:00
* @ var bool | array
2016-07-23 16:37:21 +02:00
*/
public $variation_price_percentage = false ;
/**
* Weight variation
* @ var float
*/
public $variation_weight ;
/**
* Combination entity
* @ var int
*/
public $entity ;
2020-08-12 10:14:29 +02:00
/**
* Combination price level
* @ var ProductCombinationLevel []
*/
public $combination_price_levels ;
2020-09-04 11:01:47 +02:00
/**
2020-10-31 14:32:18 +01:00
* External ref
* @ var string
*/
public $variation_ref_ext = '' ;
2020-08-12 10:14:29 +02:00
2023-02-20 20:43:23 +01:00
/**
2024-04-27 04:20:26 +02:00
* Error message
* @ var string
2023-02-20 20:43:23 +01:00
*/
public $error ;
/**
2024-04-27 04:20:26 +02:00
* Array of error messages
* @ var string []
2023-02-20 20:43:23 +01:00
*/
public $errors = array ();
2020-05-23 13:53:47 +02:00
/**
* Constructor
*
* @ param DoliDB $db Database handler
*/
public function __construct ( DoliDB $db )
2016-07-23 16:37:21 +02:00
{
global $conf ;
$this -> db = $db ;
$this -> entity = $conf -> entity ;
}
/**
2024-04-27 04:20:26 +02:00
* Retrieves a ProductCombination by its rowid
2016-07-23 16:37:21 +02:00
*
2024-07-31 13:09:10 +02:00
* @ param int $rowid ID of the ProductCombination
* @ return int <- 1 , 1 > - 1 if KO , 1 if OK
2016-07-23 16:37:21 +02:00
*/
public function fetch ( $rowid )
{
2021-03-22 11:30:18 +01:00
$sql = " SELECT rowid, fk_product_parent, fk_product_child, variation_price, variation_price_percentage, variation_weight, variation_ref_ext FROM " . MAIN_DB_PREFIX . " product_attribute_combination WHERE rowid = " . (( int ) $rowid ) . " AND entity IN ( " . getEntity ( 'product' ) . " ) " ;
2016-07-23 16:37:21 +02:00
$query = $this -> db -> query ( $sql );
if ( ! $query ) {
return - 1 ;
}
if ( ! $this -> db -> num_rows ( $query )) {
return - 1 ;
}
2019-11-12 10:11:30 +01:00
$obj = $this -> db -> fetch_object ( $query );
$this -> id = $obj -> rowid ;
$this -> fk_product_parent = $obj -> fk_product_parent ;
$this -> fk_product_child = $obj -> fk_product_child ;
$this -> variation_price = $obj -> variation_price ;
$this -> variation_price_percentage = $obj -> variation_price_percentage ;
$this -> variation_weight = $obj -> variation_weight ;
2020-10-31 14:32:18 +01:00
$this -> variation_ref_ext = $obj -> variation_ref_ext ;
2016-07-23 16:37:21 +02:00
2023-11-27 12:08:48 +01:00
if ( getDolGlobalString ( 'PRODUIT_MULTIPRICES' )) {
2020-08-12 10:14:29 +02:00
$this -> fetchCombinationPriceLevels ();
}
2016-07-23 16:37:21 +02:00
return 1 ;
}
2020-08-12 10:14:29 +02:00
/**
* Retrieves combination price levels
*
2024-10-14 21:38:12 +02:00
* @ param int $fk_price_level The price level to fetch , use 0 for all
* @ param bool $useCache To use cache or not
* @ return int <- 1 , 1 > - 1 if KO , 1 if OK
2020-08-12 10:14:29 +02:00
*/
public function fetchCombinationPriceLevels ( $fk_price_level = 0 , $useCache = true )
{
// Check cache
2020-09-07 10:18:17 +02:00
if ( ! empty ( $this -> combination_price_levels ) && $useCache ) {
if (( ! empty ( $fk_price_level ) && isset ( $this -> combination_price_levels [ $fk_price_level ])) || empty ( $fk_price_level )) {
2020-08-12 10:14:29 +02:00
return 1 ;
}
}
2020-08-13 11:48:28 +02:00
if ( ! is_array ( $this -> combination_price_levels )
2024-01-12 17:18:52 +01:00
|| empty ( $fk_price_level ) // if fetch an unique level don't erase all already fetched
2020-09-07 10:18:17 +02:00
) {
2020-08-12 10:14:29 +02:00
$this -> combination_price_levels = array ();
}
$staticProductCombinationLevel = new ProductCombinationLevel ( $this -> db );
$combination_price_levels = $staticProductCombinationLevel -> fetchAll ( $this -> id , $fk_price_level );
2020-08-13 11:43:33 +02:00
if ( ! is_array ( $combination_price_levels )) {
2020-08-12 10:14:29 +02:00
return - 1 ;
}
2020-09-07 10:18:17 +02:00
if ( empty ( $combination_price_levels )) {
2020-08-13 11:43:33 +02:00
/**
* for auto retrocompatibility with last behavior
*/
2020-09-08 21:27:28 +02:00
if ( $fk_price_level > 0 ) {
2020-09-02 16:24:22 +02:00
$combination_price_levels [ $fk_price_level ] = ProductCombinationLevel :: createFromParent ( $this -> db , $this , $fk_price_level );
2021-02-26 13:10:10 +01:00
} else {
2024-06-22 15:40:26 +02:00
$produit_multiprices_limit = getDolGlobalString ( 'PRODUIT_MULTIPRICES_LIMIT' );
for ( $i = 1 ; $i <= $produit_multiprices_limit ; $i ++ ) {
2020-09-02 16:24:22 +02:00
$combination_price_levels [ $i ] = ProductCombinationLevel :: createFromParent ( $this -> db , $this , $i );
2020-08-13 11:43:33 +02:00
}
}
}
2020-08-12 10:14:29 +02:00
$this -> combination_price_levels = $combination_price_levels ;
return 1 ;
}
/**
* Retrieves combination price levels
*
2020-08-17 13:55:18 +02:00
* @ param int $clean Levels of PRODUIT_MULTIPRICES_LIMIT
2023-12-06 15:46:39 +01:00
* @ return int Return integer < 0 KO , > 0 OK
2020-08-12 10:14:29 +02:00
*/
public function saveCombinationPriceLevels ( $clean = 1 )
{
global $conf ;
2020-08-17 05:54:22 +02:00
$error = 0 ;
2020-08-12 10:14:29 +02:00
$staticProductCombinationLevel = new ProductCombinationLevel ( $this -> db );
// Delete all
2020-08-17 13:55:18 +02:00
if ( empty ( $this -> combination_price_levels )) {
2020-08-12 10:14:29 +02:00
return $staticProductCombinationLevel -> deleteAllForCombination ( $this -> id );
}
2020-08-17 13:55:18 +02:00
// Clean not needed price levels (level higher than number max defined into setup)
if ( $clean ) {
2020-08-12 10:14:29 +02:00
$res = $staticProductCombinationLevel -> clean ( $this -> id );
2020-08-17 13:55:18 +02:00
if ( $res < 0 ) {
2020-08-12 10:14:29 +02:00
$this -> errors [] = 'Fail to clean not needed price levels' ;
return - 1 ;
}
}
2020-08-17 13:55:18 +02:00
foreach ( $this -> combination_price_levels as $fk_price_level => $combination_price_level ) {
2020-08-12 10:14:29 +02:00
$res = $combination_price_level -> save ();
2020-08-17 13:55:18 +02:00
if ( $res < 1 ) {
2020-09-07 10:18:17 +02:00
$this -> error = 'Error saving combination price level ' . $fk_price_level . ' : ' . $combination_price_level -> error ;
2020-08-12 10:14:29 +02:00
$this -> errors [] = $this -> error ;
2020-08-17 05:54:22 +02:00
$error ++ ;
2020-08-17 13:55:18 +02:00
break ;
2020-08-12 10:14:29 +02:00
}
}
2020-09-07 10:18:17 +02:00
if ( $error ) {
2020-08-17 05:54:22 +02:00
return $error * - 1 ;
2021-02-26 13:10:10 +01:00
} else {
2020-08-12 10:14:29 +02:00
return 1 ;
}
}
2016-07-23 16:37:21 +02:00
/**
2020-09-04 14:05:35 +02:00
* Retrieves information of a variant product and ID of its parent product .
2016-07-23 16:37:21 +02:00
*
2020-09-04 14:05:35 +02:00
* @ param int $productid Product ID of variant
* @ param int $donotloadpricelevel Avoid loading price impact for each level . If PRODUIT_MULTIPRICES is not set , this has no effect .
2023-12-06 15:46:39 +01:00
* @ return int Return integer < 0 if KO , 0 if product ID is not ID of a variant product ( so parent not found ), > 0 if OK ( ID of parent )
2016-07-23 16:37:21 +02:00
*/
2020-09-04 14:05:35 +02:00
public function fetchByFkProductChild ( $productid , $donotloadpricelevel = 0 )
2016-07-23 16:37:21 +02:00
{
2020-08-12 10:14:29 +02:00
global $conf ;
2020-09-04 14:05:35 +02:00
$sql = " SELECT rowid, fk_product_parent, fk_product_child, variation_price, variation_price_percentage, variation_weight " ;
$sql .= " FROM " . MAIN_DB_PREFIX . " product_attribute_combination WHERE fk_product_child = " . (( int ) $productid ) . " AND entity IN ( " . getEntity ( 'product' ) . " ) " ;
2016-07-23 16:37:21 +02:00
$query = $this -> db -> query ( $sql );
if ( ! $query ) {
return - 1 ;
}
if ( ! $this -> db -> num_rows ( $query )) {
2020-09-04 14:05:35 +02:00
return 0 ;
2016-07-23 16:37:21 +02:00
}
$result = $this -> db -> fetch_object ( $query );
$this -> id = $result -> rowid ;
$this -> fk_product_parent = $result -> fk_product_parent ;
$this -> fk_product_child = $result -> fk_product_child ;
$this -> variation_price = $result -> variation_price ;
$this -> variation_price_percentage = $result -> variation_price_percentage ;
$this -> variation_weight = $result -> variation_weight ;
2023-11-27 12:08:48 +01:00
if ( empty ( $donotloadpricelevel ) && getDolGlobalString ( 'PRODUIT_MULTIPRICES' )) {
2020-08-12 10:14:29 +02:00
$this -> fetchCombinationPriceLevels ();
}
2020-09-12 15:53:18 +02:00
return ( int ) $this -> fk_product_parent ;
2016-07-23 16:37:21 +02:00
}
/**
* Retrieves all product combinations by the product parent row id
*
2024-02-02 17:45:30 +01:00
* @ param int $fk_product_parent Rowid of parent product
* @ param bool $sort_by_ref Sort result by product child reference
* @ return int | ProductCombination [] Return integer < 0 KO
2016-07-23 16:37:21 +02:00
*/
2024-02-02 17:45:30 +01:00
public function fetchAllByFkProductParent ( $fk_product_parent , $sort_by_ref = false )
2016-07-23 16:37:21 +02:00
{
2020-08-12 10:14:29 +02:00
global $conf ;
2024-02-02 17:45:30 +01:00
$sql = " SELECT pac.rowid, pac.fk_product_parent, pac.fk_product_child, pac.variation_price, pac.variation_price_percentage, pac.variation_ref_ext, pac.variation_weight " ;
2024-03-14 21:59:15 +01:00
$sql .= " FROM " . MAIN_DB_PREFIX . " product_attribute_combination AS pac " ;
2024-02-02 17:45:30 +01:00
if ( $sort_by_ref ) {
2024-03-14 21:59:15 +01:00
$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . " product AS p ON p.rowid = pac.fk_product_child " ;
2024-02-02 17:45:30 +01:00
}
2024-03-14 21:59:15 +01:00
$sql .= " WHERE pac.fk_product_parent = " . (( int ) $fk_product_parent ) . " AND pac.entity IN ( " . getEntity ( 'product' ) . " ) " ;
2024-02-02 17:45:30 +01:00
if ( $sort_by_ref ) {
2024-03-14 21:59:15 +01:00
$sql .= $this -> db -> order ( 'p.ref' , 'ASC' );
2024-02-02 17:45:30 +01:00
}
2016-07-23 16:37:21 +02:00
$query = $this -> db -> query ( $sql );
if ( ! $query ) {
return - 1 ;
}
$return = array ();
while ( $result = $this -> db -> fetch_object ( $query )) {
$tmp = new ProductCombination ( $this -> db );
$tmp -> id = $result -> rowid ;
$tmp -> fk_product_parent = $result -> fk_product_parent ;
$tmp -> fk_product_child = $result -> fk_product_child ;
$tmp -> variation_price = $result -> variation_price ;
$tmp -> variation_price_percentage = $result -> variation_price_percentage ;
$tmp -> variation_weight = $result -> variation_weight ;
2020-10-31 14:32:18 +01:00
$tmp -> variation_ref_ext = $result -> variation_ref_ext ;
2016-07-23 16:37:21 +02:00
2023-11-27 12:08:48 +01:00
if ( getDolGlobalString ( 'PRODUIT_MULTIPRICES' )) {
2020-08-12 10:14:29 +02:00
$tmp -> fetchCombinationPriceLevels ();
}
2016-07-23 16:37:21 +02:00
$return [] = $tmp ;
}
return $return ;
}
2017-02-08 14:07:54 +01:00
/**
* Retrieves all product combinations by the product parent row id
*
* @ param int $fk_product_parent Id of parent product
* @ return int Nb of record
*/
public function countNbOfCombinationForFkProductParent ( $fk_product_parent )
{
2020-05-23 13:53:47 +02:00
$nb = 0 ;
2021-03-22 11:30:18 +01:00
$sql = " SELECT count(rowid) as nb FROM " . MAIN_DB_PREFIX . " product_attribute_combination WHERE fk_product_parent = " . (( int ) $fk_product_parent ) . " AND entity IN ( " . getEntity ( 'product' ) . " ) " ;
2017-07-28 09:45:23 +02:00
2020-05-23 13:53:47 +02:00
$resql = $this -> db -> query ( $sql );
if ( $resql ) {
$obj = $this -> db -> fetch_object ( $resql );
2021-02-26 13:10:10 +01:00
if ( $obj ) {
$nb = $obj -> nb ;
}
2020-05-23 13:53:47 +02:00
}
2017-02-08 14:07:54 +01:00
2020-05-23 13:53:47 +02:00
return $nb ;
2017-02-08 14:07:54 +01:00
}
2017-07-28 09:45:23 +02:00
2016-07-23 16:37:21 +02:00
/**
* Creates a product attribute combination
*
2017-11-26 18:43:43 +01:00
* @ param User $user Object user
2023-12-01 19:51:32 +01:00
* @ return int Return integer < 0 if KO , > 0 if OK
2016-07-23 16:37:21 +02:00
*/
2017-11-26 18:43:43 +01:00
public function create ( $user )
2016-07-23 16:37:21 +02:00
{
2020-08-12 10:14:29 +02:00
global $conf ;
2020-08-17 13:55:18 +02:00
/* $this->fk_product_child may be empty and will be filled later after subproduct has been created */
2016-07-23 16:37:21 +02:00
2020-08-17 13:55:18 +02:00
$sql = " INSERT INTO " . MAIN_DB_PREFIX . " product_attribute_combination " ;
2020-09-10 13:56:58 +02:00
$sql .= " (fk_product_parent, fk_product_child, variation_price, variation_price_percentage, variation_weight, variation_ref_ext, entity) " ;
2020-08-17 13:55:18 +02:00
$sql .= " VALUES ( " . (( int ) $this -> fk_product_parent ) . " , " . (( int ) $this -> fk_product_child ) . " , " ;
$sql .= ( float ) $this -> variation_price . " , " . ( int ) $this -> variation_price_percentage . " , " ;
2020-09-04 11:01:47 +02:00
$sql .= ( float ) $this -> variation_weight . " , ' " . $this -> db -> escape ( $this -> variation_ref_ext ) . " ', " . ( int ) $this -> entity . " ) " ;
2016-07-23 16:37:21 +02:00
2020-08-17 13:55:18 +02:00
$resql = $this -> db -> query ( $sql );
if ( $resql ) {
$this -> id = $this -> db -> last_insert_id ( MAIN_DB_PREFIX . 'product_attribute_combination' );
} else {
$this -> error = $this -> db -> lasterror ();
2016-07-23 16:37:21 +02:00
return - 1 ;
}
2023-11-27 12:08:48 +01:00
if ( getDolGlobalString ( 'PRODUIT_MULTIPRICES' )) {
2020-08-12 10:14:29 +02:00
$res = $this -> saveCombinationPriceLevels ();
2020-08-17 13:55:18 +02:00
if ( $res < 0 ) {
2020-08-12 10:14:29 +02:00
return - 2 ;
}
}
2016-07-23 16:37:21 +02:00
return 1 ;
}
/**
* Updates a product combination
*
2017-11-26 18:43:43 +01:00
* @ param User $user Object user
2023-12-06 15:46:39 +01:00
* @ return int Return integer < 0 KO , > 0 OK
2016-07-23 16:37:21 +02:00
*/
2017-11-26 18:43:43 +01:00
public function update ( User $user )
2016-07-23 16:37:21 +02:00
{
2020-08-12 10:14:29 +02:00
global $conf ;
2020-08-17 13:55:18 +02:00
$sql = " UPDATE " . MAIN_DB_PREFIX . " product_attribute_combination " ;
$sql .= " SET fk_product_parent = " . ( int ) $this -> fk_product_parent . " , fk_product_child = " . ( int ) $this -> fk_product_child . " , " ;
$sql .= " variation_price = " . ( float ) $this -> variation_price . " , variation_price_percentage = " . ( int ) $this -> variation_price_percentage . " , " ;
2020-09-04 11:01:47 +02:00
$sql .= " variation_ref_ext = ' " . $this -> db -> escape ( $this -> variation_ref_ext ) . " ', " ;
2020-10-31 14:32:18 +01:00
$sql .= " variation_weight = " . ( float ) $this -> variation_weight . " WHERE rowid = " . (( int ) $this -> id );
2016-07-23 16:37:21 +02:00
2020-08-17 13:55:18 +02:00
$resql = $this -> db -> query ( $sql );
2020-09-07 10:18:17 +02:00
if ( ! $resql ) {
2016-07-23 16:37:21 +02:00
return - 1 ;
}
2023-11-27 12:08:48 +01:00
if ( getDolGlobalString ( 'PRODUIT_MULTIPRICES' )) {
2020-08-12 10:14:29 +02:00
$res = $this -> saveCombinationPriceLevels ();
2020-08-17 13:55:18 +02:00
if ( $res < 0 ) {
2020-08-12 10:14:29 +02:00
return - 2 ;
}
}
2016-07-23 16:37:21 +02:00
$parent = new Product ( $this -> db );
$parent -> fetch ( $this -> fk_product_parent );
2019-11-09 14:13:14 +01:00
$this -> updateProperties ( $parent , $user );
2016-07-23 16:37:21 +02:00
return 1 ;
}
/**
* Deletes a product combination
*
2017-08-01 01:28:07 +02:00
* @ param User $user Object user
2023-12-01 19:51:32 +01:00
* @ return int Return integer < 0 if KO , > 0 if OK
2016-07-23 16:37:21 +02:00
*/
2017-07-28 09:45:23 +02:00
public function delete ( User $user )
2016-07-23 16:37:21 +02:00
{
$this -> db -> begin ();
$comb2val = new ProductCombination2ValuePair ( $this -> db );
$comb2val -> deleteByFkCombination ( $this -> id );
2020-08-12 10:14:29 +02:00
// remove combination price levels
2020-08-13 11:48:28 +02:00
if ( ! $this -> db -> query ( " DELETE FROM " . MAIN_DB_PREFIX . " product_attribute_combination_price_level WHERE fk_product_attribute_combination = " . ( int ) $this -> id )) {
2020-08-12 10:14:29 +02:00
$this -> db -> rollback ();
return - 1 ;
}
2016-07-23 16:37:21 +02:00
$sql = " DELETE FROM " . MAIN_DB_PREFIX . " product_attribute_combination WHERE rowid = " . ( int ) $this -> id ;
if ( $this -> db -> query ( $sql )) {
$this -> db -> commit ();
return 1 ;
}
$this -> db -> rollback ();
return - 1 ;
}
/**
* Deletes all product combinations of a parent product
*
2018-05-02 22:33:23 +02:00
* @ param User $user Object user
* @ param int $fk_product_parent Rowid of parent product
2023-12-06 15:46:39 +01:00
* @ return int Return integer < 0 KO > 0 OK
2016-07-23 16:37:21 +02:00
*/
2018-05-02 22:33:23 +02:00
public function deleteByFkProductParent ( $user , $fk_product_parent )
2016-07-23 16:37:21 +02:00
{
$this -> db -> begin ();
foreach ( $this -> fetchAllByFkProductParent ( $fk_product_parent ) as $prodcomb ) {
$prodstatic = new Product ( $this -> db );
$res = $prodstatic -> fetch ( $prodcomb -> fk_product_child );
if ( $res > 0 ) {
2018-05-02 22:33:23 +02:00
$res = $prodcomb -> delete ( $user );
2016-07-23 16:37:21 +02:00
}
if ( $res > 0 && ! $prodstatic -> isObjectUsed ( $prodstatic -> id )) {
2018-05-02 22:33:23 +02:00
$res = $prodstatic -> delete ( $user );
2016-07-23 16:37:21 +02:00
}
if ( $res < 0 ) {
$this -> db -> rollback ();
return - 1 ;
}
}
$this -> db -> commit ();
return 1 ;
}
/**
2021-02-26 13:22:13 +01:00
* Updates the weight of the child product . The price must be updated using Product :: updatePrices .
* This method is called by the update () of a product .
2016-07-23 16:37:21 +02:00
*
2021-02-26 13:22:13 +01:00
* @ param Product $parent Parent product
2019-11-09 14:13:14 +01:00
* @ param User $user Object user
2021-02-26 13:22:13 +01:00
* @ return int > 0 if OK , < 0 if KO
2016-07-23 16:37:21 +02:00
*/
2019-11-09 14:13:14 +01:00
public function updateProperties ( Product $parent , User $user )
2016-07-23 16:37:21 +02:00
{
2019-11-09 14:13:14 +01:00
global $conf ;
2016-07-23 16:37:21 +02:00
$this -> db -> begin ();
$child = new Product ( $this -> db );
$child -> fetch ( $this -> fk_product_child );
2020-05-23 13:53:47 +02:00
2016-07-23 16:37:21 +02:00
$child -> price_autogen = $parent -> price_autogen ;
2020-05-23 13:46:37 +02:00
$child -> weight = $parent -> weight ;
2021-11-02 14:16:55 +01:00
// Only when Parent Status are updated
2024-06-13 08:11:11 +02:00
if ( is_object ( $parent -> oldcopy ) && ! $parent -> oldcopy -> isEmpty () && ( $parent -> status != $parent -> oldcopy -> status )) {
2021-11-02 14:20:41 +01:00
$child -> status = $parent -> status ;
}
2024-06-13 08:11:11 +02:00
if ( is_object ( $parent -> oldcopy ) && ! $parent -> oldcopy -> isEmpty () && ( $parent -> status_buy != $parent -> oldcopy -> status_buy )) {
2021-11-02 14:20:41 +01:00
$child -> status_buy = $parent -> status_buy ;
}
2020-05-23 13:46:37 +02:00
if ( $this -> variation_weight ) { // If we must add a delta on weight
$child -> weight = ( $child -> weight ? $child -> weight : 0 ) + $this -> variation_weight ;
}
2020-09-07 10:18:17 +02:00
$child -> weight_units = $parent -> weight_units ;
2020-05-23 13:53:47 +02:00
// Don't update the child label if the user has already modified it.
if ( $child -> label == $parent -> label ) {
// This will trigger only at variant creation time
$varlabel = $this -> getCombinationLabel ( $this -> fk_product_child );
2022-12-28 16:20:35 +01:00
$child -> label = $parent -> label . $varlabel ;
2020-05-23 13:53:47 +02:00
}
2016-07-23 16:37:21 +02:00
2020-08-12 10:14:29 +02:00
2016-07-23 16:37:21 +02:00
if ( $child -> update ( $child -> id , $user ) > 0 ) {
$new_vat = $parent -> tva_tx ;
$new_npr = $parent -> tva_npr ;
// MultiPrix
2023-11-27 12:08:48 +01:00
if ( getDolGlobalString ( 'PRODUIT_MULTIPRICES' )) {
2024-12-17 16:59:13 +01:00
$produit_multiprices_limit = getDolGlobalInt ( 'PRODUIT_MULTIPRICES_LIMIT' );
2024-06-22 15:40:26 +02:00
for ( $i = 1 ; $i <= $produit_multiprices_limit ; $i ++ ) {
2020-08-12 10:14:29 +02:00
if ( $parent -> multiprices [ $i ] != '' || isset ( $this -> combination_price_levels [ $i ] -> variation_price )) {
2021-10-31 17:42:43 +01:00
$new_type = empty ( $parent -> multiprices_base_type [ $i ]) ? 'HT' : $parent -> multiprices_base_type [ $i ];
2018-10-19 12:02:14 +02:00
$new_min_price = $parent -> multiprices_min [ $i ];
2023-12-27 12:12:20 +01:00
$variation_price = ( float ) ( ! isset ( $this -> combination_price_levels [ $i ] -> variation_price ) ? $this -> variation_price : $this -> combination_price_levels [ $i ] -> variation_price );
2024-12-09 17:41:56 +01:00
$variation_price_percentage = ( bool ) ( ! isset ( $this -> combination_price_levels [ $i ] -> variation_price_percentage ) ? $this -> variation_price_percentage : $this -> combination_price_levels [ $i ] -> variation_price_percentage );
2020-08-12 10:14:29 +02:00
2018-10-19 12:02:14 +02:00
if ( $parent -> prices_by_qty_list [ $i ]) {
$new_psq = 1 ;
} else {
$new_psq = 0 ;
}
if ( $new_type == 'TTC' ) {
$new_price = $parent -> multiprices_ttc [ $i ];
} else {
$new_price = $parent -> multiprices [ $i ];
}
2020-08-12 10:14:29 +02:00
if ( $variation_price_percentage ) {
2018-10-19 12:02:14 +02:00
if ( $new_price != 0 ) {
2020-08-12 10:14:29 +02:00
$new_price *= 1 + ( $variation_price / 100 );
2018-10-19 12:02:14 +02:00
}
} else {
2020-08-12 10:14:29 +02:00
$new_price += $variation_price ;
2018-10-19 12:02:14 +02:00
}
2022-03-08 14:49:26 +01:00
$ret = $child -> updatePrice ( $new_price , $new_type , $user , $new_vat , $new_min_price , $i , $new_npr , $new_psq , 0 , array (), $parent -> default_vat_code );
if ( $ret < 0 ) {
$this -> db -> rollback ();
$this -> error = $child -> error ;
$this -> errors = $child -> errors ;
return $ret ;
}
2018-10-19 12:02:14 +02:00
}
2016-07-23 16:37:21 +02:00
}
} else {
$new_type = $parent -> price_base_type ;
$new_min_price = $parent -> price_min ;
$new_psq = $parent -> price_by_qty ;
if ( $new_type == 'TTC' ) {
$new_price = $parent -> price_ttc ;
} else {
$new_price = $parent -> price ;
}
2018-10-19 12:02:14 +02:00
if ( $this -> variation_price_percentage ) {
if ( $new_price != 0 ) {
$new_price *= 1 + ( $this -> variation_price / 100 );
}
} else {
$new_price += $this -> variation_price ;
}
2016-07-23 16:37:21 +02:00
2022-03-08 14:49:26 +01:00
$ret = $child -> updatePrice ( $new_price , $new_type , $user , $new_vat , $new_min_price , 1 , $new_npr , $new_psq );
if ( $ret < 0 ) {
$this -> db -> rollback ();
$this -> error = $child -> error ;
$this -> errors = $child -> errors ;
return $ret ;
}
2018-10-19 12:02:14 +02:00
}
2016-07-23 16:37:21 +02:00
$this -> db -> commit ();
return 1 ;
}
$this -> db -> rollback ();
2022-03-08 14:49:26 +01:00
$this -> error = $child -> error ;
$this -> errors = $child -> errors ;
2016-07-23 16:37:21 +02:00
return - 1 ;
}
/**
* Retrieves the combination that matches the given features .
*
2017-11-26 18:43:43 +01:00
* @ param int $prodid Id of parent product
2024-03-14 21:59:15 +01:00
* @ param array < string , string > $features Format : [ $attr ] => $attr_val
2017-11-26 18:43:43 +01:00
* @ return false | ProductCombination False if not found
2016-07-23 16:37:21 +02:00
*/
public function fetchByProductCombination2ValuePairs ( $prodid , array $features )
{
2017-02-08 12:37:38 +01:00
require_once DOL_DOCUMENT_ROOT . '/variants/class/ProductCombination2ValuePair.class.php' ;
2016-07-23 16:37:21 +02:00
$actual_comp = array ();
$prodcomb2val = new ProductCombination2ValuePair ( $this -> db );
$prodcomb = new ProductCombination ( $this -> db );
2024-03-14 21:59:15 +01:00
$features = array_filter (
$features ,
/**
* @ param mixed $v Feature information of a product .
2024-03-15 04:14:48 +01:00
* @ return bool
2024-03-14 21:59:15 +01:00
*/
static function ( $v ) {
return ! empty ( $v );
}
);
2022-07-07 10:17:56 +02:00
2016-07-23 16:37:21 +02:00
foreach ( $features as $attr => $attr_val ) {
$actual_comp [ $attr ] = $attr_val ;
}
foreach ( $prodcomb -> fetchAllByFkProductParent ( $prodid ) as $prc ) {
$values = array ();
foreach ( $prodcomb2val -> fetchByFkCombination ( $prc -> id ) as $value ) {
$values [ $value -> fk_prod_attr ] = $value -> fk_prod_attr_val ;
}
$check1 = count ( array_diff_assoc ( $values , $actual_comp ));
$check2 = count ( array_diff_assoc ( $actual_comp , $values ));
if ( ! $check1 && ! $check2 ) {
return $prc ;
}
}
return false ;
}
/**
2020-08-17 13:55:18 +02:00
* Retrieves all unique attributes for a parent product
2024-04-28 17:36:25 +02:00
* ( filtered on its 'to sell' variants )
2016-07-23 16:37:21 +02:00
*
2024-04-28 17:36:25 +02:00
* @ param int $productid Parent Product rowid
* @ return array < object { id : int , ref : string , label : string , values : ProductAttributeValue []} > Array of attributes
2016-07-23 16:37:21 +02:00
*/
public function getUniqueAttributesAndValuesByFkProductParent ( $productid )
{
2017-02-08 12:37:38 +01:00
require_once DOL_DOCUMENT_ROOT . '/variants/class/ProductAttribute.class.php' ;
require_once DOL_DOCUMENT_ROOT . '/variants/class/ProductAttributeValue.class.php' ;
2016-07-23 16:37:21 +02:00
2024-04-28 17:36:25 +02:00
// Attributes
// Select all unique attributes of the variants (which are to sell) of a given parent product.
$sql = " SELECT DISTINCT c2v.fk_prod_attr, a.position " ;
$sql .= " FROM " . MAIN_DB_PREFIX . " product_attribute_combination2val c2v " ;
$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . " product_attribute_combination c " ;
$sql .= " ON c2v.fk_prod_combination = c.rowid " ;
$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . " product p " ;
$sql .= " ON p.rowid = c.fk_product_child " ;
$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . " product_attribute a " ;
$sql .= " ON a.rowid = fk_prod_attr " ;
$sql .= " WHERE c.fk_product_parent = " . (( int ) $productid );
$sql .= " AND p.tosell = 1 " ;
2022-03-18 15:38:44 +01:00
$sql .= $this -> db -> order ( 'a.position' , 'asc' );
2016-07-23 16:37:21 +02:00
2024-04-28 17:36:25 +02:00
$resql = $this -> db -> query ( $sql );
2016-07-23 16:37:21 +02:00
2024-04-28 17:36:25 +02:00
// Values
$variants = array ();
while ( $obj = $this -> db -> fetch_object ( $resql )) {
2016-07-23 16:37:21 +02:00
$attr = new ProductAttribute ( $this -> db );
2024-04-28 17:36:25 +02:00
$attr -> fetch ( $obj -> fk_prod_attr );
2016-07-23 16:37:21 +02:00
$tmp = new stdClass ();
$tmp -> id = $attr -> id ;
$tmp -> ref = $attr -> ref ;
$tmp -> label = $attr -> label ;
$tmp -> values = array ();
$attrval = new ProductAttributeValue ( $this -> db );
2024-04-28 17:36:25 +02:00
// fetch only the used values of this attribute
foreach ( $attrval -> fetchAllByProductAttribute ( $attr -> id , true ) as $val ) {
2024-03-29 18:07:20 +01:00
'@phan-var-force ProductAttributeValue $val' ;
2016-07-23 16:37:21 +02:00
$tmp -> values [] = $val ;
}
2017-02-08 12:37:38 +01:00
$variants [] = $tmp ;
2016-07-23 16:37:21 +02:00
}
2017-02-08 12:37:38 +01:00
return $variants ;
2016-07-23 16:37:21 +02:00
}
/**
* Creates a product combination . Check usages to find more about its use
* Format of $combinations array :
* array (
* 0 => array (
* attr => value ,
* attr2 => value
* [ ... ]
* ),
* [ ... ]
* )
*
2024-06-22 15:40:26 +02:00
* @ param User $user User
* @ param Product $product Parent Product
* @ param array < int , int > $combinations Attribute and value combinations .
qual: fix PHAN warnings for ProductCombination.class.php (#30020)
htdocs/variants/class/ProductCombination.class.php 843 TypeError PhanTypeMismatchDimFetch When fetching an array index from a value of type array, found an array index of type int, but expected the index to be of type string
htdocs/variants/class/ProductCombination.class.php 843 TypeError PhanTypeMismatchDimFetch When fetching an array index from a value of type array, found an array index of type int, but expected the index to be of type string
htdocs/variants/class/ProductCombination.class.php 846 TypeError PhanTypeMismatchDimFetch When fetching an array index from a value of type array, found an array index of type int, but expected the index to be of type string
htdocs/variants/class/ProductCombination.class.php 846 TypeError PhanTypeMismatchDimFetch When fetching an array index from a value of type array, found an array index of type int, but expected the index to be of type string
htdocs/variants/class/ProductCombination.class.php 851 TypeError PhanTypeMismatchDimFetch When fetching an array index from a value of type array, found an array index of type int, but expected the index to be of type string
htdocs/variants/class/ProductCombination.class.php 851 TypeError PhanTypeMismatchDimFetch When fetching an array index from a value of type array, found an array index of type int, but expected the index to be of type string
2024-06-15 15:01:40 +02:00
* @ param array < int , array < int , array { weight : string | float , price : string | float } >> $variations Price and weight variations ( example : $variations [ fk_product_attribute ][ fk_product_attribute_value ][ 'weight' ])
2024-06-22 15:40:26 +02:00
* @ param bool | bool [] $price_var_percent Is the price variation value a relative variation ( in % ) ? ( it is an array if global constant " PRODUIT_MULTIPRICES " is on )
* @ param false | float | float [] $forced_pricevar Value of the price variation if it is forced ; in currency or percent . ( it is an array if global constant " PRODUIT_MULTIPRICES " is on )
* @ param false | float $forced_weightvar Value of the weight variation if it is forced
* @ param false | string $forced_refvar Value of the reference if it is forced
* @ param string $ref_ext External reference
* @ return int <- 1 , 1 > Return integer < 0 KO , > 0 OK
2016-07-23 16:37:21 +02:00
*/
2020-09-04 11:01:47 +02:00
public function createProductCombination ( User $user , Product $product , array $combinations , array $variations , $price_var_percent = false , $forced_pricevar = false , $forced_weightvar = false , $forced_refvar = false , $ref_ext = '' )
2016-07-23 16:37:21 +02:00
{
2020-09-21 12:35:20 +02:00
global $conf ;
2016-07-23 16:37:21 +02:00
2017-02-08 12:37:38 +01:00
require_once DOL_DOCUMENT_ROOT . '/variants/class/ProductAttribute.class.php' ;
require_once DOL_DOCUMENT_ROOT . '/variants/class/ProductAttributeValue.class.php' ;
2016-07-23 16:37:21 +02:00
2020-09-19 23:30:29 +02:00
$this -> db -> begin ();
2016-07-23 16:37:21 +02:00
2024-03-14 21:59:15 +01:00
$price_impact = array ( 1 => 0 ); // init level price impact
2020-08-12 10:14:29 +02:00
2024-03-19 22:40:43 +01:00
$forced_refvar = trim (( string ) $forced_refvar );
2019-11-23 16:23:31 +01:00
2019-11-23 16:14:21 +01:00
if ( ! empty ( $forced_refvar ) && $forced_refvar != $product -> ref ) {
2020-09-19 23:30:29 +02:00
$existingProduct = new Product ( $this -> db );
2024-06-15 15:53:36 +02:00
$result = $existingProduct -> fetch ( 0 , $forced_refvar );
2019-11-23 16:23:31 +01:00
if ( $result > 0 ) {
$newproduct = $existingProduct ;
} else {
$existingProduct = false ;
$newproduct = clone $product ;
$newproduct -> ref = $forced_refvar ;
}
2019-11-23 16:14:21 +01:00
} else {
2020-05-23 13:53:47 +02:00
$forced_refvar = false ;
$existingProduct = false ;
$newproduct = clone $product ;
2019-11-23 16:14:21 +01:00
}
2016-07-23 16:37:21 +02:00
2025-01-04 08:20:18 +01:00
// To avoid warning with unique extrafields values
$newproduct -> context [ 'createproductcombination' ] = 'createproductcombination' ;
2016-07-23 16:37:21 +02:00
//Final weight impact
2020-09-07 10:18:17 +02:00
$weight_impact = ( float ) $forced_weightvar ; // If false, return 0
2016-07-23 16:37:21 +02:00
//Final price impact
2020-08-17 13:55:18 +02:00
if ( ! is_array ( $forced_pricevar )) {
2020-09-07 10:18:17 +02:00
$price_impact [ 1 ] = ( float ) $forced_pricevar ; // If false, return 0
2021-02-26 13:10:10 +01:00
} else {
2020-08-12 10:14:29 +02:00
$price_impact = $forced_pricevar ;
}
2019-11-23 16:14:21 +01:00
2025-01-07 17:09:48 +01:00
if ( ! is_array ( $price_var_percent )) {
2025-01-07 17:37:30 +01:00
$price_var_percent = array ( 1 => ( bool ) $price_var_percent );
2021-09-10 15:03:08 +02:00
}
2020-09-19 23:30:29 +02:00
$newcomb = new ProductCombination ( $this -> db );
2016-07-23 16:37:21 +02:00
$existingCombination = $newcomb -> fetchByProductCombination2ValuePairs ( $product -> id , $combinations );
if ( $existingCombination ) {
$newcomb = $existingCombination ;
} else {
$newcomb -> fk_product_parent = $product -> id ;
2020-08-17 13:55:18 +02:00
// Create 1 entry into product_attribute_combination (1 entry for each combinations). This init also $newcomb->id
$result = $newcomb -> create ( $user );
if ( $result < 0 ) {
$this -> error = $newcomb -> error ;
$this -> errors = $newcomb -> errors ;
2020-09-19 23:30:29 +02:00
$this -> db -> rollback ();
2016-07-23 16:37:21 +02:00
return - 1 ;
}
}
2020-09-19 23:30:29 +02:00
$prodattr = new ProductAttribute ( $this -> db );
$prodattrval = new ProductAttributeValue ( $this -> db );
2016-07-23 16:37:21 +02:00
2017-11-26 18:43:43 +01:00
// $combination contains list of attributes pairs key->value. Example: array('id Color'=>id Blue, 'id Size'=>id Small, 'id Option'=>id val a, ...)
2016-07-23 16:37:21 +02:00
foreach ( $combinations as $currcombattr => $currcombval ) {
//This was checked earlier, so no need to double check
$prodattr -> fetch ( $currcombattr );
$prodattrval -> fetch ( $currcombval );
//If there is an existing combination, there is no need to duplicate the valuepair
if ( ! $existingCombination ) {
2020-09-19 23:30:29 +02:00
$tmp = new ProductCombination2ValuePair ( $this -> db );
2016-07-23 16:37:21 +02:00
$tmp -> fk_prod_attr = $currcombattr ;
$tmp -> fk_prod_attr_val = $currcombval ;
$tmp -> fk_prod_combination = $newcomb -> id ;
2017-11-26 18:43:43 +01:00
if ( $tmp -> create ( $user ) < 0 ) { // Create 1 entry into product_attribute_combination2val
2020-08-17 13:55:18 +02:00
$this -> error = $tmp -> error ;
$this -> errors = $tmp -> errors ;
2020-09-19 23:30:29 +02:00
$this -> db -> rollback ();
2016-07-23 16:37:21 +02:00
return - 1 ;
}
}
if ( $forced_weightvar === false ) {
$weight_impact += ( float ) price2num ( $variations [ $currcombattr ][ $currcombval ][ 'weight' ]);
}
if ( $forced_pricevar === false ) {
2020-08-12 10:14:29 +02:00
$price_impact [ 1 ] += ( float ) price2num ( $variations [ $currcombattr ][ $currcombval ][ 'price' ]);
// Manage Price levels
2024-06-22 15:40:26 +02:00
if ( getDolGlobalString ( 'PRODUIT_MULTIPRICES' )) {
$produit_multiprices_limit = getDolGlobalString ( 'PRODUIT_MULTIPRICES_LIMIT' );
for ( $i = 2 ; $i <= $produit_multiprices_limit ; $i ++ ) {
2020-08-12 10:14:29 +02:00
$price_impact [ $i ] += ( float ) price2num ( $variations [ $currcombattr ][ $currcombval ][ 'price' ]);
}
}
2016-07-23 16:37:21 +02:00
}
2019-11-23 16:23:31 +01:00
2019-11-23 16:14:21 +01:00
if ( $forced_refvar === false ) {
2020-05-23 13:53:47 +02:00
if ( isset ( $conf -> global -> PRODUIT_ATTRIBUTES_SEPARATOR )) {
2023-10-15 18:41:39 +02:00
$newproduct -> ref .= getDolGlobalString ( 'PRODUIT_ATTRIBUTES_SEPARATOR' ) . $prodattrval -> ref ;
2020-05-23 13:53:47 +02:00
} else {
2019-11-23 16:23:31 +01:00
$newproduct -> ref .= '_' . $prodattrval -> ref ;
2020-05-23 13:53:47 +02:00
}
2017-09-25 12:05:12 +02:00
}
2016-07-23 16:37:21 +02:00
//The first one should not contain a linebreak
if ( $newproduct -> description ) {
$newproduct -> description .= '<br>' ;
}
$newproduct -> description .= '<strong>' . $prodattr -> label . ':</strong> ' . $prodattrval -> value ;
}
2024-12-09 17:41:56 +01:00
$newcomb -> variation_price_percentage = ( bool ) $price_var_percent [ 1 ];
2020-08-12 10:14:29 +02:00
$newcomb -> variation_price = $price_impact [ 1 ];
2016-07-23 16:37:21 +02:00
$newcomb -> variation_weight = $weight_impact ;
2020-10-31 14:32:18 +01:00
$newcomb -> variation_ref_ext = $this -> db -> escape ( $ref_ext );
2016-07-23 16:37:21 +02:00
2020-08-12 10:14:29 +02:00
// Init price level
2024-06-22 15:40:26 +02:00
if ( getDolGlobalString ( 'PRODUIT_MULTIPRICES' )) {
2024-12-17 16:59:13 +01:00
$produit_multiprices_limit = getDolGlobalInt ( 'PRODUIT_MULTIPRICES_LIMIT' );
2024-06-22 15:40:26 +02:00
for ( $i = 1 ; $i <= $produit_multiprices_limit ; $i ++ ) {
2020-08-12 10:14:29 +02:00
$productCombinationLevel = new ProductCombinationLevel ( $this -> db );
2020-08-17 13:55:18 +02:00
$productCombinationLevel -> fk_product_attribute_combination = $newcomb -> id ;
2020-08-12 10:14:29 +02:00
$productCombinationLevel -> fk_price_level = $i ;
$productCombinationLevel -> variation_price = $price_impact [ $i ];
2025-01-20 16:23:29 +01:00
$productCombinationLevel -> variation_price_percentage = ( bool ) $price_var_percent [ $i ] ;
2020-08-12 10:14:29 +02:00
$newcomb -> combination_price_levels [ $i ] = $productCombinationLevel ;
}
}
2020-08-17 13:55:18 +02:00
//var_dump($newcomb->combination_price_levels);
2020-08-12 10:14:29 +02:00
2016-07-23 16:37:21 +02:00
$newproduct -> weight += $weight_impact ;
2017-11-26 18:43:43 +01:00
// Now create the product
//print 'Create prod '.$newproduct->ref.'<br>'."\n";
2019-11-23 16:14:21 +01:00
if ( $existingProduct === false ) {
2020-05-23 13:53:47 +02:00
//To avoid wrong information in price history log
$newproduct -> price = 0 ;
$newproduct -> price_ttc = 0 ;
$newproduct -> price_min = 0 ;
$newproduct -> price_min_ttc = 0 ;
// A new variant must use a new barcode (not same product)
$newproduct -> barcode = - 1 ;
$result = $newproduct -> create ( $user );
2021-02-26 13:10:10 +01:00
if ( $result < 0 ) {
2020-05-23 13:53:47 +02:00
//In case the error is not related with an already existing product
if ( $newproduct -> error != 'ErrorProductAlreadyExists' ) {
2024-03-13 19:28:10 +01:00
$this -> error = $newproduct -> error ;
2020-05-23 13:53:47 +02:00
$this -> errors = $newproduct -> errors ;
2020-09-19 23:30:29 +02:00
$this -> db -> rollback ();
2020-05-23 13:53:47 +02:00
return - 1 ;
}
/**
* If there is an existing combination , then we update the prices and weight
* Otherwise , we try adding a random number to the ref
*/
if ( $newcomb -> fk_product_child ) {
$res = $newproduct -> fetch ( $existingCombination -> fk_product_child );
} else {
$orig_prod_ref = $newproduct -> ref ;
$i = 1 ;
do {
$newproduct -> ref = $orig_prod_ref . $i ;
$res = $newproduct -> create ( $user );
if ( $newproduct -> error != 'ErrorProductAlreadyExists' ) {
$this -> errors [] = $newproduct -> error ;
break ;
}
$i ++ ;
} while ( $res < 0 );
}
if ( $res < 0 ) {
2020-09-19 23:30:29 +02:00
$this -> db -> rollback ();
2020-05-23 13:53:47 +02:00
return - 1 ;
}
}
2019-11-23 16:14:21 +01:00
} else {
2020-05-23 13:53:47 +02:00
$result = $newproduct -> update ( $newproduct -> id , $user );
2021-02-26 13:10:10 +01:00
if ( $result < 0 ) {
2020-09-19 23:30:29 +02:00
$this -> db -> rollback ();
2020-05-23 13:53:47 +02:00
return - 1 ;
}
2016-07-23 16:37:21 +02:00
}
$newcomb -> fk_product_child = $newproduct -> id ;
2021-02-26 13:10:10 +01:00
if ( $newcomb -> update ( $user ) < 0 ) {
2020-08-17 13:55:18 +02:00
$this -> error = $newcomb -> error ;
$this -> errors = $newcomb -> errors ;
2020-09-19 23:30:29 +02:00
$this -> db -> rollback ();
2016-07-23 16:37:21 +02:00
return - 1 ;
}
2020-09-19 23:30:29 +02:00
$this -> db -> commit ();
2018-10-11 12:08:50 +02:00
return $newproduct -> id ;
2016-07-23 16:37:21 +02:00
}
2020-05-23 13:53:47 +02:00
/**
* Copies all product combinations from the origin product to the destination product
*
2019-11-17 01:24:07 +01:00
* @ param User $user Object user
2020-05-23 13:53:47 +02:00
* @ param int $origProductId Origin product id
* @ param Product $destProduct Destination product
* @ return int > 0 OK < 0 KO
*/
2019-11-17 01:24:07 +01:00
public function copyAll ( User $user , $origProductId , Product $destProduct )
2016-07-23 16:37:21 +02:00
{
2017-02-08 12:37:38 +01:00
require_once DOL_DOCUMENT_ROOT . '/variants/class/ProductCombination2ValuePair.class.php' ;
2016-07-23 16:37:21 +02:00
//To prevent a loop
if ( $origProductId == $destProduct -> id ) {
return - 1 ;
}
$prodcomb2val = new ProductCombination2ValuePair ( $this -> db );
//Retrieve all product combinations
2017-11-26 18:43:43 +01:00
$combinations = $this -> fetchAllByFkProductParent ( $origProductId );
2016-07-23 16:37:21 +02:00
foreach ( $combinations as $combination ) {
$variations = array ();
foreach ( $prodcomb2val -> fetchByFkCombination ( $combination -> id ) as $tmp_pc2v ) {
$variations [ $tmp_pc2v -> fk_prod_attr ] = $tmp_pc2v -> fk_prod_attr_val ;
}
2025-01-07 17:20:57 +01:00
$variation_price_percentage = $combination -> variation_price_percentage ;
$variation_price = $combination -> variation_price ;
2025-01-07 17:09:48 +01:00
if ( getDolGlobalInt ( 'PRODUIT_MULTIPRICES' ) && getDolGlobalInt ( 'PRODUIT_MULTIPRICES_LIMIT' ) > 1 ) {
$variation_price_percentage = [ ];
$variation_price = [ ];
foreach ( $combination -> combination_price_levels as $productCombinationLevel ) {
$variation_price_percentage [ $productCombinationLevel -> fk_price_level ] = $productCombinationLevel -> variation_price_percentage ;
$variation_price [ $productCombinationLevel -> fk_price_level ] = $productCombinationLevel -> variation_price ;
}
}
2020-05-23 13:53:47 +02:00
if ( $this -> createProductCombination (
2019-11-17 01:24:07 +01:00
$user ,
2016-07-23 16:37:21 +02:00
$destProduct ,
$variations ,
array (),
2025-01-07 17:09:48 +01:00
$variation_price_percentage ,
$variation_price ,
2016-07-23 16:37:21 +02:00
$combination -> variation_weight
2021-02-26 13:10:10 +01:00
) < 0 ) {
2016-07-23 16:37:21 +02:00
return - 1 ;
}
}
return 1 ;
}
2018-10-18 10:26:21 +02:00
/**
* Return label for combinations
2019-02-10 10:45:49 +01:00
* @ param int $prod_child id of child
* @ return string combination label
2018-10-18 10:26:21 +02:00
*/
2018-10-19 08:53:01 +02:00
public function getCombinationLabel ( $prod_child )
2018-10-18 10:26:21 +02:00
{
$label = '' ;
$sql = 'SELECT pav.value AS label' ;
2020-04-10 10:59:32 +02:00
$sql .= ' FROM ' . MAIN_DB_PREFIX . 'product_attribute_combination pac' ;
$sql .= ' INNER JOIN ' . MAIN_DB_PREFIX . 'product_attribute_combination2val pac2v ON pac2v.fk_prod_combination=pac.rowid' ;
$sql .= ' INNER JOIN ' . MAIN_DB_PREFIX . 'product_attribute_value pav ON pav.rowid=pac2v.fk_prod_attr_val' ;
2021-08-28 03:09:18 +02:00
$sql .= ' WHERE pac.fk_product_child=' . (( int ) $prod_child );
2018-10-18 10:26:21 +02:00
$resql = $this -> db -> query ( $sql );
if ( $resql ) {
$num = $this -> db -> num_rows ( $resql );
$i = 0 ;
2021-02-26 13:10:10 +01:00
while ( $i < $num ) {
2018-10-18 10:26:21 +02:00
$obj = $this -> db -> fetch_object ( $resql );
2021-02-26 13:10:10 +01:00
if ( $obj -> label ) {
2020-04-10 10:59:32 +02:00
$label .= ' ' . $obj -> label ;
2018-10-18 10:26:21 +02:00
}
$i ++ ;
}
}
return $label ;
}
2017-09-25 12:05:12 +02:00
}
2020-08-12 10:14:29 +02:00
/**
* Class ProductCombinationLevel
* Used to represent a product combination Level
*/
class ProductCombinationLevel
{
/**
* Database handler
* @ var DoliDB
*/
2020-09-19 23:30:29 +02:00
public $db ;
2020-08-12 10:14:29 +02:00
/**
* @ var string Name of table without prefix where object is stored
*/
public $table_element = 'product_attribute_combination_price_level' ;
/**
* Rowid of combination
* @ var int
*/
public $id ;
/**
* Rowid of parent product combination
* @ var int
*/
public $fk_product_attribute_combination ;
/**
* Combination price level
* @ var int
*/
public $fk_price_level ;
/**
* Price variation
* @ var float
*/
public $variation_price ;
/**
* Is the price variation a relative variation ?
* @ var bool
*/
public $variation_price_percentage = false ;
2023-02-20 20:43:23 +01:00
/**
* @ var string error
*/
public $error ;
/**
* @ var string [] array of errors
*/
public $errors = array ();
2020-08-12 10:14:29 +02:00
/**
* Constructor
*
* @ param DoliDB $db Database handler
*/
public function __construct ( DoliDB $db )
{
$this -> db = $db ;
}
/**
* Retrieves a combination level by its rowid
*
* @ param int $rowid Row id
2023-12-06 15:46:39 +01:00
* @ return int Return integer < 0 KO , > 0 OK
2020-08-12 10:14:29 +02:00
*/
public function fetch ( $rowid )
{
2022-05-10 14:38:40 +02:00
$sql = " SELECT rowid, fk_product_attribute_combination, fk_price_level, variation_price, variation_price_percentage " ;
$sql .= " FROM " . MAIN_DB_PREFIX . $this -> table_element ;
$sql .= " WHERE rowid = " . ( int ) $rowid ;
2020-08-12 10:14:29 +02:00
2022-05-10 14:38:40 +02:00
$resql = $this -> db -> query ( $sql );
if ( $resql ) {
$obj = $this -> db -> fetch_object ( $resql );
if ( $obj ) {
return $this -> fetchFormObj ( $obj );
}
2020-08-12 10:14:29 +02:00
}
return - 1 ;
}
/**
* Retrieves combination price levels
*
2020-08-17 05:09:27 +02:00
* @ param int $fk_product_attribute_combination Id of product combination
* @ param int $fk_price_level The price level to fetch , use 0 for all
* @ return mixed self [] | - 1 on KO
2020-08-12 10:14:29 +02:00
*/
public function fetchAll ( $fk_product_attribute_combination , $fk_price_level = 0 )
{
2021-11-07 18:14:49 +01:00
$result = array ();
2020-08-12 10:14:29 +02:00
2020-08-17 05:54:22 +02:00
$sql = " SELECT rowid, fk_product_attribute_combination, fk_price_level, variation_price, variation_price_percentage " ;
$sql .= " FROM " . MAIN_DB_PREFIX . $this -> table_element ;
$sql .= " WHERE fk_product_attribute_combination = " . intval ( $fk_product_attribute_combination );
2020-09-07 10:18:17 +02:00
if ( ! empty ( $fk_price_level )) {
$sql .= ' AND fk_price_level = ' . intval ( $fk_price_level );
2020-08-12 10:14:29 +02:00
}
2021-11-07 18:14:49 +01:00
$res = $this -> db -> query ( $sql );
if ( $res ) {
if ( $this -> db -> num_rows ( $res ) > 0 ) {
while ( $obj = $this -> db -> fetch_object ( $res )) {
$productCombinationLevel = new ProductCombinationLevel ( $this -> db );
$productCombinationLevel -> fetchFormObj ( $obj );
$result [ $obj -> fk_price_level ] = $productCombinationLevel ;
}
2020-08-13 11:43:33 +02:00
}
2021-11-07 18:14:49 +01:00
} else {
return - 1 ;
2020-08-12 10:14:29 +02:00
}
return $result ;
}
/**
2021-11-07 18:14:49 +01:00
* Assign vars form an stdclass like sql obj
2020-08-12 10:14:29 +02:00
*
2023-02-20 20:43:23 +01:00
* @ param Object $obj Object resultset
2023-12-06 15:46:39 +01:00
* @ return int Return integer < 0 KO , > 0 OK
2020-08-12 10:14:29 +02:00
*/
public function fetchFormObj ( $obj )
{
if ( ! $obj ) {
return - 1 ;
}
$this -> id = $obj -> rowid ;
2024-02-25 10:00:58 +01:00
$this -> fk_product_attribute_combination = ( int ) $obj -> fk_product_attribute_combination ;
2020-08-12 10:14:29 +02:00
$this -> fk_price_level = intval ( $obj -> fk_price_level );
2023-12-27 12:12:20 +01:00
$this -> variation_price = ( float ) $obj -> variation_price ;
2020-08-13 11:48:28 +02:00
$this -> variation_price_percentage = ( bool ) $obj -> variation_price_percentage ;
2020-08-12 10:14:29 +02:00
return 1 ;
}
/**
2020-08-17 13:55:18 +02:00
* Save a price impact of a product combination for a price level
2020-08-12 10:14:29 +02:00
*
2023-12-06 15:46:39 +01:00
* @ return int Return integer < 0 KO , > 0 OK
2020-08-12 10:14:29 +02:00
*/
public function save ()
{
2020-08-17 13:55:18 +02:00
if (( $this -> id > 0 && empty ( $this -> fk_product_attribute_combination )) || empty ( $this -> fk_price_level )) {
2020-08-12 10:14:29 +02:00
return - 1 ;
}
2020-08-17 13:55:18 +02:00
// Check if level exist in DB before add
if ( $this -> fk_product_attribute_combination > 0 && empty ( $this -> id )) {
2020-08-17 05:09:27 +02:00
$sql = " SELECT rowid id " ;
2020-09-07 10:18:17 +02:00
$sql .= " FROM " . MAIN_DB_PREFIX . $this -> table_element ;
2020-08-17 05:09:27 +02:00
$sql .= " WHERE fk_product_attribute_combination = " . ( int ) $this -> fk_product_attribute_combination ;
2022-05-10 14:38:40 +02:00
$sql .= ' AND fk_price_level = ' . (( int ) $this -> fk_price_level );
2020-08-12 10:14:29 +02:00
2022-05-10 14:38:40 +02:00
$resql = $this -> db -> query ( $sql );
if ( $resql ) {
$obj = $this -> db -> fetch_object ( $resql );
if ( $obj ) {
$this -> id = $obj -> id ;
}
2020-08-12 10:14:29 +02:00
}
}
// Update
2020-08-13 11:48:28 +02:00
if ( ! empty ( $this -> id )) {
2020-09-07 10:18:17 +02:00
$sql = 'UPDATE ' . MAIN_DB_PREFIX . $this -> table_element ;
2023-12-27 12:12:20 +01:00
$sql .= ' SET variation_price = ' . ( float ) $this -> variation_price . ' , variation_price_percentage = ' . intval ( $this -> variation_price_percentage );
2020-08-17 05:26:32 +02:00
$sql .= ' WHERE rowid = ' . (( int ) $this -> id );
2020-08-12 10:14:29 +02:00
$res = $this -> db -> query ( $sql );
2020-08-17 13:55:18 +02:00
if ( $res > 0 ) {
2020-08-12 10:14:29 +02:00
return $this -> id ;
2020-08-17 13:55:18 +02:00
} else {
2020-08-12 10:14:29 +02:00
$this -> error = $this -> db -> error ();
$this -> errors [] = $this -> error ;
return - 1 ;
}
2020-08-17 13:55:18 +02:00
} else {
// Add
2020-09-07 10:18:17 +02:00
$sql = " INSERT INTO " . MAIN_DB_PREFIX . $this -> table_element . " ( " ;
2020-08-17 05:26:32 +02:00
$sql .= " fk_product_attribute_combination, fk_price_level, variation_price, variation_price_percentage " ;
$sql .= " ) VALUES ( " ;
$sql .= ( int ) $this -> fk_product_attribute_combination ;
$sql .= " , " . intval ( $this -> fk_price_level );
2023-12-27 12:12:20 +01:00
$sql .= " , " . ( float ) $this -> variation_price ;
2020-08-17 05:26:32 +02:00
$sql .= " , " . intval ( $this -> variation_price_percentage );
2020-08-17 05:09:27 +02:00
$sql .= " ) " ;
2020-08-12 10:14:29 +02:00
$res = $this -> db -> query ( $sql );
2020-08-17 13:55:18 +02:00
if ( $res ) {
2020-09-07 10:18:17 +02:00
$this -> id = $this -> db -> last_insert_id ( MAIN_DB_PREFIX . $this -> table_element );
2020-08-17 13:55:18 +02:00
} else {
2020-08-12 10:14:29 +02:00
$this -> error = $this -> db -> error ();
$this -> errors [] = $this -> error ;
return - 1 ;
}
}
return $this -> id ;
}
/**
* delete
*
2023-12-06 15:46:39 +01:00
* @ return int Return integer < 0 KO , > 0 OK
2020-08-12 10:14:29 +02:00
*/
public function delete ()
{
2020-08-17 05:09:27 +02:00
$sql = " DELETE FROM " . MAIN_DB_PREFIX . $this -> table_element . " WHERE rowid = " . ( int ) $this -> id ;
$res = $this -> db -> query ( $sql );
2020-08-12 10:14:29 +02:00
return $res ? 1 : - 1 ;
}
/**
* delete all for a combination
*
2020-08-17 05:54:22 +02:00
* @ param int $fk_product_attribute_combination Id of combination
2023-12-06 15:46:39 +01:00
* @ return int Return integer < 0 KO , > 0 OK
2020-08-12 10:14:29 +02:00
*/
public function deleteAllForCombination ( $fk_product_attribute_combination )
{
2020-08-17 05:09:27 +02:00
$sql = " DELETE FROM " . MAIN_DB_PREFIX . $this -> table_element . " WHERE fk_product_attribute_combination = " . ( int ) $fk_product_attribute_combination ;
$res = $this -> db -> query ( $sql );
2020-08-12 10:14:29 +02:00
return $res ? 1 : - 1 ;
}
/**
* Clean not needed price levels for a combination
*
2020-08-17 05:54:22 +02:00
* @ param int $fk_product_attribute_combination Id of combination
2023-12-06 15:46:39 +01:00
* @ return int Return integer < 0 KO , > 0 OK
2020-08-12 10:14:29 +02:00
*/
public function clean ( $fk_product_attribute_combination )
{
global $conf ;
2020-08-17 05:54:22 +02:00
$sql = " DELETE FROM " . MAIN_DB_PREFIX . $this -> table_element ;
$sql .= " WHERE fk_product_attribute_combination = " . ( int ) $fk_product_attribute_combination ;
2024-06-22 15:40:26 +02:00
$sql .= " AND fk_price_level > " . ( int ) getDolGlobalString ( 'PRODUIT_MULTIPRICES_LIMIT' );
2020-08-17 05:54:22 +02:00
$res = $this -> db -> query ( $sql );
2020-08-12 10:14:29 +02:00
return $res ? 1 : - 1 ;
}
2020-09-02 16:24:22 +02:00
/**
* Create new Product Combination Price level from Parent
*
2020-09-03 12:37:30 +02:00
* @ param DoliDB $db Database handler
* @ param ProductCombination $productCombination Product combination
* @ param int $fkPriceLevel Price level
2020-09-02 16:24:22 +02:00
* @ return ProductCombinationLevel
*/
public static function createFromParent ( DoliDB $db , ProductCombination $productCombination , $fkPriceLevel )
{
$productCombinationLevel = new self ( $db );
$productCombinationLevel -> fk_price_level = $fkPriceLevel ;
$productCombinationLevel -> fk_product_attribute_combination = $productCombination -> id ;
$productCombinationLevel -> variation_price = $productCombination -> variation_price ;
2020-09-14 04:23:14 +02:00
$productCombinationLevel -> variation_price_percentage = ( bool ) $productCombination -> variation_price_percentage ;
2020-09-02 16:24:22 +02:00
return $productCombinationLevel ;
}
2020-08-12 10:14:29 +02:00
}