dolibarr 23.0.4
products.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2006 Andre Cianfarani <acianfa@free.fr>
3 * Copyright (C) 2005-2013 Regis Houssin <regis.houssin@inodbox.com>
4 * Copyright (C) 2007-2011 Laurent Destailleur <eldy@users.sourceforge.net>
5 * Copyright (C) 2020 Josep Lluís Amador <joseplluis@lliuretic.cat>
6 * Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
7 * Copyright (C) 2025 MDW <mdeweerd@users.noreply.github.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 */
22
28if (!defined('NOTOKENRENEWAL')) {
29 define('NOTOKENRENEWAL', 1); // Disables token renewal
30}
31if (!defined('NOREQUIREMENU')) {
32 define('NOREQUIREMENU', '1');
33}
34if (!defined('NOREQUIREHTML')) {
35 define('NOREQUIREHTML', '1');
36}
37if (!defined('NOREQUIREAJAX')) {
38 define('NOREQUIREAJAX', '1');
39}
40if (empty($_GET['keysearch']) && !defined('NOREQUIREHTML')) { // Keep $_GET here, GETPOST is not yet defined
41 define('NOREQUIREHTML', '1');
42}
43
44// Load Dolibarr environment
45require '../../main.inc.php';
46
56$htmlname = GETPOST('htmlname', 'aZ09');
57$socid = GETPOSTINT('socid');
58// type can be empty string or 0 or 1
59$type = GETPOST('type', 'int');
60$mode = GETPOSTINT('mode');
61$status = ((GETPOSTINT('status') >= 0) ? GETPOSTINT('status') : - 1); // status buy when mode = customer , status purchase when mode = supplier
62$status_purchase = ((GETPOSTINT('status_purchase') >= 0) ? GETPOSTINT('status_purchase') : - 1); // status purchase when mode = customer
63$outjson = (GETPOSTINT('outjson') ? GETPOSTINT('outjson') : 0);
64$price_level = GETPOSTINT('price_level');
65$action = GETPOST('action', 'aZ09');
66$id = GETPOSTINT('id');
67$price_by_qty_rowid = GETPOSTINT('pbq');
68$finished = GETPOSTINT('finished');
69$alsoproductwithnosupplierprice = GETPOSTINT('alsoproductwithnosupplierprice');
70$warehouseStatus = GETPOST('warehousestatus', 'alpha');
71$hidepriceinlabel = GETPOSTINT('hidepriceinlabel');
72$warehouseId = GETPOSTINT('warehouseid');
73
74// Security check
75restrictedArea($user, 'produit|service|commande|propal|facture', 0, 'product&product');
76
77/*
78 * Actions
79 */
80
81// None
82
83
84/*
85 * View
86 */
87
88// print '<!-- Ajax page called with url '.dol_escape_htmltag($_SERVER["PHP_SELF"]).'?'.dol_escape_htmltag($_SERVER["QUERY_STRING"]).' -->'."\n";
89
90if ($action == 'fetch' && !empty($id)) {
91 // action='fetch' is used to get product information on a product. So when action='fetch', id must be the product id.
92 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
93 require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
94
95 top_httphead('application/json');
96
97 $outjson = array();
98
99 $object = new Product($db);
100 $ret = $object->fetch($id);
101 if ($ret > 0) {
102 $outref = $object->ref;
103 $outlabel = $object->label;
104 $outlabel_trans = '';
105 $default_unit = $object->fk_unit;
106 $outdesc = $object->description;
107 $outdesc_trans = '';
108 $outtype = $object->type;
109 $outprice_ht = null;
110 $outprice_ttc = null;
111 $outpricebasetype = null;
112 $outtva_tx_formated = 0;
113 $outtva_tx = 0;
114 $outdefault_vat_code = '';
115 $outqty = 1;
116 $outdiscount = 0;
117 $mandatory_period = $object->mandatory_period;
118 $found = false;
119
120 $price_level = 1;
121 if ($socid > 0) {
122 $needchangeaccordingtothirdparty = 0;
123 if (getDolGlobalInt('MAIN_MULTILANGS') && getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE')) {
124 $needchangeaccordingtothirdparty = 1;
125 }
126 if (getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
127 $needchangeaccordingtothirdparty = 1;
128 }
129 if ($needchangeaccordingtothirdparty) {
130 $thirdpartytemp = new Societe($db);
131 $thirdpartytemp->fetch($socid);
132
133 //Load translation description and label according to thirdparty language
134 if (getDolGlobalInt('MAIN_MULTILANGS') && getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE')) {
135 $newlang = $thirdpartytemp->default_lang;
136
137 if (!empty($newlang)) {
138 $outputlangs = new Translate("", $conf);
139 $outputlangs->setDefaultLang($newlang);
140 $outdesc_trans = (!empty($object->multilangs[$outputlangs->defaultlang]["description"])) ? $object->multilangs[$outputlangs->defaultlang]["description"] : $object->description;
141 $outlabel_trans = (!empty($object->multilangs[$outputlangs->defaultlang]["label"])) ? $object->multilangs[$outputlangs->defaultlang]["label"] : $object->label;
142 } else {
143 $outdesc_trans = $object->description;
144 $outlabel_trans = $object->label;
145 }
146 }
147
148 //Set price level according to thirdparty
149 if (getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
150 $price_level = $thirdpartytemp->price_level;
151 }
152 }
153 }
154
155 // Price by qty
156 if (!empty($price_by_qty_rowid) && $price_by_qty_rowid >= 1 && (getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES'))) { // If we need a particular price related to qty
157 $sql = "SELECT price, unitprice, quantity, remise_percent";
158 $sql .= " FROM ".MAIN_DB_PREFIX."product_price_by_qty";
159 $sql .= " WHERE rowid = ".((int) $price_by_qty_rowid);
160 // The price by quantity must be a price of the product asked, not the one of another product
161 $sql .= " AND fk_product_price IN (SELECT pp.rowid FROM ".MAIN_DB_PREFIX."product_price as pp WHERE pp.fk_product = ".((int) $id).")";
162
163 $result = $db->query($sql);
164 if ($result) {
165 $objp = $db->fetch_object($result);
166 if ($objp) {
167 $found = true;
168 $outprice_ht = price($objp->unitprice);
169 $outprice_ttc = price($objp->unitprice * (1 + ($object->tva_tx / 100)));
170
171 $outpricebasetype = $object->price_base_type;
172 $outtva_tx_formated = price($object->tva_tx);
173 $outtva_tx = price2num($object->tva_tx);
174 $outdefault_vat_code = $object->default_vat_code;
175
176 $outqty = $objp->quantity;
177 $outdiscount = $objp->remise_percent;
178 }
179 }
180 }
181
182 // Multiprice (1 price per level)
183 if (!$found && isset($price_level) && $price_level >= 1 && (getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES'))) { // If we need a particular price level (from 1 to 6)
184 $sql = "SELECT price, price_ttc, price_base_type,";
185 $sql .= " tva_tx, default_vat_code"; // Vat rate and code will be used if PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL is on.
186 $sql .= " FROM ".MAIN_DB_PREFIX."product_price ";
187 $sql .= " WHERE fk_product = ".((int) $id);
188 $sql .= " AND entity IN (".getEntity('productprice').")";
189 $sql .= " AND price_level = ".((int) $price_level);
190 $sql .= " ORDER BY date_price DESC, rowid";
191 $sql .= " DESC LIMIT 1";
192
193 $result = $db->query($sql);
194 if ($result) {
195 $objp = $db->fetch_object($result);
196 if ($objp) {
197 $found = true;
198 $outprice_ht = price($objp->price); // formatted for language user because is inserted into input field
199 $outprice_ttc = price($objp->price_ttc); // formatted for language user because is inserted into input field
200 $outpricebasetype = $objp->price_base_type;
201 if (getDolGlobalString('PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL')) {
202 $outtva_tx_formated = price($objp->tva_tx); // formatted for language user because is inserted into input field
203 $outtva_tx = price2num($objp->tva_tx); // international numeric
204 $outdefault_vat_code = $objp->default_vat_code;
205 } else {
206 // The common and default behaviour.
207 $outtva_tx_formated = price($object->tva_tx);
208 $outtva_tx = price2num($object->tva_tx);
209 $outdefault_vat_code = $object->default_vat_code;
210 }
211 }
212 }
213 }
214
215 // Price by customer
216 if ((getDolGlobalString('PRODUIT_CUSTOMER_PRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) && !empty($socid)) {
217 require_once DOL_DOCUMENT_ROOT.'/product/class/productcustomerprice.class.php';
218
219 $prodcustprice = new ProductCustomerPrice($db);
220
221 $filter = array('t.fk_product' => (string) $object->id, 't.fk_soc' => (string) $socid);
222
223 $result = $prodcustprice->fetchAll('', '', 0, 0, $filter);
224 if ($result) {
225 if (count($prodcustprice->lines) > 0) {
226 $date_now = (int) floor(dol_now() / 86400) * 86400; // date without hours
227 foreach ($prodcustprice->lines as $k => $custprice_line) {
228 if ($custprice_line->date_begin <= $date_now && (empty($custprice_line->date_end) || $date_now <= $custprice_line->date_end)) {
229 $found = true;
230 $outprice_ht = price($custprice_line->price);
231 $outprice_ttc = price($custprice_line->price_ttc);
232 $outpricebasetype = $custprice_line->price_base_type;
233 $outtva_tx_formated = price($custprice_line->tva_tx);
234 $outtva_tx = price2num($custprice_line->tva_tx);
235 $outdefault_vat_code = $custprice_line->default_vat_code;
236 $outdiscount = $custprice_line->discount_percent;
237 break;
238 }
239 }
240 }
241 }
242 }
243
244 if (!$found) {
245 $outprice_ht = price($object->price);
246 $outprice_ttc = price($object->price_ttc);
247 $outpricebasetype = $object->price_base_type;
248 $outtva_tx_formated = price($object->tva_tx);
249 $outtva_tx = price2num($object->tva_tx);
250 $outdefault_vat_code = $object->default_vat_code;
251
252 // Variant rows are kept in llx_product without their own price/vat
253 // information - the parent product holds them. If this product is a
254 // variant child and its vat fields are empty, inherit them from the
255 // parent so the sales/order line gets the default tax preselected
256 // like the non-variant flow does (#30400).
257 if (empty($outtva_tx) && empty($outdefault_vat_code)) {
258 require_once DOL_DOCUMENT_ROOT.'/variants/class/ProductCombination.class.php';
259 $combination = new ProductCombination($db);
260 if ($combination->fetchByFkProductChild($object->id, 1) > 0) {
261 $parent = new Product($db);
262 if ($parent->fetch($combination->fk_product_parent) > 0) {
263 $outtva_tx_formated = price($parent->tva_tx);
264 $outtva_tx = price2num($parent->tva_tx);
265 $outdefault_vat_code = $parent->default_vat_code;
266 $product_outtva_tx_formated = $outtva_tx_formated;
267 $product_outtva_tx = $outtva_tx;
268 $product_outdefault_vat_code = $outdefault_vat_code;
269 }
270 }
271 }
272 }
273
274 // VAT to use and default VAT for product are set to same value by default
275 $product_outtva_tx_formated = $outtva_tx_formated;
276 $product_outtva_tx = $outtva_tx;
277 $product_outdefault_vat_code = $outdefault_vat_code;
278
279 // If we ask the price according to buyer, we change it.
280 if (GETPOSTINT('addalsovatforthirdpartyid')) {
281 $thirdparty_buyer = new Societe($db);
282 $thirdparty_buyer->fetch($socid);
283
284 $tmpvatwithcode = get_default_tva($mysoc, $thirdparty_buyer, $id, 0);
285
286 if (!is_numeric($tmpvatwithcode) || $tmpvatwithcode != -1) {
287 $reg = array();
288 if (preg_match('/(.+)\s\‍((.+)\‍)/', $tmpvatwithcode, $reg)) {
289 $outtva_tx = price2num($reg[1]);
290 $outtva_tx_formated = price($outtva_tx);
291 $outdefault_vat_code = $reg[2];
292 } else {
293 $outtva_tx = price2num($tmpvatwithcode);
294 $outtva_tx_formated = price($outtva_tx);
295 $outdefault_vat_code = '';
296 }
297 }
298 }
299
300 $outjson = array(
301 'ref' => $outref,
302 'label' => $outlabel,
303 'label_trans' => $outlabel_trans,
304 'desc' => $outdesc,
305 'desc_trans' => $outdesc_trans,
306 'type' => $outtype,
307 'price_ht' => $outprice_ht,
308 'price_ttc' => $outprice_ttc,
309 'pricebasetype' => $outpricebasetype,
310 'product_tva_tx_formated' => $product_outtva_tx_formated,
311 'product_tva_tx' => $product_outtva_tx,
312 'product_default_vat_code' => $product_outdefault_vat_code,
313
314 'tva_tx_formated' => $outtva_tx_formated,
315 'tva_tx' => $outtva_tx,
316 'default_vat_code' => $outdefault_vat_code,
317
318 'qty' => $outqty,
319 'discount' => $outdiscount,
320 'mandatory_period' => $mandatory_period,
321
322 'array_options' => $object->array_options,
323
324 'default_unit' => $default_unit
325 );
326 }
327
328 echo json_encode($outjson);
329} else {
330 require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
331
332 $langs->loadLangs(array("main", "products"));
333
334 top_httphead();
335
336 if (empty($htmlname)) {
337 print json_encode(array());
338 return;
339 }
340
341 // Filter on the product to search can be:
342 // Into an array with key $htmlname123 (we take first one found). Which page use this ?
343 // Into a var with name $htmlname can be 'prodid', 'productid', ...
344 $match = preg_grep('/('.preg_quote($htmlname, '/').'[0-9]+)/', array_keys($_GET));
345 sort($match);
346
347 $idprod = (empty($match[0]) ? '' : $match[0]); // Take first key found into GET array with matching $htmlname123
348
349 if (GETPOST($htmlname, 'alpha') == '' && (!$idprod || !GETPOST($idprod, 'alpha'))) {
350 print json_encode(array());
351 return;
352 }
353
354 // When used from jQuery, the search term is added as GET param "term".
355 $searchkey = (($idprod && GETPOST($idprod, 'alpha')) ? GETPOST($idprod, 'alpha') : (GETPOST($htmlname, 'alpha') ? GETPOST($htmlname, 'alpha') : ''));
356
357 if (!isset($form) || !is_object($form)) {
358 $form = new Form($db);
359 }
360
361 $arrayresult = [];
362 if (empty($mode) || $mode == 1) { // mode=1: customer
363 $arrayresult = $form->select_produits_list(0, $htmlname, $type, getDolGlobalInt('PRODUIT_LIMIT_SIZE', 1000), $price_level, $searchkey, $status, $finished, $outjson, $socid, '1', 0, '', $hidepriceinlabel, $warehouseStatus, $status_purchase, $warehouseId);
364 } elseif ($mode == 2) { // mode=2: supplier
365 $arrayresult = $form->select_produits_fournisseurs_list($socid, "", $htmlname, $type, "", $searchkey, $status, $outjson, getDolGlobalInt('PRODUIT_LIMIT_SIZE', 1000), $alsoproductwithnosupplierprice, '', getDolGlobalInt('SUPPLIER_SHOW_STOCK_IN_PRODUCTS_COMBO'));
366 }
367
368 $db->close();
369
370 if ($outjson) {
371 print json_encode($arrayresult);
372 }
373}
$id
Support class for third parties, contacts, members, users or resources.
Definition account.php:47
if(! $sortfield) if(! $sortorder) $object
Definition account.php:100
Class to manage generation of HTML components Only common components must be here.
Class ProductCombination Used to represent the relation between a product and one of its variants.
File of class to manage predefined price products or services by customer.
Class to manage products or services.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage translations.
global $mysoc
print $script_file $mode $langs defaultlang(is_numeric($duration_value) ? " delay=". $duration_value :"").(is_numeric($duration_value2) ? " after cd cd cd description as description
Only used if Module[ID]Desc translation string is not found.
dol_now($mode='gmt')
Return date for now.
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Function that return vat rate of a product line (according to seller, buyer and product vat rate) VAT...
if(!defined( 'NOREQUIREMENU')) if(!empty(GETPOST('seteventmessages', 'alpha'))) if(!function_exists("llxHeader")) top_httphead($contenttype='text/html', $forcenocache=0)
Show HTTP header.
restrictedArea(User $user, $features, $object=0, $tableandshare='', $feature2='', $dbt_keyfield='fk_soc', $dbt_select='rowid', $isdraft=0, $mode=0)
Check permissions of a user to show a page and an object.