Merge pull request #12501 from c3do/patch-3

NEW API to update purchase price. NEW Can force ref of a variant product
This commit is contained in:
Laurent Destailleur 2020-02-21 19:48:22 +01:00 committed by GitHub
commit 2492188de3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 169 additions and 69 deletions

View File

@ -380,3 +380,4 @@ ErrorProductCombinationNotFound=Product variant not found
ActionAvailableOnVariantProductOnly=Action only available on the variant of product
ProductsPricePerCustomer=Product prices per customers
ProductSupplierExtraFields=Additional Attributes (Supplier Prices)
DeleteLinkedProduct=Delete the child product linked to the combination

View File

@ -375,4 +375,4 @@ ErrorDestinationProductNotFound=Produit destination non trouvé
ErrorProductCombinationNotFound=Variante du produit non trouvé
ActionAvailableOnVariantProductOnly=Action disponible uniquement sur la variante du produit
ProductsPricePerCustomer=Prix produit par clients
ProductSupplierExtraFields=Attributs supplémentaires (Prix fournisseur)
ProductSupplierExtraFields=Attributs supplémentaires (Prix fournisseur)

View File

@ -630,6 +630,73 @@ class Products extends DolibarrApi
);
}
/**
* Add/Update purchase prices for a product.
*
* @param int $id ID of Product
* @param float $qty Min quantity for which price is valid
* @param float $buyprice Purchase price for the quantity min
* @param string $price_base_type HT or TTC
* @param int $fourn_id Supplier ID
* @param int $availability Product availability
* @param string $ref_fourn Supplier ref
* @param float $tva_tx New VAT Rate (For example 8.5. Should not be a string)
* @param string $charges costs affering to product
* @param float $remise_percent Discount regarding qty (percent)
* @param float $remise Discount regarding qty (amount)
* @param int $newnpr Set NPR or not
* @param int $delivery_time_days Delay in days for delivery (max). May be '' if not defined.
* @param string $supplier_reputation Reputation with this product to the defined supplier (empty, FAVORITE, DONOTORDER)
* @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
* @param float $multicurrency_buyprice Purchase price for the quantity min in currency
* @param string $multicurrency_price_base_type HT or TTC in currency
* @param float $multicurrency_tx Rate currency
* @param string $multicurrency_code Currency code
* @param string $desc_fourn Custom description for product_fourn_price
* @param string $barcode Barcode
* @param int $fk_barcode_type Barcode type
* @return int
*
* @throws RestException 500
* @throws RestException 401
*
* @url POST {id}/purchase_prices
*/
public function addPurchasePrice($id, $qty, $buyprice, $price_base_type, $fourn_id, $availability, $ref_fourn, $tva_tx, $charges = 0, $remise_percent = 0, $remise = 0, $newnpr = 0, $delivery_time_days = 0, $supplier_reputation = '', $localtaxes_array = array(), $newdefaultvatcode = '', $multicurrency_buyprice = 0, $multicurrency_price_base_type = 'HT', $multicurrency_tx = 1, $multicurrency_code = '', $desc_fourn = '', $barcode = '', $fk_barcode_type = null)
{
if(! DolibarrApiAccess::$user->rights->produit->creer) {
throw new RestException(401);
}
$result = $this->productsupplier->fetch($id);
if (!$result) {
throw new RestException(404, 'Product not found');
}
if (!DolibarrApi::_checkAccessToResource('product', $this->productsupplier->id)) {
throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
}
$result = $this->productsupplier->add_fournisseur(DolibarrApiAccess::$user, $fourn_id, $ref_fourn, $qty);
if ($result < 0) {
throw new RestException(500, "Error adding supplier to product : ".$this->db->lasterror());
}
$fourn = new Fournisseur($this->db);
$result = $fourn->fetch($fourn_id);
if ($result <= 0) {
throw new RestException(404, 'Supplier not found');
}
$result = $this->productsupplier->update_buyprice($qty, $buyprice, DolibarrApiAccess::$user, $price_base_type, $fourn, $availability, $ref_fourn, $tva_tx, $charges, $remise_percent, $remise, $newnpr, $delivery_time_days, $supplier_reputation, $localtaxes_array, $newdefaultvatcode, $multicurrency_buyprice, $multicurrency_price_base_type, $multicurrency_tx, $multicurrency_code, $desc_fourn, $barcode, $fk_barcode_type);
if ($result <= 0) {
throw new RestException(500, "Error updating buy price : ".$this->db->lasterror());
}
return (int) $this->productsupplier->product_fourn_price_id;
}
/**
* Delete purchase price for a product
*
@ -1325,11 +1392,12 @@ class Products extends DolibarrApi
*
* "features" is a list of attributes pairs id_attribute=>id_value. Example: array(id_color=>id_Blue, id_size=>id_small, id_option=>id_val_a, ...)
*
* @param int $id ID of Product
* @param float $weight_impact Weight impact of variant
* @param float $price_impact Price impact of variant
* @param bool $price_impact_is_percent Price impact in percent (true or false)
* @param array $features List of attributes pairs id_attribute->id_value. Example: array(id_color=>id_Blue, id_size=>id_small, id_option=>id_val_a, ...)
* @param int $id ID of Product
* @param float $weight_impact Weight impact of variant
* @param float $price_impact Price impact of variant
* @param bool $price_impact_is_percent Price impact in percent (true or false)
* @param array $features List of attributes pairs id_attribute->id_value. Example: array(id_color=>id_Blue, id_size=>id_small, id_option=>id_val_a, ...)
* @param bool|string $reference Customized reference of variant
* @return int
*
* @throws RestException 500
@ -1338,7 +1406,7 @@ class Products extends DolibarrApi
*
* @url POST {id}/variants
*/
public function addVariant($id, $weight_impact, $price_impact, $price_impact_is_percent, $features)
public function addVariant($id, $weight_impact, $price_impact, $price_impact_is_percent, $features, $reference = false)
{
if (!DolibarrApiAccess::$user->rights->produit->creer) {
throw new RestException(401);
@ -1368,17 +1436,13 @@ class Products extends DolibarrApi
}
$prodcomb = new ProductCombination($this->db);
if (!$prodcomb->fetchByProductCombination2ValuePairs($id, $features))
$result = $prodcomb->createProductCombination(DolibarrApiAccess::$user, $this->product, $features, array(), $price_impact_is_percent, $price_impact, $weight_impact, $reference);
if ($result > 0)
{
$result = $prodcomb->createProductCombination(DolibarrApiAccess::$user, $this->product, $features, array(), $price_impact_is_percent, $price_impact, $weight_impact);
if ($result > 0)
{
return $result;
} else {
throw new RestException(500, "Error creating new product variant");
}
return $result;
} else {
return $prodcomb->id;
throw new RestException(500, "Error creating new product variant");
}
}

