diff --git a/htdocs/core/class/html.form.class.php b/htdocs/core/class/html.form.class.php
index bada8627ee0..593d4100c22 100644
--- a/htdocs/core/class/html.form.class.php
+++ b/htdocs/core/class/html.form.class.php
@@ -77,7 +77,7 @@ class Form
* @param string $preselected Name of Value to show/edit (not used in this function)
* @param object $object Object
* @param boolean $perm Permission to allow button to edit parameter. Set it to 0 to have a not edited field.
- * @param string $typeofdata Type of data ('string' by default, 'email', 'numeric:99', 'text' or 'textarea:rows:cols', 'day' or 'datepicker', 'ckeditor:dolibarr_zzz:width:height:savemethod:1:rows:cols', 'select;xxx[:class]'...)
+ * @param string $typeofdata Type of data ('string' by default, 'email', 'amount:99', 'numeric:99', 'text' or 'textarea:rows:cols', 'day' or 'datepicker', 'ckeditor:dolibarr_zzz:width:height:savemethod:1:rows:cols', 'select;xxx[:class]'...)
* @param string $moreparam More param to add on a href URL
* @return string HTML edit field
*/
@@ -122,7 +122,7 @@ class Form
* @param string $value Value to show/edit
* @param object $object Object
* @param boolean $perm Permission to allow button to edit parameter
- * @param string $typeofdata Type of data ('string' by default, 'amount', 'email', 'numeric:99', 'text' or 'textarea:rows:cols', 'day' or 'datepicker', 'ckeditor:dolibarr_zzz:width:height:savemethod:toolbarstartexpanded:rows:cols', 'select:xxx'...)
+ * @param string $typeofdata Type of data ('string' by default, 'email', 'amount:99', 'numeric:99', 'text' or 'textarea:rows:cols', 'day' or 'datepicker', 'ckeditor:dolibarr_zzz:width:height:savemethod:toolbarstartexpanded:rows:cols', 'select:xxx'...)
* @param string $editvalue When in edit mode, use this value as $value instead of value (for example, you can provide here a formated price instead of value). Use '' to use same than $value
* @param object $extObject External object
* @param mixed $custommsg String or Array of custom messages : eg array('success' => 'MyMessage', 'error' => 'MyMessage')
@@ -154,11 +154,16 @@ class Form
$ret.='';
$ret.='
';
$ret.='
';
- if (preg_match('/^(string|email|numeric|amount)/',$typeofdata))
+ if (preg_match('/^(string|email)/',$typeofdata))
{
$tmp=explode(':',$typeofdata);
$ret.='';
}
+ else if (preg_match('/^(numeric|amount)/',$typeofdata))
+ {
+ $tmp=explode(':',$typeofdata);
+ $ret.='';
+ }
else if (preg_match('/^text/',$typeofdata) || preg_match('/^note/',$typeofdata))
{
$tmp=explode(':',$typeofdata);
@@ -203,8 +208,8 @@ class Form
}
else
{
- if ($typeofdata == 'email') $ret.=dol_print_email($value,0,0,0,0,1);
- elseif ($typeofdata == 'amount') $ret.=($value != '' ? price($value,'',$langs,0,-1,-1,$conf->currency) : '');
+ if (preg_match('/^(email)/',$typeofdata)) $ret.=dol_print_email($value,0,0,0,0,1);
+ elseif (preg_match('/^(amount|numeric)/',$typeofdata)) $ret.=($value != '' ? price($value,'',$langs,0,-1,-1,$conf->currency) : '');
elseif (preg_match('/^text/',$typeofdata) || preg_match('/^note/',$typeofdata)) $ret.=dol_htmlentitiesbr($value);
elseif ($typeofdata == 'day' || $typeofdata == 'datepicker') $ret.=dol_print_date($value,'day');
elseif ($typeofdata == 'dayhour' || $typeofdata == 'datehourpicker') $ret.=dol_print_date($value,'dayhour');
diff --git a/htdocs/core/lib/product.lib.php b/htdocs/core/lib/product.lib.php
index 5ca0389455f..73c74cfb97e 100644
--- a/htdocs/core/lib/product.lib.php
+++ b/htdocs/core/lib/product.lib.php
@@ -1,5 +1,5 @@
+/* Copyright (C) 2006-2015 Laurent Destailleur
* Copyright (C) 2007 Rodolphe Quiedeville
* Copyright (C) 2009-2010 Regis Houssin
* Copyright (C) 2015 Raphaƫl Doursenaud
@@ -46,14 +46,16 @@ function product_prepare_head($object)
$h++;
$head[$h][0] = DOL_URL_ROOT."/product/price.php?id=".$object->id;
- $head[$h][1] = $langs->trans("CustomerPrices");
+ $head[$h][1] = $langs->trans("SellingPrices");
$head[$h][2] = 'price';
$h++;
- if (! empty($conf->fournisseur->enabled) && $user->rights->fournisseur->lire)
+ if ((! empty($conf->fournisseur->enabled) && $user->rights->fournisseur->lire)
+ || (! empty($conf->margin->enabled) && $user->rights->margin->liretous)
+ )
{
$head[$h][0] = DOL_URL_ROOT."/product/fournisseurs.php?id=".$object->id;
- $head[$h][1] = $langs->trans("SuppliersPrices");
+ $head[$h][1] = $langs->trans("BuyingPrices");
$head[$h][2] = 'suppliers';
$h++;
}
diff --git a/htdocs/fourn/class/fournisseur.product.class.php b/htdocs/fourn/class/fournisseur.product.class.php
index d7344787623..424f9ad65d4 100644
--- a/htdocs/fourn/class/fournisseur.product.class.php
+++ b/htdocs/fourn/class/fournisseur.product.class.php
@@ -353,7 +353,7 @@ class ProductFournisseur extends Product
function fetch_product_fournisseur_price($rowid, $ignore_expression = 0)
{
$sql = "SELECT pfp.rowid, pfp.price, pfp.quantity, pfp.unitprice, pfp.remise_percent, pfp.remise, pfp.tva_tx, pfp.fk_availability,";
- $sql.= " pfp.fk_soc, pfp.ref_fourn, pfp.fk_product, pfp.charges, pfp.unitcharges, pfp.fk_supplier_price_expression, pfp.delivery_time_days"; // , pfp.recuperableonly as fourn_tva_npr"; FIXME this field not exist in llx_product_fournisseur_price
+ $sql.= " pfp.fk_soc, pfp.ref_fourn, pfp.fk_product, pfp.charges, pfp.cost_price, pfp.unitcharges, pfp.fk_supplier_price_expression, pfp.delivery_time_days"; // , pfp.recuperableonly as fourn_tva_npr"; FIXME this field not exist in llx_product_fournisseur_price
$sql.= " FROM ".MAIN_DB_PREFIX."product_fournisseur_price as pfp";
$sql.= " WHERE pfp.rowid = ".$rowid;
@@ -368,12 +368,12 @@ class ProductFournisseur extends Product
$this->fourn_ref = $obj->ref_fourn; // deprecated
$this->ref_supplier = $obj->ref_fourn;
$this->fourn_price = $obj->price;
- $this->fourn_charges = $obj->charges;
+ $this->fourn_charges = $obj->charges; // deprecated
$this->fourn_qty = $obj->quantity;
$this->fourn_remise_percent = $obj->remise_percent;
$this->fourn_remise = $obj->remise;
$this->fourn_unitprice = $obj->unitprice;
- $this->fourn_unitcharges = $obj->unitcharges;
+ $this->fourn_unitcharges = $obj->unitcharges; // deprecated
$this->fourn_tva_tx = $obj->tva_tx;
$this->product_id = $obj->fk_product; // deprecated
$this->fk_product = $obj->fk_product;
@@ -456,9 +456,9 @@ class ProductFournisseur extends Product
$prodfourn->fourn_qty = $record["quantity"];
$prodfourn->fourn_remise_percent = $record["remise_percent"];
$prodfourn->fourn_remise = $record["remise"];
- $prodfourn->fourn_unitprice = $record["unitprice"];
- $prodfourn->fourn_charges = $record["charges"];
- $prodfourn->fourn_unitcharges = $record["unitcharges"];
+ $prodfourn->fourn_unitprice = $record["unitprice"];
+ $prodfourn->fourn_charges = $record["charges"]; // deprecated
+ $prodfourn->fourn_unitcharges = $record["unitcharges"]; // deprecated
$prodfourn->fourn_tva_tx = $record["tva_tx"];
$prodfourn->fourn_id = $record["fourn_id"];
$prodfourn->fourn_name = $record["supplier_name"];
@@ -591,8 +591,8 @@ class ProductFournisseur extends Product
$this->fourn_remise_percent = $record["remise_percent"];
$this->fourn_remise = $record["remise"];
$this->fourn_unitprice = $fourn_unitprice;
- $this->fourn_charges = $record["charges"];
- $this->fourn_unitcharges = $record["unitcharges"];
+ $this->fourn_charges = $record["charges"]; // deprecated
+ $this->fourn_unitcharges = $record["unitcharges"]; // deprecated
$this->fourn_tva_tx = $record["tva_tx"];
$this->fourn_id = $record["fourn_id"];
$this->fourn_name = $record["supplier_name"];
diff --git a/htdocs/install/mysql/migration/3.8.0-3.9.0.sql b/htdocs/install/mysql/migration/3.8.0-3.9.0.sql
index 99a042c8c31..1a0bb08d44c 100755
--- a/htdocs/install/mysql/migration/3.8.0-3.9.0.sql
+++ b/htdocs/install/mysql/migration/3.8.0-3.9.0.sql
@@ -97,6 +97,8 @@ ALTER TABLE llx_commande ADD COLUMN fk_warehouse integer DEFAULT NULL AFTER fk_s
ALTER TABLE llx_commande_fournisseur ADD COLUMN billed smallint DEFAULT 0 AFTER fk_statut;
ALTER TABLE llx_commande_fournisseur ADD INDEX billed (billed);
+ALTER TABLE llx_product ADD COLUMN cost_price double(24,8) DEFAULT NULL;
+
ALTER TABLE llx_ecm_directories MODIFY COLUMN fullpath varchar(750);
ALTER TABLE llx_ecm_directories DROP INDEX idx_ecm_directories;
ALTER TABLE llx_ecm_directories ADD UNIQUE INDEX uk_ecm_directories (label, fk_parent, entity);
diff --git a/htdocs/install/mysql/tables/llx_product.sql b/htdocs/install/mysql/tables/llx_product.sql
index e34ae887ffc..570bc651b2a 100755
--- a/htdocs/install/mysql/tables/llx_product.sql
+++ b/htdocs/install/mysql/tables/llx_product.sql
@@ -44,8 +44,9 @@ create table llx_product
price_min double(24,8) DEFAULT 0,
price_min_ttc double(24,8) DEFAULT 0,
price_base_type varchar(3) DEFAULT 'HT',
- tva_tx double(6,3), -- Default VAT rate of product
- recuperableonly integer NOT NULL DEFAULT '0', -- French NPR VAT
+ cost_price double(24,8) DEFAULT NULL, -- Cost price without tax. Can be used for margin calculation.
+ tva_tx double(6,3), -- Default VAT rate of product
+ recuperableonly integer NOT NULL DEFAULT '0', -- French NPR VAT
localtax1_tx double(6,3) DEFAULT 0, -- Spanish local VAT 1
localtax2_tx double(6,3) DEFAULT 0, -- Spanish local VAT 2
fk_user_author integer DEFAULT NULL, -- user making creation
diff --git a/htdocs/install/mysql/tables/llx_product_fournisseur_price.sql b/htdocs/install/mysql/tables/llx_product_fournisseur_price.sql
index fb5184715d1..e7cf555d24e 100755
--- a/htdocs/install/mysql/tables/llx_product_fournisseur_price.sql
+++ b/htdocs/install/mysql/tables/llx_product_fournisseur_price.sql
@@ -34,12 +34,12 @@ create table llx_product_fournisseur_price
remise_percent double NOT NULL DEFAULT 0,
remise double NOT NULL DEFAULT 0,
unitprice double(24,8) DEFAULT 0,
- charges double(24,8) DEFAULT 0,
- unitcharges double(24,8) DEFAULT 0,
+ charges double(24,8) DEFAULT 0, -- to store transport cost. Constant PRODUCT_CHARGES must be set to see it.
+ unitcharges double(24,8) DEFAULT 0, -- deprecated
tva_tx double(6,3) NOT NULL,
info_bits integer NOT NULL DEFAULT 0,
fk_user integer,
- fk_supplier_price_expression integer, -- Link to the rule for dynamic price calculation
- import_key varchar(14), -- Import key
+ fk_supplier_price_expression integer, -- Link to the rule for dynamic price calculation
+ import_key varchar(14), -- Import key
delivery_time_days integer
)ENGINE=innodb;
diff --git a/htdocs/langs/en_US/products.lang b/htdocs/langs/en_US/products.lang
index ddda0152245..b74db95d722 100644
--- a/htdocs/langs/en_US/products.lang
+++ b/htdocs/langs/en_US/products.lang
@@ -76,6 +76,8 @@ SellingPriceHT=Selling price (net of tax)
SellingPriceTTC=Selling price (inc. tax)
PublicPrice=Public price
CurrentPrice=Current price
+CostPriceDescription=This price (net of tax) can be used to store the average amount this product cost to your company. It may be any price you calculate yourself, for example from the average buying price plus average transportation and acquiring cost.
+CostPriceUsage=In a future version, this value could be used for margin calculation.
NewPrice=New price
MinPrice=Min. selling price
MinPriceHT=Min. selling price (net of tax)
@@ -190,6 +192,8 @@ ClonePricesProduct=Clone main informations and prices
CloneCompositionProduct=Clone packaged product/service
ProductIsUsed=This product is used
NewRefForClone=Ref. of new product/service
+SellingPrices=Selling prices
+BuyingPrices=Buying prices
CustomerPrices=Customer prices
SuppliersPrices=Supplier prices
SuppliersPricesOfProductsOrServices=Supplier prices (of products or services)
diff --git a/htdocs/product/class/product.class.php b/htdocs/product/class/product.class.php
index aaf5aaf1baa..dcd889a0a77 100644
--- a/htdocs/product/class/product.class.php
+++ b/htdocs/product/class/product.class.php
@@ -1563,7 +1563,7 @@ class Product extends CommonObject
}
$sql = "SELECT rowid, ref, ref_ext, label, description, url, note, customcode, fk_country, price, price_ttc,";
- $sql.= " price_min, price_min_ttc, price_base_type, tva_tx, recuperableonly as tva_npr, localtax1_tx, localtax2_tx, tosell,";
+ $sql.= " price_min, price_min_ttc, price_base_type, cost_price, tva_tx, recuperableonly as tva_npr, localtax1_tx, localtax2_tx, tosell,";
$sql.= " tobuy, fk_product_type, duration, seuil_stock_alerte, canvas,";
$sql.= " weight, weight_units, length, length_units, surface, surface_units, volume, volume_units, barcode, fk_barcode_type, finished,";
$sql.= " accountancy_code_buy, accountancy_code_sell, stock, pmp,";
@@ -1606,6 +1606,7 @@ class Product extends CommonObject
$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->tva_tx = $obj->tva_tx;
//! French VAT NPR
$this->tva_npr = $obj->tva_npr;
diff --git a/htdocs/product/fournisseurs.php b/htdocs/product/fournisseurs.php
index 96aa9aa6311..dab8d8dcf5d 100644
--- a/htdocs/product/fournisseurs.php
+++ b/htdocs/product/fournisseurs.php
@@ -46,6 +46,7 @@ $rowid=GETPOST('rowid','int');
$action=GETPOST('action', 'alpha');
$cancel=GETPOST('cancel', 'alpha');
$socid=GETPOST('socid', 'int');
+$cost_price=GETPOST('cost_price', 'int');
$backtopage=GETPOST('backtopage','alpha');
$error=0;
@@ -91,17 +92,37 @@ if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'e
if (empty($reshook))
{
+ if ($action == 'setcost_price')
+ {
+ if ($id)
+ {
+ $result=$object->fetch($id);
+ $result=$object->setValueFrom('cost_price', price2num($cost_price));
+ if ($result > 0)
+ {
+ $object->cost_price = price2num($cost_price);
+ setEventMessage($langs->trans("RecordSaved"));
+ }
+ else
+ {
+ $error++;
+ setEventMessages($object->error, $object->errors, 'errors');
+ }
+ }
+ $action='';
+ }
+
if ($action == 'remove_pf')
{
- if ($rowid)
+ if ($rowid) // id of product supplier price to remove
{
$action = '';
- $result=$product->remove_product_fournisseur_price($rowid);
+ $result=$object->remove_product_fournisseur_price($rowid);
if($result > 0){
setEventMessage($langs->trans("PriceRemoved"));
}else{
$error++;
- setEventMessages($product->error, $product->errors, 'errors');
+ setEventMessages($object->error, $object->errors, 'errors');
}
}
}
@@ -282,6 +303,17 @@ if ($id > 0 || $ref)
}
print '
';
+ // Cost price. Can be used for margin module for option "calculate margin on explicit cost price
+ // Accountancy sell code
+ print '