dolibarr 25.0.0-alpha
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';
55$htmlname = GETPOST('htmlname', 'aZ09');
56$socid = GETPOSTINT('socid');
57// type can be empty string or 0 or 1
58$type = GETPOST('type', 'int');
59$mode = GETPOSTINT('mode');
60$status = ((GETPOSTINT('status') >= 0) ? GETPOSTINT('status') : - 1); // status buy when mode = customer , status purchase when mode = supplier
61$status_purchase = ((GETPOSTINT('status_purchase') >= 0) ? GETPOSTINT('status_purchase') : - 1); // status purchase when mode = customer
62$outjson = (GETPOSTINT('outjson') ? GETPOSTINT('outjson') : 0);
63$price_level = GETPOSTINT('price_level');
64$action = GETPOST('action', 'aZ09');
65$id = GETPOSTINT('id');
66$price_by_qty_rowid = GETPOSTINT('pbq');
67$finished = GETPOSTINT('finished');
68$alsoproductwithnosupplierprice = GETPOSTINT('alsoproductwithnosupplierprice');
69$warehouseStatus = GETPOST('warehousestatus', 'alpha');
70$hidepriceinlabel = GETPOSTINT('hidepriceinlabel');
71$warehouseId = GETPOSTINT('warehouseid');
72
73// Security check
74restrictedArea($user, 'produit|service|commande|propal|facture', 0, 'product&product');
75
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 = null; // A discount on product price level is defined only for some price mode (like a price per customer 'PRODUIT_CUSTOMER_PRICES' or a price percustomer and quantity 'PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')
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
161 $result = $db->query($sql);
162 if ($result) {
163 $objp = $db->fetch_object($result);
164 if ($objp) {
165 $found = true;
166 $outprice_ht = price($objp->unitprice);
167 $outprice_ttc = price($objp->unitprice * (1 + ($object->tva_tx / 100)));
168
169 $outpricebasetype = $object->price_base_type;
170 $outtva_tx_formated = price($object->tva_tx);
171 $outtva_tx = price2num($object->tva_tx);
172 $outdefault_vat_code = $object->default_vat_code;
173
174 $outqty = $objp->quantity;
175 $outdiscount = $objp->remise_percent;
176 }
177 }
178 }
179
180 // Multiprice (1 price per level)
181 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)
182 $sql = "SELECT price, price_ttc, price_base_type,";
183 $sql .= " tva_tx, default_vat_code"; // Vat rate and code will be used if PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL is on.
184 $sql .= " FROM ".MAIN_DB_PREFIX."product_price ";
185 $sql .= " WHERE fk_product = ".((int) $id);
186 $sql .= " AND entity IN (".getEntity('productprice').")";
187 $sql .= " AND price_level = ".((int) $price_level);
188 $sql .= " ORDER BY date_price DESC, rowid";
189 $sql .= " DESC LIMIT 1";
190
191 $result = $db->query($sql);
192 if ($result) {
193 $objp = $db->fetch_object($result);
194 if ($objp) {
195 $found = true;
196 $outprice_ht = price($objp->price); // formatted for language user because is inserted into input field
197 $outprice_ttc = price($objp->price_ttc); // formatted for language user because is inserted into input field
198 $outpricebasetype = $objp->price_base_type;
199 if (getDolGlobalString('PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL')) {
200 $outtva_tx_formated = price($objp->tva_tx); // formatted for language user because is inserted into input field
201 $outtva_tx = price2num($objp->tva_tx); // international numeric
202 $outdefault_vat_code = $objp->default_vat_code;
203 } else {
204 // The common and default behaviour.
205 $outtva_tx_formated = price($object->tva_tx);
206 $outtva_tx = price2num($object->tva_tx);
207 $outdefault_vat_code = $object->default_vat_code;
208 }
209 }
210 }
211 }
212
213 // Price by customer
214 if ((getDolGlobalString('PRODUIT_CUSTOMER_PRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) && !empty($socid)) {
215 require_once DOL_DOCUMENT_ROOT.'/product/class/productcustomerprice.class.php';
216
217 $prodcustprice = new ProductCustomerPrice($db);
218
219 $filter = array('t.fk_product' => (string) $object->id, 't.fk_soc' => (string) $socid);
220
221 $result = $prodcustprice->fetchAll('', '', 0, 0, $filter);
222 if ($result) {
223 if (count($prodcustprice->lines) > 0) {
224 $date_now = (int) floor(dol_now() / 86400) * 86400; // date without hours
225 foreach ($prodcustprice->lines as $k => $custprice_line) {
226 if ($custprice_line->date_begin <= $date_now && (empty($custprice_line->date_end) || $date_now <= $custprice_line->date_end)) {
227 $found = true;
228 $outprice_ht = price($custprice_line->price);
229 $outprice_ttc = price($custprice_line->price_ttc);
230 $outpricebasetype = $custprice_line->price_base_type;
231 $outtva_tx_formated = price($custprice_line->tva_tx);
232 $outtva_tx = price2num($custprice_line->tva_tx);
233 $outdefault_vat_code = $custprice_line->default_vat_code;
234 $outdiscount = $custprice_line->discount_percent;
235 break;
236 }
237 }
238 }
239 }
240 }
241
242 if (!$found) {
243 $outprice_ht = price($object->price);
244 $outprice_ttc = price($object->price_ttc);
245 $outpricebasetype = $object->price_base_type;
246 $outtva_tx_formated = price($object->tva_tx);
247 $outtva_tx = price2num($object->tva_tx);
248 $outdefault_vat_code = $object->default_vat_code;
249 }
250
251 // VAT to use and default VAT for product are set to same value by default
252 $product_outtva_tx_formated = $outtva_tx_formated;
253 $product_outtva_tx = $outtva_tx;
254 $product_outdefault_vat_code = $outdefault_vat_code;
255
256 // If we ask the price according to buyer, we change it.
257 if (GETPOSTINT('addalsovatforthirdpartyid')) {
258 $thirdparty_buyer = new Societe($db);
259 $thirdparty_buyer->fetch($socid);
260
261 $tmpvatwithcode = get_default_tva($mysoc, $thirdparty_buyer, $id, 0);
262
263 if (!is_numeric($tmpvatwithcode) || $tmpvatwithcode != -1) {
264 $reg = array();
265 if (preg_match('/(.+)\s\‍((.+)\‍)/', $tmpvatwithcode, $reg)) {
266 $outtva_tx = price2num($reg[1]);
267 $outtva_tx_formated = price($outtva_tx);
268 $outdefault_vat_code = $reg[2];
269 } else {
270 $outtva_tx = price2num($tmpvatwithcode);
271 $outtva_tx_formated = price($outtva_tx);
272 $outdefault_vat_code = '';
273 }
274 }
275
276 // The VAT rate above was just changed to the one applicable to this buyer, but price_ht/
277 // price_ttc are still whatever was set for the product/price line, so they can now be an
278 // inconsistent triple (e.g. a price defined as tax included, VAT rate now 0 for an EU
279 // reverse-charge buyer, but price_ttc is still the original tax-included amount). Off by
280 // default to keep historical behavior (price_ttc unchanged, whatever VAT rate applies);
281 // when enabled, price_ht is kept as the fixed reference and price_ttc is recomputed from
282 // it and the buyer's actual VAT rate instead.
283 if (getDolGlobalString('PRODUIT_RECALCULATE_TTC_ACCORDING_TO_BUYER_VAT') && isset($outprice_ht)) {
284 $outprice_ttc = price((float) price2num($outprice_ht) * (1 + ($outtva_tx / 100)));
285 }
286 }
287
288 $outjson = array(
289 'ref' => $outref,
290 'label' => $outlabel,
291 'label_trans' => $outlabel_trans,
292 'desc' => $outdesc,
293 'desc_trans' => $outdesc_trans,
294 'type' => $outtype,
295 'price_ht' => $outprice_ht,
296 'price_ttc' => $outprice_ttc,
297 'pricebasetype' => $outpricebasetype,
298 'product_tva_tx_formated' => $product_outtva_tx_formated,
299 'product_tva_tx' => $product_outtva_tx,
300 'product_default_vat_code' => $product_outdefault_vat_code,
301
302 'tva_tx_formated' => $outtva_tx_formated,
303 'tva_tx' => $outtva_tx,
304 'default_vat_code' => $outdefault_vat_code,
305
306 'qty' => $outqty,
307 'discount' => $outdiscount,
308 'mandatory_period' => $mandatory_period,
309
310 'array_options' => $object->array_options,
311
312 'default_unit' => $default_unit
313 );
314 }
315
316 echo json_encode($outjson);
317} else {
318 require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
319 if (!isset($form) || !is_object($form)) {
320 $form = new Form($db);
321 }
322
323 $langs->loadLangs(array("main", "products"));
324
325 top_httphead();
326
327 if (empty($htmlname)) {
328 print json_encode(array());
329 return;
330 }
331
332 // Filter on the product to search can be:
333 // Into an array with key $htmlname123 (we take first one found). Which page use this ?
334 // Into a var with name $htmlname can be 'prodid', 'productid', ...
335 $match = preg_grep('/('.preg_quote($htmlname, '/').'[0-9]+)/', array_keys($_GET));
336 sort($match);
337
338 $idprod = (empty($match[0]) ? '' : $match[0]); // Take first key found into GET array with matching $htmlname123
339
340 if (GETPOST($htmlname, 'alpha') == '' && (!$idprod || !GETPOST($idprod, 'alpha'))) {
341 print json_encode(array());
342 return;
343 }
344
345 // When used from jQuery, the search term is added as GET param "term".
346 $searchkey = (($idprod && GETPOST($idprod, 'alpha')) ? GETPOST($idprod, 'alpha') : (GETPOST($htmlname, 'alpha') ? GETPOST($htmlname, 'alpha') : ''));
347
348 $arrayresult = [];
349 if (empty($mode) || $mode == 1) { // mode=1: customer
350 $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);
351 } elseif ($mode == 2) { // mode=2: supplier
352 $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'));
353 }
354
355 $db->close();
356
357 if ($outjson) {
358 print json_encode($arrayresult);
359 }
360}
$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.
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.
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $db
API class for accounts.
dol_now($mode='gmt')
Return date for now.
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, $nodefault=0)
Return value of a param into GET or POST supervariable.
GETPOSTINT($paramname, $method=0, $nodefault=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
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.