View File

@ -502,9 +502,10 @@ WHERE c.fk_product_parent = ".(int) $productid." AND p.tosell = 1";
* @param bool $price_var_percent Is the price variation a relative variation?
* @param bool|float $forced_pricevar If the price variation is forced
* @param bool|float $forced_weightvar If the weight variation is forced
* @param bool|string $forced_refvar If the reference is forced
* @return int <0 KO, >0 OK
*/
public function createProductCombination(User $user, Product $product, array $combinations, array $variations, $price_var_percent = false, $forced_pricevar = false, $forced_weightvar = false)
public function createProductCombination(User $user, Product $product, array $combinations, array $variations, $price_var_percent = false, $forced_pricevar = false, $forced_weightvar = false, $forced_refvar = false)
{
global $db, $conf;
@ -513,7 +514,23 @@ WHERE c.fk_product_parent = ".(int) $productid." AND p.tosell = 1";
$db->begin();
$newproduct = clone $product;
$forced_refvar = trim($forced_refvar);
if (!empty($forced_refvar) && $forced_refvar != $product->ref) {
$existingProduct = new Product($db);
$result = $existingProduct->fetch('', $forced_refvar);
if ($result > 0) {
$newproduct = $existingProduct;
} else {
$existingProduct = false;
$newproduct = clone $product;
$newproduct->ref = $forced_refvar;
}
} else {
$forced_refvar = false;
$existingProduct = false;
$newproduct = clone $product;
}
//Final weight impact
$weight_impact = $forced_weightvar;
@ -536,7 +553,6 @@ WHERE c.fk_product_parent = ".(int) $productid." AND p.tosell = 1";
$newcomb = $existingCombination;
} else {
$newcomb->fk_product_parent = $product->id;
if ($newcomb->create($user) < 0) { // Create 1 entry into product_attribute_combination (1 entry for all combinations)
$db->rollback();
return -1;
@ -573,10 +589,12 @@ WHERE c.fk_product_parent = ".(int) $productid." AND p.tosell = 1";
$price_impact += (float) price2num($variations[$currcombattr][$currcombval]['price']);
}
if (isset($conf->global->PRODUIT_ATTRIBUTES_SEPARATOR)) {
$newproduct->ref .= $conf->global->PRODUIT_ATTRIBUTES_SEPARATOR . $prodattrval->ref;
} else {
$newproduct->ref .= '_'.$prodattrval->ref;
if ($forced_refvar === false) {
if (isset($conf->global->PRODUIT_ATTRIBUTES_SEPARATOR)) {
$newproduct->ref .= $conf->global->PRODUIT_ATTRIBUTES_SEPARATOR . $prodattrval->ref;
} else {
$newproduct->ref .= '_'.$prodattrval->ref;
}
}
//The first one should not contain a linebreak
@ -592,58 +610,65 @@ WHERE c.fk_product_parent = ".(int) $productid." AND p.tosell = 1";
$newproduct->weight += $weight_impact;
//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;
// Now create the product
//print 'Create prod '.$newproduct->ref.'<br>'."\n";
$newprodid = $newproduct->create($user);
if ($newprodid < 0)
{
//In case the error is not related with an already existing product
if ($newproduct->error != 'ErrorProductAlreadyExists') {
$this->error[] = $newproduct->error;
$this->errors = $newproduct->errors;
$db->rollback();
return -1;
}
if ($existingProduct === false) {
//To avoid wrong information in price history log
$newproduct->price = 0;
$newproduct->price_ttc = 0;
$newproduct->price_min = 0;
$newproduct->price_min_ttc = 0;
/**
* If there is an existing combination, then we update the prices and weight
* Otherwise, we try adding a random number to the ref
*/
// A new variant must use a new barcode (not same product)
$newproduct->barcode = -1;
$result = $newproduct->create($user);
if ($newcomb->fk_product_child) {
$res = $newproduct->fetch($existingCombination->fk_product_child);
} else {
$orig_prod_ref = $newproduct->ref;
$i = 1;
if ($result < 0)
{
//In case the error is not related with an already existing product
if ($newproduct->error != 'ErrorProductAlreadyExists') {
$this->error[] = $newproduct->error;
$this->errors = $newproduct->errors;
$db->rollback();
return -1;
}
do {
$newproduct->ref = $orig_prod_ref.$i;
$res = $newproduct->create($user);
/**
* If there is an existing combination, then we update the prices and weight
* Otherwise, we try adding a random number to the ref
*/
if ($newproduct->error != 'ErrorProductAlreadyExists') {
$this->errors[] = $newproduct->error;
break;
}
if ($newcomb->fk_product_child) {
$res = $newproduct->fetch($existingCombination->fk_product_child);
} else {
$orig_prod_ref = $newproduct->ref;
$i = 1;
$i++;
} while ($res < 0);
}
do {
$newproduct->ref = $orig_prod_ref.$i;
$res = $newproduct->create($user);
if ($res < 0) {
$db->rollback();
return -1;
}
if ($newproduct->error != 'ErrorProductAlreadyExists') {
$this->errors[] = $newproduct->error;
break;
}
$newproduct->weight += $weight_impact;
$i++;
} while ($res < 0);
}
if ($res < 0) {
$db->rollback();
return -1;
}
}
} else {
$result = $newproduct->update($newproduct->id, $user);
if ($result < 0)
{
$db->rollback();
return -1;
}
}
$newcomb->fk_product_child = $newproduct->id;

View File

@ -33,6 +33,7 @@ $ref = GETPOST('ref', 'alpha');
$weight_impact = GETPOST('weight_impact', 'alpha');
$price_impact = GETPOST('price_impact', 'alpha');
$price_impact_percent = (bool) GETPOST('price_impact_percent');
$reference = GETPOST('reference', 'alpha');
$form = new Form($db);
$action = GETPOST('action', 'alpha');
@ -41,6 +42,7 @@ $show_files = GETPOST('show_files', 'int');
$confirm = GETPOST('confirm', 'alpha');
$toselect = GETPOST('toselect', 'array');
$cancel = GETPOST('cancel', 'alpha');
$delete_product = GETPOST('delete_product', 'alpha');
// Security check
$fieldvalue = (!empty($id) ? $id : $ref);
@ -106,6 +108,10 @@ if ($_POST) {
}
else
{
$reference = trim($reference);
if (empty($reference)) {
$reference = false;
}
$weight_impact = price2num($weight_impact);
$price_impact = price2num($price_impact);
$sanit_features = array();
@ -141,7 +147,7 @@ if ($_POST) {
if (!$prodcomb->fetchByProductCombination2ValuePairs($id, $sanit_features))
{
$result = $prodcomb->createProductCombination($user, $object, $sanit_features, array(), $price_impact_percent, $price_impact, $weight_impact);
$result = $prodcomb->createProductCombination($user, $object, $sanit_features, array(), $price_impact_percent, $price_impact, $weight_impact, $reference);
if ($result > 0)
{
setEventMessages($langs->trans('RecordSaved'), null, 'mesgs');
@ -242,7 +248,7 @@ if ($action === 'confirm_deletecombination') {
if ($prodcomb->fetch($valueid) > 0) {
$db->begin();
if ($prodcomb->delete($user) > 0 && $prodstatic->fetch($prodcomb->fk_product_child) > 0 && $prodstatic->delete($user) > 0) {
if ($prodcomb->delete($user) > 0 && (empty($delete_product) || ($delete_product == 'on' && $prodstatic->fetch($prodcomb->fk_product_child) > 0 && $prodstatic->delete($user) > 0))) {
$db->commit();
setEventMessages($langs->trans('RecordSaved'), null, 'mesgs');
header('Location: '.dol_buildpath('/variants/combinations.php?id='.$object->id, 2));
@ -590,6 +596,10 @@ if (!empty($id) || !empty($ref))
<td>
</td>
</tr>
<tr>
<td><label for="reference"><?php echo $langs->trans('Reference') ?></label></td>
<td><input type="text" id="reference" name="reference" value="<?php echo trim($reference) ?>"></td>
</tr>
<tr>
<td><label for="price_impact"><?php echo $langs->trans('PriceImpact') ?></label></td>
<td><input type="text" id="price_impact" name="price_impact" value="<?php echo price($price_impact) ?>">
@ -629,7 +639,7 @@ if (!empty($id) || !empty($ref))
$langs->trans('Delete'),
$langs->trans('ProductCombinationDeleteDialog', $prodstatic->ref),
"confirm_deletecombination",
'',
array(array('label'=> $langs->trans('DeleteLinkedProduct'),'type'=> 'checkbox', 'name' => 'delete_product', 'value' => false)),
0,
1
);