dolibarr 23.0.3
price.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2001-2007 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2004-2014 Laurent Destailleur <eldy@users.sourceforge.net>
4 * Copyright (C) 2005 Eric Seigne <eric.seigne@ryxeo.com>
5 * Copyright (C) 2005-2017 Regis Houssin <regis.houssin@inodbox.com>
6 * Copyright (C) 2006 Andre Cianfarani <acianfa@free.fr>
7 * Copyright (C) 2014 Florian Henry <florian.henry@open-concept.pro>
8 * Copyright (C) 2014-2018 Juanjo Menent <jmenent@2byte.es>
9 * Copyright (C) 2014-2019 Philippe Grand <philippe.grand@atoo-net.com>
10 * Copyright (C) 2014 Ion agorria <ion@agorria.com>
11 * Copyright (C) 2015-2023 Alexandre Spangaro <aspangaro@open-dsi.fr>
12 * Copyright (C) 2015 Marcos García <marcosgdf@gmail.com>
13 * Copyright (C) 2016 Ferran Marcet <fmarcet@2byte.es>
14 * Copyright (C) 2018-2025 Frédéric France <frederic.france@free.fr>
15 * Copyright (C) 2018 Nicolas ZABOURI <info@inovea-conseil.com>
16 * Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
17 * Copyright (C) 2025 Mélina Joum <melina.joum@altairis.fr>
18 *
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 3 of the License, or
22 * (at your option) any later version.
23 *
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License
30 * along with this program. If not, see <https://www.gnu.org/licenses/>.
31 */
32
39// Load Dolibarr environment
40require '../main.inc.php';
41require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
42require_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
43require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
44require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_expression.class.php';
45require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php';
46require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
47
57$prodcustprice = null;
58if (getDolGlobalString('PRODUIT_CUSTOMER_PRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
59 require_once DOL_DOCUMENT_ROOT.'/product/class/productcustomerprice.class.php';
60
61 $prodcustprice = new ProductCustomerPrice($db);
62}
63
64// Load translation files required by the page
65$langs->loadLangs(array('products', 'bills', 'companies', 'other'));
66
67$error = 0;
68$errors = array();
69
70$id = GETPOSTINT('id');
71$ref = GETPOST('ref', 'alpha');
72$action = GETPOST('action', 'aZ09');
73$cancel = GETPOST('cancel', 'alpha');
74$eid = GETPOSTINT('eid');
75
76$search_soc = GETPOST('search_soc');
77
78// Security check
79$fieldvalue = (!empty($id) ? $id : (!empty($ref) ? $ref : ''));
80$fieldtype = (!empty($ref) ? 'ref' : 'rowid');
81if ($user->socid) {
82 $socid = $user->socid;
83}
84
85$object = new Product($db);
86if ($id > 0 || !empty($ref)) {
87 $object->fetch($id, $ref);
88}
89
90$extrafields = new ExtraFields($db);
91
92// Clean param
93if ((getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) && !getDolGlobalString('PRODUIT_MULTIPRICES_LIMIT')) {
94 $conf->global->PRODUIT_MULTIPRICES_LIMIT = 5;
95}
96
97// Initialize a technical object to manage hooks of page. Note that conf->hooks_modules contains an array of hook context
98$hookmanager->initHooks(array('productpricecard', 'globalcard'));
99
100if ($object->id > 0) {
101 if ($object->type == $object::TYPE_PRODUCT) {
102 restrictedArea($user, 'produit', $object->id, 'product&product', '', '');
103 }
104 if ($object->type == $object::TYPE_SERVICE) {
105 restrictedArea($user, 'service', $object->id, 'product&product', '', '');
106 }
107} else {
108 restrictedArea($user, 'produit|service', $fieldvalue, 'product&product', '', '', $fieldtype);
109}
110
111$maxpricesupplier = 0;
112
113if ($object->id > 0) {
114 $permissiontoadd = $object->getRights()->creer;
115} else {
116 $permissiontoadd = ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'));
117}
118
119
120/*
121 * Actions
122 */
123
124if ($cancel) {
125 $action = '';
126}
127
128$parameters = array('id' => $id, 'ref' => $ref);
129$reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
130if ($reshook < 0) {
131 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
132}
133
134if (empty($reshook)) {
135 if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x', 'alpha') || GETPOST('button_removefilter', 'alpha')) { // All tests are required to be compatible with all browsers
136 $search_soc = '';
137 }
138
139 if ($action == 'setlabelsellingprice' && $user->admin) {
140 require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
141 $keyforlabel = 'PRODUIT_MULTIPRICES_LABEL'.GETPOST('pricelevel');
142 dolibarr_set_const($db, $keyforlabel, GETPOST('labelsellingprice', 'alpha'), 'chaine', 0, '', $conf->entity);
143 $action = '';
144 }
145
146 if (($action == 'update_vat') && !$cancel && $permissiontoadd) {
147 $tva_tx_txt = GETPOST('tva_tx', 'alpha'); // tva_tx can be '8.5' or '8.5*' or '8.5 (XXX)' or '8.5* (XXX)'
148
149 $price_label = GETPOST('price_label', 'alpha');
150
151 // We must define tva_tx, npr and local taxes
152 $tva_tx = $tva_tx_txt;
153 $reg = array();
154 $vatratecode = '';
155 if (preg_match('/\‍((.*)\‍)/', $tva_tx_txt, $reg)) {
156 $vatratecode = $reg[1];
157 $tva_tx = preg_replace('/\s*\‍(.*\‍)/', '', $tva_tx_txt); // Remove code into vatrate.
158 }
159
160 $tva_tx = price2num(preg_replace('/\*/', '', $tva_tx)); // keep remove all after the numbers and dot
161 $npr = preg_match('/\*/', $tva_tx_txt) ? 1 : 0;
162 $localtax1 = 0;
163 $localtax2 = 0;
164 $localtax1_type = '0';
165 $localtax2_type = '0';
166 // If value contains the unique code of vat line (new recommended method), we use it to find npr and local taxes
167
168 if (preg_match('/\‍((.*)\‍)/', $tva_tx_txt, $reg)) {
169 // We look into database using code (we can't use get_localtax() because it depends on buyer that is not known). Same in create product.
170 $vatratecode = $reg[1];
171 // Get record from code
172 $sql = "SELECT t.rowid, t.type_vat, t.code, t.recuperableonly, t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
173 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
174 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($mysoc->country_code)."'";
175 $sql .= " AND t.taux = ".((float) $tva_tx)." AND t.active = 1";
176 $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
177 $sql .= " AND t.type_vat IN (0, 1)"; // Use only VAT rates type all or i.e. the sales type VAT rates.
178 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
179 $resql = $db->query($sql);
180 if ($resql) {
181 $obj = $db->fetch_object($resql);
182 if ($obj) {
183 $npr = $obj->recuperableonly;
184 $localtax1 = $obj->localtax1;
185 $localtax2 = $obj->localtax2;
186 $localtax1_type = $obj->localtax1_type;
187 $localtax2_type = $obj->localtax2_type;
188 }
189 }
190 } else {
191 // Get record with empty code
192 $sql = "SELECT t.rowid, t.code, t.recuperableonly, t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
193 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
194 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($mysoc->country_code)."'";
195 $sql .= " AND t.taux = ".((float) $tva_tx)." AND t.active = 1";
196 $sql .= " AND t.code = ''";
197 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
198 $resql = $db->query($sql);
199 if ($resql) {
200 $obj = $db->fetch_object($resql);
201 if ($obj) {
202 $npr = $obj->recuperableonly;
203 $localtax1 = $obj->localtax1;
204 $localtax2 = $obj->localtax2;
205 $localtax1_type = $obj->localtax1_type;
206 $localtax2_type = $obj->localtax2_type;
207 }
208 }
209 }
210
211 $object->default_vat_code = $vatratecode;
212 $object->tva_tx = $tva_tx;
213 $object->tva_npr = $npr;
214 $object->localtax1_tx = $localtax1;
215 $object->localtax2_tx = $localtax2;
216 $object->localtax1_type = $localtax1_type;
217 $object->localtax2_type = $localtax2_type;
218 $object->price_label = $price_label;
219
220 $db->begin();
221
222 $resql = $object->update($object->id, $user);
223 if ($resql <= 0) {
224 $error++;
225 setEventMessages($object->error, $object->errors, 'errors');
226 }
227
228 if (!$error) {
229 if (getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
230 $produit_multiprices_limit = getDolGlobalInt('PRODUIT_MULTIPRICES_LIMIT');
231 for ($i = 1; $i <= $produit_multiprices_limit; $i++) {
232 // Force the update of the price of the product using the new VAT
233 if ($object->multiprices_base_type[$i] == 'HT') {
234 $oldprice = $object->multiprices[$i];
235 $oldminprice = $object->multiprices_min[$i];
236 } else {
237 $oldprice = $object->multiprices_ttc[$i];
238 $oldminprice = $object->multiprices_min_ttc[$i];
239 }
240 $oldpricebasetype = $object->multiprices_base_type[$i];
241 $oldnpr = $object->multiprices_recuperableonly[$i];
242
243 //$localtaxarray=array('0'=>$localtax1_type,'1'=>$localtax1,'2'=>$localtax2_type,'3'=>$localtax2);
244 $localtaxarray = array(); // We do not store localtaxes into product, we will use instead the "vat code" to retrieve them.
245 $level = $i;
246 $ret = $object->updatePrice($oldprice, $oldpricebasetype, $user, (float) $tva_tx, $oldminprice, $level, $oldnpr, 0, 0, $localtaxarray, $vatratecode, $price_label);
247
248 if ($ret < 0) {
249 $error++;
250 setEventMessages($object->error, $object->errors, 'errors');
251 }
252 }
253 } else {
254 // Force the update of the price of the product using the new VAT
255 if ($object->price_base_type == 'HT') {
256 $oldprice = $object->price;
257 $oldminprice = $object->price_min;
258 } else {
259 $oldprice = $object->price_ttc;
260 $oldminprice = $object->price_min_ttc;
261 }
262 $oldpricebasetype = $object->price_base_type;
263 $oldnpr = $object->tva_npr;
264
265 //$localtaxarray=array('0'=>$localtax1_type,'1'=>$localtax1,'2'=>$localtax2_type,'3'=>$localtax2);
266 $localtaxarray = array(); // We do not store localtaxes into product, we will use instead the "vat code" to retrieve them when required.
267 $level = 0;
268 $ret = $object->updatePrice($oldprice, $oldpricebasetype, $user, (float) $tva_tx, $oldminprice, $level, $oldnpr, 0, 0, $localtaxarray, $vatratecode, $price_label);
269
270 if ($ret < 0) {
271 $error++;
272 setEventMessages($object->error, $object->errors, 'errors');
273 }
274 }
275 }
276
277 if (!$error) {
278 $db->commit();
279 } else {
280 $db->rollback();
281 }
282
283 $action = '';
284 }
285
286 $maxpricesupplier = 0;
287
288 if (($action == 'update_price' || $action == 'update_level_price') && !$cancel && $permissiontoadd) {
289 $error = 0;
290 $pricestoupdate = array();
291 $object->oldcopy = dol_clone($object, 1); // when calling ->update later we need to call method on ->oldcopy so we clone using param 1
292
293 $psq = GETPOSTINT('psqflag');
294
295 $maxpricesupplier = $object->min_recommended_price();
296
297 // Packaging
298 $packaging = getDolGlobalString('PRODUCT_USE_CUSTOMER_PACKAGING') ? price2num(GETPOST('packaging', 'alpha'), 'MS') : null;
299
300 if (isModEnabled('dynamicprices')) {
301 $object->fk_price_expression = empty($eid) ? 0 : $eid; //0 discards expression
302
303 if ($object->fk_price_expression != 0) {
304 //Check the expression validity by parsing it
305 require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php';
306 $priceparser = new PriceParser($db);
307
308 if ($priceparser->parseProduct($object) < 0) {
309 $error++;
310 setEventMessages($priceparser->translatedError(), null, 'errors');
311 }
312 }
313 }
314
315 // Multiprices
316 if (!$error && (getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')
317 || ($action == 'update_level_price' && getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')))) { // Test on permission already done
318 $newprice = GETPOST('price', 'array');
319 $newprice_min = GETPOST('price_min', 'array');
320 $newpricebase = GETPOST('multiprices_base_type', 'array');
321 $newvattx = GETPOST('tva_tx', 'array');
322 $newvatnpr = GETPOST('tva_npr', 'array');
323 $newlocaltax1_tx = GETPOST('localtax1_tx', 'array');
324 $newlocaltax1_type = GETPOST('localtax1_type', 'array');
325 $newlocaltax2_tx = GETPOST('localtax2_tx', 'array');
326 $newlocaltax2_type = GETPOST('localtax2_type', 'array');
327
328 //Shall we generate prices using price rules?
329 $object->price_autogen = (int) (GETPOST('usePriceRules') == 'on');
330
331 $produit_multiprices_limit = getDolGlobalInt('PRODUIT_MULTIPRICES_LIMIT');
332 for ($i = 1; $i <= $produit_multiprices_limit; $i++) {
333 if (!isset($newprice[$i])) {
334 continue;
335 }
336
337 $tva_tx_txt = $newvattx[$i];
338
339 $tva_tx = $tva_tx_txt;
340 $vatratecode = '';
341 $reg = array();
342 if (preg_match('/\‍((.*)\‍)/', $tva_tx_txt, $reg)) {
343 $vat_src_code = $reg[1];
344 $tva_tx = preg_replace('/\s*\‍(.*\‍)/', '', $tva_tx_txt); // Remove code into vatrate.
345 }
346 $tva_tx = price2num(preg_replace('/\*/', '', $tva_tx)); // keep remove all after the numbers and dot
347
348 $npr = preg_match('/\*/', $tva_tx_txt) ? 1 : 0;
349 $localtax1 = $newlocaltax1_tx[$i];
350 $localtax1_type = $newlocaltax1_type[$i];
351 $localtax2 = $newlocaltax2_tx[$i];
352 $localtax2_type = $newlocaltax2_type[$i];
353 if (preg_match('/\‍((.*)\‍)/', $tva_tx_txt, $reg)) {
354 // We look into database using code
355 $vatratecode = $reg[1];
356 // Get record from code
357 $sql = "SELECT t.rowid, t.code, t.recuperableonly, t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
358 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
359 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($mysoc->country_code)."'";
360 $sql .= " AND t.taux = ".((float) $tva_tx)." AND t.active = 1";
361 $sql .= " AND t.code ='".$db->escape($vatratecode)."'";
362 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
363 $resql = $db->query($sql);
364 if ($resql) {
365 $obj = $db->fetch_object($resql);
366 if ($obj) {
367 $npr = $obj->recuperableonly;
368 $localtax1 = $obj->localtax1;
369 $localtax2 = $obj->localtax2;
370 $localtax1_type = $obj->localtax1_type;
371 $localtax2_type = $obj->localtax2_type;
372 }
373
374 // If spain, we don't use the localtax found into tax record in database with same code, but using the get_localtax rule.
375 if (in_array($mysoc->country_code, array('ES'))) {
376 $localtax1 = get_localtax($tva_tx, 1);
377 $localtax2 = get_localtax($tva_tx, 2);
378 }
379 }
380 } else {
381 // Get record with empty code
382 $sql = "SELECT t.rowid, t.code, t.recuperableonly, t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
383 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
384 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($mysoc->country_code)."'";
385 $sql .= " AND t.taux = ".((float) $tva_tx)." AND t.active = 1";
386 $sql .= " AND t.code = ''";
387 $resql = $db->query($sql);
388 if ($resql) {
389 $obj = $db->fetch_object($resql);
390 if ($obj) {
391 $npr = $obj->recuperableonly;
392 $localtax1 = $obj->localtax1;
393 $localtax2 = $obj->localtax2;
394 $localtax1_type = $obj->localtax1_type;
395 $localtax2_type = $obj->localtax2_type;
396 }
397 }
398 }
399
400 $pricestoupdate[$i] = array(
401 'price_label' => '', // no change so empty value
402 'price' => price2num($newprice[$i], '', 2),
403 'price_min' => price2num($newprice_min[$i], '', 2),
404 'price_base_type' => $newpricebase[$i],
405 'default_vat_code' => $vatratecode,
406 'vat_tx' => $tva_tx, // default_vat_code should be used in priority in a future
407 'npr' => $npr, // default_vat_code should be used in priority in a future
408 'localtaxes_array' => array('0' => $localtax1_type, '1' => $localtax1, '2' => $localtax2_type, '3' => $localtax2) // default_vat_code should be used in priority in a future
409 );
410
411 //If autogeneration is enabled, then we only set the first level
412 if ($object->price_autogen) {
413 break;
414 }
415 }
416 } elseif (!$error) {
417 $newprice = price2num(GETPOST('price', 'alpha'), '', 2);
418 $newprice_min = price2num(GETPOST('price_min', 'alpha'), '', 2);
419 $newpricebase = GETPOST('price_base_type', 'alpha');
420 $tva_tx_txt = GETPOST('tva_tx', 'alpha'); // tva_tx can be '8.5' or '8.5*' or '8.5 (XXX)' or '8.5* (XXX)'
421 $price_label = GETPOST('price_label', 'alpha');
422
423 $tva_tx = $tva_tx_txt;
424 $vatratecode = '';
425 $reg = array();
426 if (preg_match('/\‍((.*)\‍)/', $tva_tx_txt, $reg)) {
427 $vat_src_code = $reg[1];
428 $tva_tx = preg_replace('/\s*\‍(.*\‍)/', '', $tva_tx_txt); // Remove code into vatrate.
429 }
430 $tva_tx = price2num(preg_replace('/\*/', '', $tva_tx)); // keep remove all after the numbers and dot
431
432 $npr = preg_match('/\*/', $tva_tx_txt) ? 1 : 0;
433 $localtax1 = 0;
434 $localtax2 = 0;
435 $localtax1_type = '0';
436 $localtax2_type = '0';
437 // If value contains the unique code of vat line (new recommended method), we use it to find npr and local taxes
438 if (preg_match('/\‍((.*)\‍)/', $tva_tx_txt, $reg)) {
439 // We look into database using code
440 $vatratecode = $reg[1];
441 // Get record from code
442 $sql = "SELECT t.rowid, t.code, t.recuperableonly, t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
443 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
444 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($mysoc->country_code)."'";
445 $sql .= " AND t.taux = ".((float) $tva_tx)." AND t.active = 1";
446 $sql .= " AND t.code ='".$db->escape($vatratecode)."'";
447 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
448 $resql = $db->query($sql);
449 if ($resql) {
450 $obj = $db->fetch_object($resql);
451 if ($obj) {
452 $npr = $obj->recuperableonly;
453 $localtax1 = $obj->localtax1;
454 $localtax2 = $obj->localtax2;
455 $localtax1_type = $obj->localtax1_type;
456 $localtax2_type = $obj->localtax2_type;
457 }
458
459 // If spain, we don't use the localtax found into tax record in database with same code, but using the get_localtax rule.
460 if (in_array($mysoc->country_code, array('ES'))) {
461 $localtax1 = get_localtax($tva_tx, 1);
462 $localtax2 = get_localtax($tva_tx, 2);
463 }
464 }
465 } else {
466 // Get record with empty code
467 $sql = "SELECT t.rowid, t.code, t.recuperableonly, t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
468 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
469 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($mysoc->country_code)."'";
470 $sql .= " AND t.taux = ".((float) $tva_tx)." AND t.active = 1";
471 $sql .= " AND t.code = ''";
472 $resql = $db->query($sql);
473 if ($resql) {
474 $obj = $db->fetch_object($resql);
475 if ($obj) {
476 $npr = $obj->recuperableonly;
477 $localtax1 = $obj->localtax1;
478 $localtax2 = $obj->localtax2;
479 $localtax1_type = $obj->localtax1_type;
480 $localtax2_type = $obj->localtax2_type;
481 }
482 }
483 }
484
485 $pricestoupdate[0] = array(
486 'price' => $newprice,
487 'price_label' => $price_label,
488 'price_min' => $newprice_min,
489 'price_base_type' => $newpricebase,
490 'default_vat_code' => $vatratecode,
491 'vat_tx' => $tva_tx, // default_vat_code should be used in priority in a future
492 'npr' => $npr, // default_vat_code should be used in priority in a future
493 'localtaxes_array' => array('0' => $localtax1_type, '1' => $localtax1, '2' => $localtax2_type, '3' => $localtax2) // default_vat_code should be used in priority in a future
494 );
495 }
496
497 if (!$error) {
498 $db->begin();
499
500 // Packaging
501 if (getDolGlobalString('PRODUCT_USE_CUSTOMER_PACKAGING')) {
502 $object->packaging = (float) $packaging;
503 }
504
505 foreach ($pricestoupdate as $key => $val) {
506 $newprice = $val['price'];
507
508 if ($val['price'] < $val['price_min'] && !empty($object->fk_price_expression)) {
509 $newprice = $val['price_min']; //Set price same as min, the user will not see the
510 }
511
512 $newprice = price2num($newprice, 'MU');
513 $newprice_min = price2num($val['price_min'], 'MU');
514 $newvattx = price2num($val['vat_tx']);
515
516 if (getDolGlobalString('PRODUCT_MINIMUM_RECOMMENDED_PRICE') && $newprice_min < $maxpricesupplier) {
517 setEventMessages($langs->trans("MinimumPriceLimit", price($maxpricesupplier, 0, '', 1, - 1, - 1, 'auto')), null, 'errors');
518 $error++;
519 break;
520 }
521 // If price has changed, we update it
522 if (!array_key_exists($key, $object->multiprices) || $object->multiprices[$key] != $newprice || $object->multiprices_min[$key] != $newprice_min || $object->multiprices_base_type[$key] != $val['price_base_type'] || $object->multiprices_tva_tx[$key] != $newvattx) {
523 $res = $object->updatePrice((float) $newprice, $val['price_base_type'], $user, (float) $val['vat_tx'], (float) $newprice_min, $key, $val['npr'], $psq, 0, $val['localtaxes_array'], $val['default_vat_code'], $val['price_label']);
524 } else {
525 $res = 0;
526 }
527
528 if ($res < 0) {
529 $error++;
530 setEventMessages($object->error, $object->errors, 'errors');
531 break;
532 }
533
534 // Update level price extrafields
535 $price_extralabels = $extrafields->fetch_name_optionals_label("product_price");
536 if ((getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) && !empty($price_extralabels)) {
537 $sql = "SELECT rowid";
538 $sql .= " FROM ".$object->db->prefix()."product_price";
539 $sql .= " WHERE entity IN (".getEntity('productprice').")";
540 $sql .= " AND price_level=".((int) $key); // $i
541 $sql .= " AND fk_product = ".((int) $object->id);
542 $sql .= " ORDER BY date_price DESC, rowid DESC";
543 $sql .= " LIMIT 1";
544 $resql = $object->db->query($sql);
545 if ($resql) {
546 $lineid = $object->db->fetch_object($resql);
547 $db->free($resql);
548 }
549 if (!empty($lineid->rowid)) {
550 foreach ($price_extralabels as $code => $label) {
551 $code_array = GETPOST($code, 'array');
552 $object->array_options['options_'.$code] = $code_array[$key];
553 }
554 // We need to force table to update product_price and not product extrafields
555 $object->id = $lineid->rowid;
556 $object->table_element = 'product_price';
557 $result = $object->insertExtraFields();
558 // Back to product table
559 $object->id = $id;
560 $object->table_element = 'product';
561 if ($result < 0) {
562 $error++;
563 }
564 }
565 }
566 }
567 }
568
569 if (!$error && $object->update($object->id, $user) < 0) {
570 $error++;
571 setEventMessages($object->error, $object->errors, 'errors');
572 }
573
574 if (empty($error)) {
575 $action = '';
576 setEventMessages($langs->trans("RecordSaved"), null, 'mesgs');
577 $db->commit();
578 } else {
579 $action = 'edit_price';
580 $db->rollback();
581 }
582 }
583
584
585 if ($action == 'delete' && $user->hasRight('produit', 'supprimer')) {
586 $result = $object->log_price_delete($user, GETPOSTINT('lineid'));
587 if ($result < 0) {
588 setEventMessages($object->error, $object->errors, 'errors');
589 }
590 }
591
592 // Set Price by quantity
593 if ($action == 'activate_price_by_qty' && $permissiontoadd) {
594 // Activating product price by quantity add a new price line with price_by_qty set to 1
595 $level = GETPOSTINT('level');
596 $basePrice = ($object->price_base_type == 'HT') ? $object->price : $object->price_ttc;
597 $basePriceMin = ($object->price_base_type == 'HT') ? $object->price_min : $object->price_min_ttc;
598 $ret = $object->updatePrice($basePrice, $object->price_base_type, $user, $object->tva_tx, $basePriceMin, $level, $object->tva_npr, 1);
599
600 if ($ret < 0) {
601 setEventMessages($object->error, $object->errors, 'errors');
602 }
603 }
604 // Unset Price by quantity
605 if ($action == 'disable_price_by_qty' && $permissiontoadd) {
606 // Disabling product price by quantity add a new price line with price_by_qty set to 0
607 $level = GETPOSTINT('level');
608 $basePrice = ($object->price_base_type == 'HT') ? $object->price : $object->price_ttc;
609 $basePriceMin = ($object->price_base_type == 'HT') ? $object->price_min : $object->price_min_ttc;
610 $ret = $object->updatePrice($basePrice, $object->price_base_type, $user, $object->tva_tx, $basePriceMin, $level, $object->tva_npr, 0);
611
612 if ($ret < 0) {
613 setEventMessages($object->error, $object->errors, 'errors');
614 }
615 }
616
617 if ($action == 'edit_price_by_qty') { // Test on permission not required
618 $rowid = GETPOSTINT('rowid');
619 }
620
621 // Add or update price by quantity
622 if ($action == 'update_price_by_qty' && $permissiontoadd) {
623 // Récupération des variables
624 $rowid = GETPOSTINT('rowid');
625 $priceid = GETPOSTINT('priceid');
626 $newprice = price2num(GETPOST("price"), 'MU', 2);
627 // $newminprice=price2num(GETPOST("price_min"),'MU'); // TODO : Add min price management
628 $quantity = price2num(GETPOST('quantity'), 'MS', 2);
629 $remise_percent = price2num(GETPOST('remise_percent'), '', 2);
630 $remise = 0; // TODO : allow discount by amount when available on documents
631
632 if (empty($quantity)) {
633 $error++;
634 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentities("Qty")), null, 'errors');
635 }
636 if (empty($newprice)) {
637 $error++;
638 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentities("Price")), null, 'errors');
639 }
640 if (!$error) {
641 // Calcul du prix HT et du prix unitaire
642 if ($object->price_base_type == 'TTC') {
643 $price = (float) price2num($newprice) / (1 + ($object->tva_tx / 100));
644 }
645
646 $price = price2num($newprice, 'MU');
647 $unitPrice = price2num((float) $price / (float) $quantity, 'MU');
648
649 // Ajout / mise à jour
650 if ($rowid > 0) {
651 $sql = "UPDATE ".MAIN_DB_PREFIX."product_price_by_qty SET";
652 $sql .= " price=".((float) $price).",";
653 $sql .= " unitprice=".((float) $unitPrice).",";
654 $sql .= " quantity=".((float) $quantity).",";
655 $sql .= " remise_percent=".((float) $remise_percent).",";
656 $sql .= " remise=".((float) $remise);
657 $sql .= " WHERE rowid = ".((int) $rowid);
658
659 $result = $db->query($sql);
660 if (!$result) {
661 dol_print_error($db);
662 }
663 } else {
664 $sql = "INSERT INTO ".MAIN_DB_PREFIX."product_price_by_qty (fk_product_price,price,unitprice,quantity,remise_percent,remise) values (";
665 $sql .= ((int) $priceid).','.((float) $price).','.((float) $unitPrice).','.((float) $quantity).','.((float) $remise_percent).','.((float) $remise).')';
666
667 $result = $db->query($sql);
668 if (!$result) {
669 if ($db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
670 setEventMessages($langs->trans("DuplicateRecord"), null, 'errors');
671 } else {
672 dol_print_error($db);
673 }
674 }
675 }
676 }
677 }
678
679 if ($action == 'delete_price_by_qty' && $permissiontoadd) {
680 $rowid = GETPOSTINT('rowid');
681 if (!empty($rowid)) {
682 $sql = "DELETE FROM ".MAIN_DB_PREFIX."product_price_by_qty";
683 $sql .= " WHERE rowid = ".((int) $rowid);
684
685 $result = $db->query($sql);
686 } else {
687 setEventMessages(('delete_price_by_qty'.$langs->transnoentities('MissingIds')), null, 'errors');
688 }
689 }
690
691 if ($action == 'delete_all_price_by_qty' && $permissiontoadd) {
692 $priceid = GETPOSTINT('priceid');
693 if (!empty($priceid)) {
694 $sql = "DELETE FROM ".MAIN_DB_PREFIX."product_price_by_qty";
695 $sql .= " WHERE fk_product_price = ".((int) $priceid);
696
697 $result = $db->query($sql);
698 } else {
699 setEventMessages(('delete_price_by_qty'.$langs->transnoentities('MissingIds')), null, 'errors');
700 }
701 }
702
708 if ($action == 'add_customer_price_confirm' && !$cancel && $prodcustprice !== null && ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
709 $maxpricesupplier = $object->min_recommended_price();
710
711 $update_child_soc = GETPOSTINT('updatechildprice');
712
713 // add price by customer
714 $prodcustprice->fk_soc = GETPOSTINT('socid');
715 $prodcustprice->ref_customer = GETPOST('ref_customer', 'alpha');
716 $prodcustprice->fk_product = $object->id;
717 $prodcustprice->price = price2num(GETPOST("price"), 'MU');
718 $prodcustprice->price_min = price2num(GETPOST("price_min"), 'MU');
719 $prodcustprice->price_base_type = GETPOST("price_base_type", 'alpha');
720 $prodcustprice->price_label = GETPOST("price_label", 'alpha');
721 $prodcustprice->discount_percent = price2num(GETPOST("discount_percent"));
722 $prodcustprice->date_begin = dol_mktime(0, 0, 0, GETPOSTINT('date_beginmonth'), GETPOSTINT('date_beginday'), GETPOSTINT('date_beginyear'), 'tzserver'); // If we enter the 02 january, we need to save the 02 january for server;
723 $prodcustprice->date_end = dol_mktime(0, 0, 0, GETPOSTINT('date_endmonth'), GETPOSTINT('date_endday'), GETPOSTINT('date_endyear'), 'tzserver'); // If we enter the 02 january, we need to save the 02 january for server
724
725 $extralabels = $extrafields->fetch_name_optionals_label("product_customer_price");
726 $extrafield_values = $extrafields->getOptionalsFromPost("product_customer_price");
727
728 $tva_tx_txt = GETPOST("tva_tx", 'alpha');
729
730 $tva_tx = $tva_tx_txt;
731 $vatratecode = '';
732 if (preg_match('/\‍((.*)\‍)/', $tva_tx_txt, $reg)) {
733 $vat_src_code = $reg[1];
734 $tva_tx = preg_replace('/\s*\‍(.*\‍)/', '', $tva_tx_txt); // Remove code into vatrate.
735 }
736 $tva_tx = price2num(preg_replace('/\*/', '', $tva_tx)); // keep remove all after the numbers and dot
737
738 $npr = preg_match('/\*/', $tva_tx_txt) ? 1 : 0;
739 $localtax1 = 0;
740 $localtax2 = 0;
741 $localtax1_type = '0';
742 $localtax2_type = '0';
743 // If value contains the unique code of vat line (new recommended method), we use it to find npr and local taxes
744 if (preg_match('/\‍((.*)\‍)/', $tva_tx_txt, $reg)) {
745 // We look into database using code
746 $vatratecode = $reg[1];
747 // Get record from code
748 $sql = "SELECT t.rowid, t.code, t.recuperableonly, t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
749 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
750 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($mysoc->country_code)."'";
751 $sql .= " AND t.taux = ".((float) $tva_tx)." AND t.active = 1";
752 $sql .= " AND t.code ='".$db->escape($vatratecode)."'";
753 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
754 $resql = $db->query($sql);
755 if ($resql) {
756 $obj = $db->fetch_object($resql);
757 if ($obj) {
758 $npr = $obj->recuperableonly;
759 $localtax1 = $obj->localtax1;
760 $localtax2 = $obj->localtax2;
761 $localtax1_type = $obj->localtax1_type;
762 $localtax2_type = $obj->localtax2_type;
763 }
764
765 // If spain, we don't use the localtax found into tax record in database with same code, but using the get_localtax rule.
766 if (in_array($mysoc->country_code, array('ES'))) {
767 $localtax1 = get_localtax($tva_tx, 1);
768 $localtax2 = get_localtax($tva_tx, 2);
769 }
770 }
771 } else {
772 // Get record with empty code
773 $sql = "SELECT t.rowid, t.code, t.recuperableonly, t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
774 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
775 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($mysoc->country_code)."'";
776 $sql .= " AND t.taux = ".((float) $tva_tx)." AND t.active = 1";
777 $sql .= " AND t.code = ''";
778 $resql = $db->query($sql);
779 if ($resql) {
780 $obj = $db->fetch_object($resql);
781 if ($obj) {
782 $npr = $obj->recuperableonly;
783 $localtax1 = $obj->localtax1;
784 $localtax2 = $obj->localtax2;
785 $localtax1_type = $obj->localtax1_type;
786 $localtax2_type = $obj->localtax2_type;
787 }
788 }
789 }
790
791 $prodcustprice->default_vat_code = $vatratecode;
792 $prodcustprice->tva_tx = $tva_tx;
793 $prodcustprice->recuperableonly = $npr;
794 $prodcustprice->localtax1_tx = $localtax1;
795 $prodcustprice->localtax2_tx = $localtax2;
796 $prodcustprice->localtax1_type = $localtax1_type;
797 $prodcustprice->localtax2_type = $localtax2_type;
798
799 if (!($prodcustprice->fk_soc > 0)) {
800 $langs->load("errors");
801 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("ThirdParty")), null, 'errors');
802 $error++;
803 $action = 'add_customer_price';
804 }
805 if (getDolGlobalString('PRODUCT_MINIMUM_RECOMMENDED_PRICE') && $prodcustprice->price_min < (float) $maxpricesupplier) {
806 $langs->load("errors");
807 setEventMessages($langs->trans("MinimumPriceLimit", price((float) $maxpricesupplier, 0, '', 1, -1, -1, 'auto')), null, 'errors');
808 $error++;
809 $action = 'add_customer_price';
810 }
811
812 if (!$error) {
813 // Insert price
814 $result = $prodcustprice->create($user, 0, $update_child_soc);
815 if ($result > 0) {
816 if (!empty($extrafield_values) && is_array($extrafield_values)) {
817 $productcustomerprice = new ProductCustomerPrice($db);
818 $res = $productcustomerprice->fetch($prodcustprice->id);
819 if ($res > 0) {
820 foreach ($extrafield_values as $key => $value) {
821 $productcustomerprice->array_options[$key] = $value;
822 }
823 $result2 = $productcustomerprice->insertExtraFields();
824 if ($result2 < 0) {
825 $prodcustprice->error = $productcustomerprice->error;
826 $prodcustprice->errors = $productcustomerprice->errors;
827 $error++;
828 }
829 }
830 }
831 }
832
833 if ($result < 0) {
834 setEventMessages($prodcustprice->error, $prodcustprice->errors, 'errors');
835 $action = 'add_customer_price';
836 } else {
837 setEventMessages($langs->trans('RecordSaved'), null, 'mesgs');
838 $action = '';
839 }
840 }
841 }
842
843 if ($action == 'delete_customer_price' && $prodcustprice !== null && ($user->hasRight('produit', 'supprimer') || $user->hasRight('service', 'supprimer'))) {
844 // Delete price by customer
845 $prodcustprice->id = GETPOSTINT('lineid');
846 $result = $prodcustprice->delete($user);
847
848 if ($result > 0) {
849 $db->query("DELETE FROM ".MAIN_DB_PREFIX."product_customer_price_extrafields WHERE fk_object = ".((int) $prodcustprice->id));
850 setEventMessages($langs->trans("PriceRemoved"), null, 'mesgs');
851 } else {
852 $error++;
853 setEventMessages($object->error, $object->errors, 'errors');
854 }
855
856 if ($result < 0) {
857 setEventMessages($prodcustprice->error, $prodcustprice->errors, 'errors');
858 } else {
859 setEventMessages($langs->trans('RecordDeleted'), null, 'mesgs');
860 }
861 $action = '';
862 }
863
864 if ($action == 'update_customer_price_confirm' && !$cancel && $prodcustprice !== null && ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
865 $maxpricesupplier = $object->min_recommended_price();
866
867 $update_child_soc = GETPOSTINT('updatechildprice');
868
869 $prodcustprice->fetch(GETPOSTINT('lineid'));
870
871 // update price by customer
872 $prodcustprice->ref_customer = GETPOST('ref_customer', 'alpha');
873 $prodcustprice->price = price2num(GETPOST("price"), 'MU');
874 $prodcustprice->price_min = price2num(GETPOST("price_min"), 'MU');
875 $prodcustprice->price_base_type = GETPOST("price_base_type", 'alpha');
876 $prodcustprice->price_label = GETPOST("price_label", 'alpha');
877 $prodcustprice->discount_percent = price2num(GETPOST("discount_percent"));
878 $prodcustprice->date_begin = dol_mktime(0, 0, 0, GETPOSTINT('date_beginmonth'), GETPOSTINT('date_beginday'), GETPOSTINT('date_beginyear'), 'tzserver'); // If we enter the 02 january, we need to save the 02 january for server;
879 $prodcustprice->date_end = dol_mktime(0, 0, 0, GETPOSTINT('date_endmonth'), GETPOSTINT('date_endday'), GETPOSTINT('date_endyear'), 'tzserver'); // If we enter the 02 january, we need to save the 02 january for server
880
881 $extralabels = $extrafields->fetch_name_optionals_label("product_customer_price");
882 $extrafield_values = $extrafields->getOptionalsFromPost("product_customer_price");
883
884 $tva_tx_txt = GETPOST("tva_tx");
885
886 $tva_tx = $tva_tx_txt;
887 $vatratecode = '';
888 if (preg_match('/\‍((.*)\‍)/', $tva_tx_txt, $reg)) {
889 $vat_src_code = $reg[1];
890 $tva_tx = preg_replace('/\s*\‍(.*\‍)/', '', $tva_tx_txt); // Remove code into vatrate.
891 }
892 $tva_tx = price2num(preg_replace('/\*/', '', $tva_tx)); // keep remove all after the numbers and dot
893
894 $npr = preg_match('/\*/', $tva_tx_txt) ? 1 : 0;
895 $localtax1 = 0;
896 $localtax2 = 0;
897 $localtax1_type = '0';
898 $localtax2_type = '0';
899 // If value contains the unique code of vat line (new recommended method), we use it to find npr and local taxes
900 if (preg_match('/\‍((.*)\‍)/', $tva_tx_txt, $reg)) {
901 // We look into database using code
902 $vatratecode = $reg[1];
903 // Get record from code
904 $sql = "SELECT t.rowid, t.code, t.recuperableonly, t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
905 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
906 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($mysoc->country_code)."'";
907 $sql .= " AND t.taux = ".((float) $tva_tx)." AND t.active = 1";
908 $sql .= " AND t.code ='".$db->escape($vatratecode)."'";
909 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
910 $resql = $db->query($sql);
911 if ($resql) {
912 $obj = $db->fetch_object($resql);
913 if ($obj) {
914 $npr = $obj->recuperableonly;
915 $localtax1 = $obj->localtax1;
916 $localtax2 = $obj->localtax2;
917 $localtax1_type = $obj->localtax1_type;
918 $localtax2_type = $obj->localtax2_type;
919 }
920
921 // If spain, we don't use the localtax found into tax record in database with same code, but using the get_localtax rule.
922 if (in_array($mysoc->country_code, array('ES'))) {
923 $localtax1 = get_localtax($tva_tx, 1);
924 $localtax2 = get_localtax($tva_tx, 2);
925 }
926 }
927 } else {
928 // Get record with empty code
929 $sql = "SELECT t.rowid, t.code, t.recuperableonly, t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
930 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
931 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($mysoc->country_code)."'";
932 $sql .= " AND t.taux = ".((float) $tva_tx)." AND t.active = 1";
933 $sql .= " AND t.code = ''";
934 $resql = $db->query($sql);
935 if ($resql) {
936 $obj = $db->fetch_object($resql);
937 if ($obj) {
938 $npr = $obj->recuperableonly;
939 $localtax1 = $obj->localtax1;
940 $localtax2 = $obj->localtax2;
941 $localtax1_type = $obj->localtax1_type;
942 $localtax2_type = $obj->localtax2_type;
943 }
944 }
945 }
946
947 $prodcustprice->default_vat_code = $vatratecode;
948 $prodcustprice->tva_tx = $tva_tx;
949 $prodcustprice->recuperableonly = $npr;
950 $prodcustprice->localtax1_tx = $localtax1;
951 $prodcustprice->localtax2_tx = $localtax2;
952 $prodcustprice->localtax1_type = $localtax1_type;
953 $prodcustprice->localtax2_type = $localtax2_type;
954
955 if ($prodcustprice->price_min < $maxpricesupplier && getDolGlobalString('PRODUCT_MINIMUM_RECOMMENDED_PRICE')) {
956 setEventMessages($langs->trans("MinimumPriceLimit", price($maxpricesupplier, 0, '', 1, -1, -1, 'auto')), null, 'errors');
957 $error++;
958 $action = 'update_customer_price';
959 }
960
961 if (!$error) {
962 $result = $prodcustprice->update($user, 0, $update_child_soc);
963 if ($result > 0) {
964 if (!empty($extrafield_values) && is_array($extrafield_values)) {
965 $productcustomerprice = new ProductCustomerPrice($db);
966 $res = $productcustomerprice->fetch($prodcustprice->id);
967 if ($res > 0) {
968 foreach ($extrafield_values as $key => $value) {
969 $productcustomerprice->array_options[$key] = $value;
970 }
971 $result2 = $productcustomerprice->insertExtraFields();
972 if ($result2 < 0) {
973 $prodcustprice->error = $productcustomerprice->error;
974 $prodcustprice->errors = $productcustomerprice->errors;
975 $error++;
976 }
977 }
978 }
979 }
980
981 if ($result < 0) {
982 setEventMessages($prodcustprice->error, $prodcustprice->errors, 'errors');
983 $action = 'update_customer_price';
984 } else {
985 setEventMessages($langs->trans("Save"), null, 'mesgs');
986 $action = '';
987 }
988 }
989 }
990}
991
992
993/*
994 * View
995 */
996
997$form = new Form($db);
998
999if (!empty($id) || !empty($ref)) {
1000 // fetch updated prices
1001 $object->fetch($id, $ref);
1002}
1003
1004$title = $langs->trans('ProductServiceCard');
1005$helpurl = '';
1006$shortlabel = dol_trunc($object->label, 16);
1007if (GETPOST("type") == '0' || ($object->type == Product::TYPE_PRODUCT)) {
1008 $title = $langs->trans('Product')." ".$shortlabel." - ".$langs->trans('SellingPrices');
1009 $helpurl = 'EN:Module_Products|FR:Module_Produits|ES:M&oacute;dulo_Productos';
1010}
1011if (GETPOST("type") == '1' || ($object->type == Product::TYPE_SERVICE)) {
1012 $title = $langs->trans('Service')." ".$shortlabel." - ".$langs->trans('SellingPrices');
1013 $helpurl = 'EN:Module_Services_En|FR:Module_Services|ES:M&oacute;dulo_Servicios';
1014}
1015
1016llxHeader('', $title, $helpurl, '', 0, 0, '', '', '', 'classforhorizontalscrolloftabs mod-product page-price');
1017
1018$head = product_prepare_head($object);
1019$titre = $langs->trans("CardProduct".$object->type);
1020$picto = ($object->type == Product::TYPE_SERVICE ? 'service' : 'product');
1021
1022print dol_get_fiche_head($head, 'price', $titre, -1, $picto);
1023
1024$linkback = '<a href="'.DOL_URL_ROOT.'/product/list.php?restore_lastsearch_values=1&type='.$object->type.'">'.$langs->trans("BackToList").'</a>';
1025$object->next_prev_filter = "(te.fk_product_type:=:".((int) $object->type).")";
1026
1027$shownav = 1;
1028if ($user->socid && !in_array('product', explode(',', getDolGlobalString('MAIN_MODULES_FOR_EXTERNAL')))) {
1029 $shownav = 0;
1030}
1031
1032dol_banner_tab($object, 'ref', $linkback, $shownav, 'ref');
1033
1034
1035print '<div class="fichecenter">';
1036
1037print '<div class="underbanner clearboth"></div>';
1038print '<table class="border tableforfield centpercent">';
1039
1040$soc = null;
1041
1042// Price per customer segment/level
1043if (getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
1044 // Price and min price are variable (depends on level of company).
1045 if (!empty($socid)) {
1046 $soc = new Societe($db);
1047 $soc->id = $socid;
1048 $soc->fetch($socid);
1049
1050 // Type
1051 if (isModEnabled("product") && isModEnabled("service")) {
1052 $typeformat = 'select;0:'.$langs->trans("Product").',1:'.$langs->trans("Service");
1053 print '<tr><td class="">';
1054 print (!getDolGlobalString('PRODUCT_DENY_CHANGE_PRODUCT_TYPE')) ? $form->editfieldkey("Type", 'fk_product_type', (string) $object->type, $object, 0, $typeformat) : $langs->trans('Type');
1055 print '</td><td>';
1056 print $form->editfieldval("Type", 'fk_product_type', $object->type, $object, 0, $typeformat);
1057 print '</td></tr>';
1058 }
1059
1060 // Selling price
1061 print '<tr><td class="titlefieldcreate">';
1062 print $langs->trans("SellingPrice");
1063 print '</td>';
1064 print '<td colspan="2">';
1065 if ($object->multiprices_base_type[$soc->price_level] == 'TTC') {
1066 print '<span class="amount">'.price($object->multiprices_ttc[$soc->price_level]).'</span>';
1067 } else {
1068 print '<span class="amount">'.price($object->multiprices[$soc->price_level]).'</span>';
1069 }
1070 if ($object->multiprices_base_type[$soc->price_level]) {
1071 print ' '.$langs->trans($object->multiprices_base_type[$soc->price_level]);
1072 } else {
1073 print ' '.$langs->trans($object->price_base_type);
1074 }
1075 print '</td></tr>';
1076
1077 // Price min
1078 print '<tr><td>'.$langs->trans("MinPrice").'</td><td colspan="2">';
1079 if ($object->multiprices_base_type[$soc->price_level] == 'TTC') {
1080 print price($object->multiprices_min_ttc[$soc->price_level]).' '.$langs->trans($object->multiprices_base_type[$soc->price_level]);
1081 } else {
1082 print price($object->multiprices_min[$soc->price_level]).' '.$langs->trans(empty($object->multiprices_base_type[$soc->price_level]) ? 'HT' : $object->multiprices_base_type[$soc->price_level]);
1083 }
1084 print '</td></tr>';
1085
1086 if (getDolGlobalString('PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL')) { // using this option is a bug. kept for backward compatibility
1087 // TVA
1088 print '<tr><td>'.$langs->trans("DefaultTaxRate").'</td><td colspan="2">';
1089
1090 $positiverates = '';
1091 if (price2num($object->multiprices_tva_tx[$soc->price_level])) {
1092 $positiverates .= ($positiverates ? '/' : '').price2num($object->multiprices_tva_tx[$soc->price_level]);
1093 }
1094 if (price2num($object->multiprices_localtax1_type[$soc->price_level])) {
1095 $positiverates .= ($positiverates ? '/' : '').price2num($object->multiprices_localtax1_tx[$soc->price_level]);
1096 }
1097 if (price2num($object->multiprices_localtax2_type[$soc->price_level])) {
1098 $positiverates .= ($positiverates ? '/' : '').price2num($object->multiprices_localtax2_tx[$soc->price_level]);
1099 }
1100 if (empty($positiverates)) {
1101 $positiverates = '0';
1102 }
1103 echo vatrate($positiverates.($object->default_vat_code ? ' ('.$object->default_vat_code.')' : ''), true, $object->tva_npr);
1104
1105 print '</td></tr>';
1106 } else {
1107 // TVA
1108 print '<tr><td>'.$langs->trans("DefaultTaxRate").'</td><td>';
1109
1110 $positiverates = '';
1111 if (price2num($object->tva_tx)) {
1112 $positiverates .= ($positiverates ? '/' : '').price2num($object->tva_tx);
1113 }
1114 if (price2num($object->localtax1_type)) {
1115 $positiverates .= ($positiverates ? '/' : '').price2num($object->localtax1_tx);
1116 }
1117 if (price2num($object->localtax2_type)) {
1118 $positiverates .= ($positiverates ? '/' : '').price2num($object->localtax2_tx);
1119 }
1120 if (empty($positiverates)) {
1121 $positiverates = '0';
1122 }
1123 echo vatrate($positiverates.($object->default_vat_code ? ' ('.$object->default_vat_code.')' : ''), true, $object->tva_npr);
1124
1125 print '</td></tr>';
1126 }
1127 } else {
1128 if (getDolGlobalString('PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL')) { // using this option is a bug. kept for backward compatibility
1129 // Type
1130 if (isModEnabled("product") && isModEnabled("service")) {
1131 $typeformat = 'select;0:'.$langs->trans("Product").',1:'.$langs->trans("Service");
1132 print '<tr><td class="">';
1133 print (!getDolGlobalString('PRODUCT_DENY_CHANGE_PRODUCT_TYPE')) ? $form->editfieldkey("Type", 'fk_product_type', (string) $object->type, $object, 0, $typeformat) : $langs->trans('Type');
1134 print '</td><td>';
1135 print $form->editfieldval("Type", 'fk_product_type', $object->type, $object, 0, $typeformat);
1136 print '</td></tr>';
1137 }
1138
1139 // We show only vat for level 1
1140 print '<tr><td class="titlefieldcreate">'.$langs->trans("DefaultTaxRate").'</td>';
1141 print '<td colspan="2">'.vatrate($object->multiprices_tva_tx[1], true).'</td>';
1142 print '</tr>';
1143 } else {
1144 // Type
1145 if (isModEnabled("product") && isModEnabled("service")) {
1146 $typeformat = 'select;0:'.$langs->trans("Product").',1:'.$langs->trans("Service");
1147 print '<tr><td class="">';
1148 print (!getDolGlobalString('PRODUCT_DENY_CHANGE_PRODUCT_TYPE')) ? $form->editfieldkey("Type", 'fk_product_type', (string) $object->type, $object, 0, $typeformat) : $langs->trans('Type');
1149 print '</td><td>';
1150 print $form->editfieldval("Type", 'fk_product_type', $object->type, $object, 0, $typeformat);
1151 print '</td></tr>';
1152 }
1153
1154 // TVA
1155 print '<!-- Default VAT Rate -->';
1156 print '<tr><td class="titlefieldcreate">'.$langs->trans("DefaultTaxRate").'</td><td>';
1157
1158 // TODO We show localtax from $object, but this properties may not be correct. Only value $object->default_vat_code is guaranteed.
1159 $positiverates = '';
1160 if (price2num($object->tva_tx)) {
1161 $positiverates .= ($positiverates ? '<span class="opacitymedium">/</span>' : '').price2num($object->tva_tx);
1162 }
1163 if (price2num($object->localtax1_type)) {
1164 $positiverates .= ($positiverates ? '<span class="opacitymedium">/</span>' : '').price2num($object->localtax1_tx);
1165 }
1166 if (price2num($object->localtax2_type)) {
1167 $positiverates .= ($positiverates ? '<span class="opacitymedium">/</span>' : '').price2num($object->localtax2_tx);
1168 }
1169 if (empty($positiverates)) {
1170 $positiverates = '0';
1171 }
1172
1173 print vatrate($positiverates.($object->default_vat_code ? ' ('.$object->default_vat_code.')' : ''), true, $object->tva_npr, 1);
1174 /*
1175 if ($object->default_vat_code)
1176 {
1177 print vatrate($object->tva_tx, true) . ' ('.$object->default_vat_code.')';
1178 }
1179 else print vatrate($object->tva_tx . ($object->tva_npr ? '*' : ''), true);*/
1180 print '</td></tr>';
1181 }
1182 print '</table>';
1183
1184 print '<br>';
1185
1186 print '<table class="liste centpercent">';
1187 print '<tr class="liste_titre"><td>';
1188 print $langs->trans("PriceLevel");
1189 if ($user->admin) {
1190 print ' <a class="editfielda" href="'.$_SERVER["PHP_SELF"].'?action=editlabelsellingprice&token='.newToken().'&pricelevel='.$i.'&id='.$object->id.'">'.img_edit($langs->trans('EditSellingPriceLabel'), 0).'</a>';
1191 }
1192 print '</td>';
1193 print '<td style="text-align: right">'.$langs->trans("SellingPrice").'</td>';
1194 print '<td style="text-align: right">'.$langs->trans("MinPrice").'</td>';
1195 // fetch optionals attributes and labels
1196 $extrafields->fetch_name_optionals_label("product_price");
1197 if ($extrafields->attributes["product_price"] && array_key_exists('label', $extrafields->attributes["product_price"])) {
1198 $extralabels = $extrafields->attributes["product_price"]['label'];
1199 if (!empty($extralabels)) {
1200 foreach ($extralabels as $key => $value) {
1201 // Show field if not hidden
1202 if (!empty($extrafields->attributes["product_price"]['list'][$key]) && $extrafields->attributes["product_price"]['list'][$key] != 3) {
1203 if (!empty($extrafields->attributes["product_price"]['langfile'][$key])) {
1204 $langs->load($extrafields->attributes["product_price"]['langfile'][$key]);
1205 }
1206 if (!empty($extrafields->attributes["product_price"]['help'][$key])) {
1207 $extratitle = $form->textwithpicto($langs->trans($value), $langs->trans($extrafields->attributes["product_price"]['help'][$key]));
1208 } else {
1209 $extratitle = $langs->trans($value);
1210 }
1211 $field = 'ef.' . $key;
1212 print '<td style="text-align: right">'.$extratitle.'</td>';
1213 }
1214 }
1215 }
1216 }
1217 print '</tr>';
1218
1219 $produit_multiprices_limit = getDolGlobalInt('PRODUIT_MULTIPRICES_LIMIT');
1220 for ($i = 1; $i <= $produit_multiprices_limit; $i++) {
1221 print '<tr class="oddeven">';
1222
1223 // Label of price
1224 print '<td>';
1225 $keyforlabel = 'PRODUIT_MULTIPRICES_LABEL'.$i;
1226 if (preg_match('/editlabelsellingprice/', $action)) {
1227 print '<form method="post" action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'">';
1228 print '<input type="hidden" name="token" value="'.newToken().'">';
1229 print '<input type="hidden" name="action" value="setlabelsellingprice">';
1230 print '<input type="hidden" name="pricelevel" value="'.$i.'">';
1231 print $langs->trans("SellingPrice").' '.$i.' - ';
1232 print '<input class="maxwidthonsmartphone" type="text" name="labelsellingprice" value="' . getDolGlobalString($keyforlabel).'">';
1233 print '&nbsp;<input type="submit" class="button smallpaddingimp" value="'.$langs->trans("Modify").'">';
1234 print '</form>';
1235 } else {
1236 print $langs->trans("SellingPrice").' '.$i;
1237 if (getDolGlobalString($keyforlabel)) {
1238 print ' - '.$langs->trans(getDolGlobalString($keyforlabel));
1239 }
1240 }
1241 print '</td>';
1242
1243 if ($object->multiprices_base_type [$i] == 'TTC') {
1244 print '<td class="right"><span class="amount">'.price($object->multiprices_ttc[$i]);
1245 } else {
1246 print '<td class="right"><span class="amount">'.price($object->multiprices[$i]);
1247 }
1248
1249 if ($object->multiprices_base_type[$i]) {
1250 print ' '.$langs->trans($object->multiprices_base_type [$i]).'</span></td>';
1251 } else {
1252 print ' '.$langs->trans($object->price_base_type).'</span></td>';
1253 }
1254
1255 // Prix min
1256 print '<td style="text-align: right">';
1257 if (empty($object->multiprices_base_type[$i])) {
1258 $object->multiprices_base_type[$i] = "HT";
1259 }
1260 if ($object->multiprices_base_type[$i] == 'TTC') {
1261 print price($object->multiprices_min_ttc[$i]).' '.$langs->trans($object->multiprices_base_type[$i]);
1262 } else {
1263 print price($object->multiprices_min[$i]).' '.$langs->trans($object->multiprices_base_type[$i]);
1264 }
1265 print '</td>';
1266 if (!empty($extralabels)) {
1267 $sql1 = "SELECT rowid";
1268 $sql1 .= " FROM ".$object->db->prefix()."product_price";
1269 $sql1 .= " WHERE entity IN (".getEntity('productprice').")";
1270 $sql1 .= " AND price_level=".((int) $i);
1271 $sql1 .= " AND fk_product = ".((int) $object->id);
1272 $sql1 .= " ORDER BY date_price DESC, rowid DESC";
1273 $sql1 .= " LIMIT 1";
1274 $resql1 = $object->db->query($sql1);
1275 if ($resql1) {
1276 $lineid = $object->db->fetch_object($resql1);
1277 }
1278 $sql2 = "SELECT";
1279 $sql2 .= " fk_object";
1280 foreach ($extralabels as $key => $value) {
1281 $sql2 .= ", ".$db->sanitize($key);
1282 }
1283 $sql2 .= " FROM ".MAIN_DB_PREFIX."product_price_extrafields";
1284 $sql2 .= " WHERE fk_object = ".((int) $lineid->rowid);
1285 $resql2 = $db->query($sql2);
1286 if ($resql2) {
1287 if ($db->num_rows($resql2) != 1) {
1288 foreach ($extralabels as $key => $value) {
1289 if (!empty($extrafields->attributes["product_price"]['list'][$key]) && $extrafields->attributes["product_price"]['list'][$key] != 3) {
1290 print '<td align="right"></td>';
1291 }
1292 }
1293 } else {
1294 $obj = $db->fetch_object($resql2);
1295 foreach ($extralabels as $key => $value) {
1296 if (!empty($extrafields->attributes["product_price"]['list'][$key]) && $extrafields->attributes["product_price"]['list'][$key] != 3) {
1297 print '<td align="right">'.$extrafields->showOutputField($key, $obj->{$key}, '', 'product_price')."</td>";
1298 }
1299 }
1300 }
1301 $db->free($resql1);
1302 $db->free($resql2);
1303 }
1304 }
1305 print '</tr>';
1306
1307 // Price by quantity
1308 if (getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) { // TODO Fix the form included into a tr instead of a td
1309 print '<tr><td>'.$langs->trans("PriceByQuantity").' '.$i;
1310 if (getDolGlobalString($keyforlabel)) {
1311 print ' - '.$langs->trans(getDolGlobalString($keyforlabel));
1312 }
1313 print '</td><td colspan="2">';
1314
1315 if ($object->prices_by_qty[$i] == 1) {
1316 print '<table width="50%" class="border" summary="List of quantities">';
1317
1318 print '<tr class="liste_titre">';
1319 print '<td>'.$langs->trans("PriceByQuantityRange").' '.$i.'</td>';
1320 print '<td class="right">'.$langs->trans("HT").'</td>';
1321 print '<td class="right">'.$langs->trans("UnitPrice").'</td>';
1322 print '<td class="right">'.$langs->trans("Discount").'</td>';
1323 print '<td>&nbsp;</td>';
1324 print '</tr>';
1325 $ii = 0;
1326 foreach ($object->prices_by_qty_list[$i] as $ii => $prices) {
1327 if ($action == 'edit_price_by_qty' && $rowid == $prices['rowid'] && ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
1328 print '<form action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="POST">';
1329 print '<input type="hidden" name="token" value="'.newToken().'">';
1330 print '<input type="hidden" name="action" value="update_price_by_qty">';
1331 print '<input type="hidden" name="priceid" value="'.$object->prices_by_qty_id[$i].'">';
1332 print '<input type="hidden" value="'.$prices['rowid'].'" name="rowid">';
1333 print '<tr class="'.($ii % 2 == 0 ? 'pair' : 'impair').'">';
1334 print '<td><input size="5" type="text" value="'.$prices['quantity'].'" name="quantity"></td>';
1335 print '<td class="right" colspan="2"><input size="10" type="text" value="'.price2num($prices['price'], 'MU').'" name="price">&nbsp;'.$object->price_base_type.'</td>';
1336 print '<td class="right nowraponall"><input size="5" type="text" value="'.$prices['remise_percent'].'" name="remise_percent"> %</td>';
1337 print '<td class="center"><input type="submit" value="'.$langs->trans("Modify").'" class="button"></td>';
1338 print '</tr>';
1339 print '</form>';
1340 } else {
1341 print '<tr class="'.($ii % 2 == 0 ? 'pair' : 'impair').'">';
1342 print '<td>'.$prices['quantity'].'</td>';
1343 print '<td class="right">'.price($prices['price']).'</td>';
1344 print '<td class="right">'.price($prices['unitprice']).'</td>';
1345 print '<td class="right">'.price($prices['remise_percent']).' %</td>';
1346 print '<td class="center">';
1347 if (($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
1348 print '<a class="editfielda marginleftonly marginrightonly" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=edit_price_by_qty&token='.newToken().'&rowid='.$prices["rowid"].'">';
1349 print img_edit().'</a>';
1350 print '<a class="marginleftonly marginrightonly" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=delete_price_by_qty&token='.newToken().'&rowid='.$prices["rowid"].'">';
1351 print img_delete().'</a>';
1352 } else {
1353 print '&nbsp;';
1354 }
1355 print '</td>';
1356 print '</tr>';
1357 }
1358 }
1359 if ($action != 'edit_price_by_qty' && ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
1360 print '<form action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="POST">';
1361 print '<input type="hidden" name="token" value="'.newToken().'">';
1362 print '<input type="hidden" name="action" value="update_price_by_qty">';
1363 print '<input type="hidden" name="priceid" value="'.$object->prices_by_qty_id[$i].'">'; // id in product_price
1364 print '<input type="hidden" value="0" name="rowid">'; // id in product_price
1365 print '<tr class="'.($ii % 2 == 0 ? 'pair' : 'impair').'">';
1366 print '<td><input size="5" type="text" value="1" name="quantity"></td>';
1367 print '<td class="right" class="nowrap"><input size="10" type="text" value="0" name="price">&nbsp;'.$object->price_base_type.'</td>';
1368 print '<td class="right">&nbsp;</td>';
1369 print '<td class="right" class="nowraponall"><input size="5" type="text" value="0" name="remise_percent"> %</td>';
1370 print '<td class="center"><input type="submit" value="'.$langs->trans("Add").'" class="button"></td>';
1371 print '</tr>';
1372 print '</form>';
1373 }
1374
1375 print '</table>';
1376 print '<a class="editfielda marginleftonly marginrightonly" href="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'&action=disable_price_by_qty&level='.$i.'&token='.newToken().'">('.$langs->trans("DisablePriceByQty").')</a>';
1377 } else {
1378 print $langs->trans("No");
1379 print '&nbsp; <a class="marginleftonly marginrightonly" href="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'&action=activate_price_by_qty&level='.$i.'&token='.newToken().'">('.$langs->trans("Activate").')</a>';
1380 }
1381 print '</td></tr>';
1382 }
1383 }
1384 }
1385} else {
1386 // Type
1387 if (isModEnabled("product") && isModEnabled("service")) {
1388 $typeformat = 'select;0:'.$langs->trans("Product").',1:'.$langs->trans("Service");
1389 print '<tr><td class="">';
1390 print (!getDolGlobalString('PRODUCT_DENY_CHANGE_PRODUCT_TYPE')) ? $form->editfieldkey("Type", 'fk_product_type', (string) $object->type, $object, 0, $typeformat) : $langs->trans('Type');
1391 print '</td><td>';
1392 print $form->editfieldval("Type", 'fk_product_type', $object->type, $object, 0, $typeformat);
1393 print '</td></tr>';
1394 }
1395
1396 // TVA
1397 print '<tr><td class="titlefield">'.$langs->trans("DefaultTaxRate").'</td><td>';
1398
1399 $positiverates = '';
1400 if (price2num($object->tva_tx)) {
1401 $positiverates .= ($positiverates ? '/' : '').price2num($object->tva_tx);
1402 }
1403 if (price2num($object->localtax1_type)) {
1404 $positiverates .= ($positiverates ? '/' : '').price2num($object->localtax1_tx);
1405 }
1406 if (price2num($object->localtax2_type)) {
1407 $positiverates .= ($positiverates ? '/' : '').price2num($object->localtax2_tx);
1408 }
1409 if (empty($positiverates)) {
1410 $positiverates = '0';
1411 }
1412 echo vatrate($positiverates.($object->default_vat_code ? ' ('.$object->default_vat_code.')' : ''), true, $object->tva_npr, 0, 1);
1413 /*
1414 if ($object->default_vat_code)
1415 {
1416 print vatrate($object->tva_tx, true) . ' ('.$object->default_vat_code.')';
1417 }
1418 else print vatrate($object->tva_tx, true, $object->tva_npr, true);*/
1419 print '</td></tr>';
1420
1421 // Price
1422 print '<tr class="field_selling_price"><td>'.$langs->trans("SellingPrice").'</td><td>';
1423 if ($object->price_base_type == 'TTC') {
1424 print price($object->price_ttc).' '.$langs->trans($object->price_base_type);
1425 } else {
1426 print price($object->price).' '.$langs->trans($object->price_base_type);
1427 if (getDolGlobalString('PRODUCT_DISPLAY_VAT_INCL_PRICES') && !empty($object->price_ttc)) {
1428 print '<i class="opacitymedium"> - ' . price($object->price_ttc).' '.$langs->trans('TTC') . '</i>';
1429 }
1430 }
1431
1432 print '</td></tr>';
1433
1434 // Price minimum
1435 print '<tr class="field_min_price"><td>'.$langs->trans("MinPrice").'</td><td>';
1436 if ($object->price_base_type == 'TTC') {
1437 print price($object->price_min_ttc).' '.$langs->trans($object->price_base_type);
1438 } else {
1439 print price($object->price_min).' '.$langs->trans($object->price_base_type);
1440 if (getDolGlobalString('PRODUCT_DISPLAY_VAT_INCL_PRICES') && !empty($object->price_min_ttc)) {
1441 print '<i class="opacitymedium"> - ' . price($object->price_min_ttc).' '.$langs->trans('TTC') . '</i>';
1442 }
1443 }
1444 print '</td></tr>';
1445
1446 // Packaging
1447 if (getDolGlobalString('PRODUCT_USE_CUSTOMER_PACKAGING')) {
1448 print '<tr class="field_price_label"><td>'.$form->textwithpicto($langs->trans("PackagingForThisProduct"), $langs->trans("PackagingForThisProductDesc")).'</td><td>';
1449 print $object->packaging;
1450 print '</td></tr>';
1451 }
1452
1453 // Price Label
1454 print '<tr class="field_price_label"><td>'.$langs->trans("PriceLabel").'</td><td>';
1455 print $object->price_label;
1456 print '</td></tr>';
1457
1458 $ii = 0;
1459 // Price by quantity
1460 if (getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) { // TODO Fix the form inside tr instead of td
1461 print '<tr><td>'.$langs->trans("PriceByQuantity");
1462 if ($object->prices_by_qty[0] == 0) {
1463 print '&nbsp; <a href="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'&action=activate_price_by_qty&level=1&token='.newToken().'">('.$langs->trans("Activate").')';
1464 } else {
1465 print '&nbsp; <a href="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'&action=disable_price_by_qty&level=1&token='.newToken().'">('.$langs->trans("DisablePriceByQty").')';
1466 }
1467 print '</td><td>';
1468
1469 if ($object->prices_by_qty[0] == 1) {
1470 print '<table width="50%" class="border" summary="List of quantities">';
1471 print '<tr class="liste_titre">';
1472 //print '<td>' . $langs->trans("PriceByQuantityRange") . '</td>';
1473 print '<td>'.$langs->trans("Quantity").'</td>';
1474 print '<td class="right">'.$langs->trans("Price").'</td>';
1475 print '<td class="right"></td>';
1476 print '<td class="right">'.$langs->trans("UnitPrice").'</td>';
1477 print '<td class="right">'.$langs->trans("Discount").'</td>';
1478 print '<td>&nbsp;</td>';
1479 print '</tr>';
1480 if ($action != 'edit_price_by_qty') {
1481 print '<form action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="POST">'; // FIXME a form into a table is not allowed
1482 print '<input type="hidden" name="token" value="'.newToken().'">';
1483 print '<input type="hidden" name="action" value="update_price_by_qty">';
1484 print '<input type="hidden" name="priceid" value="'.$object->prices_by_qty_id[0].'">'; // id in product_price
1485 print '<input type="hidden" value="0" name="rowid">'; // id in product_price_by_qty
1486
1487 print '<tr class="'.(($ii % 2) == 0 ? 'pair' : 'impair').'">'; // @phpstan-ignore-line
1488 print '<td><input size="5" type="text" value="1" name="quantity"></td>';
1489 print '<td class="right"><input class="width50 right" type="text" value="0" name="price"></td>';
1490 print '<td>';
1491 //print $object->price_base_type;
1492 print '</td>';
1493 print '<td class="right">&nbsp;</td>';
1494 print '<td class="right nowraponall"><input type="text" class="width50 right" value="0" name="remise_percent"> %</td>';
1495 print '<td class="center"><input type="submit" value="'.$langs->trans("Add").'" class="button"></td>';
1496 print '</tr>';
1497
1498 print '</form>';
1499 }
1500 foreach ($object->prices_by_qty_list[0] as $ii => $prices) {
1501 if ($action == 'edit_price_by_qty' && $rowid == $prices['rowid'] && ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
1502 print '<form action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="POST">';
1503 print '<input type="hidden" name="token" value="'.newToken().'">';
1504 print '<input type="hidden" name="action" value="update_price_by_qty">';
1505 print '<input type="hidden" name="priceid" value="'.$object->prices_by_qty_id[0].'">'; // id in product_price
1506 print '<input type="hidden" value="'.$prices['rowid'].'" name="rowid">'; // id in product_price_by_qty
1507 print '<tr class="'.($ii % 2 == 0 ? 'pair' : 'impair').'">';
1508 print '<td><input size="5" type="text" value="'.$prices['quantity'].'" name="quantity"></td>';
1509 print '<td class="right"><input class="width50 right" type="text" value="'.price2num($prices['price'], 'MU').'" name="price"></td>';
1510 print '<td class="right">';
1511 //print $object->price_base_type;
1512 print $prices['price_base_type'];
1513 print '</td>';
1514 print '<td class="right">&nbsp;</td>';
1515 print '<td class="right nowraponall"><input class="width50 right" type="text" value="'.$prices['remise_percent'].'" name="remise_percent"> %</td>';
1516 print '<td class="center"><input type="submit" value="'.$langs->trans("Modify").'" class="button"></td>';
1517 print '</tr>';
1518 print '</form>';
1519 } else {
1520 print '<tr class="'.($ii % 2 == 0 ? 'pair' : 'impair').'">';
1521 print '<td>'.$prices['quantity'].'</td>';
1522 print '<td class="right">'.price($prices['price']).'</td>';
1523 print '<td class="right">';
1524 //print $object->price_base_type;
1525 print $prices['price_base_type'];
1526 print '</td>';
1527 print '<td class="right">'.price($prices['unitprice']).'</td>';
1528 print '<td class="right">'.price($prices['remise_percent']).' %</td>';
1529 print '<td class="center">';
1530 if (($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
1531 print '<a class="editfielda marginleftonly marginrightonly" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=edit_price_by_qty&token='.newToken().'&rowid='.$prices["rowid"].'">';
1532 print img_edit().'</a>';
1533 print '<a class="marginleftonly marginrightonly" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=delete_price_by_qty&token='.newToken().'&rowid='.$prices["rowid"].'">';
1534 print img_delete().'</a>';
1535 } else {
1536 print '&nbsp;';
1537 }
1538 print '</td>';
1539 print '</tr>';
1540 }
1541 }
1542 print '</table>';
1543 } else {
1544 print $langs->trans("No");
1545 }
1546 print '</td></tr>';
1547 }
1548}
1549
1550print "</table>\n";
1551
1552print '</div>';
1553print '<div class="clearboth"></div>';
1554
1555
1556print dol_get_fiche_end();
1557
1558
1559
1560// Button for actions
1561
1562if (!$action || $action == 'delete' || $action == 'showlog_customer_price' || $action == 'showlog_default_price' || $action == 'add_customer_price'
1563 || $action == 'activate_price_by_qty' || $action == 'disable_price_by_qty') {
1564 print "\n".'<div class="tabsAction">'."\n";
1565
1566 $parameters = array();
1567 $reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been
1568 if (empty($reshook)) {
1569 if ($object->isVariant()) {
1570 //if ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer')) {
1571 print '<div class="inline-block divButAction"><a class="butActionRefused classfortooltip" href="#" title="' . dol_escape_htmltag($langs->trans("NoEditVariants")) . '">' . $langs->trans("UpdateDefaultPrice") . '</a></div>';
1572 //}
1573 } else {
1574 if (!getDolGlobalString('PRODUIT_MULTIPRICES') && !getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES') && !getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
1575 if ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer')) {
1576 print '<div class="inline-block divButAction"><a class="butAction" href="' . $_SERVER['PHP_SELF'] . '?action=edit_price&token='.newToken().'&id=' . $object->id . '">' . $langs->trans("UpdateDefaultPrice") . '</a></div>';
1577 } else {
1578 print '<div class="inline-block divButAction"><span class="butActionRefused" title="'.dol_escape_htmltag($langs->trans("NotEnoughPermissions")).'">' . $langs->trans("UpdateDefaultPrice") . '</span></div>';
1579 }
1580 }
1581
1582 if (getDolGlobalString('PRODUIT_CUSTOMER_PRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
1583 if ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer')) {
1584 print '<div class="inline-block divButAction"><a class="butAction" href="' . $_SERVER["PHP_SELF"] . '?action=add_customer_price&token='.newToken().'&id=' . $object->id . '">' . $langs->trans("AddCustomerPrice") . '</a></div>';
1585 } else {
1586 print '<div class="inline-block divButAction"><span class="butActionRefused" title="'.dol_escape_htmltag($langs->trans("NotEnoughPermissions")).'">' . $langs->trans("AddCustomerPrice") . '</span></div>';
1587 }
1588 }
1589
1590 if (getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
1591 if ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer')) {
1592 print '<div class="inline-block divButAction"><a class="butAction" href="' . $_SERVER['PHP_SELF'] . '?action=edit_vat&token='.newToken().'&id=' . $object->id . '">' . $langs->trans("UpdateVAT") . '</a></div>';
1593 } else {
1594 print '<div class="inline-block divButAction"><span class="butActionRefused" title="'.dol_escape_htmltag($langs->trans("NotEnoughPermissions")).'">' . $langs->trans("UpdateVAT") . '</span></div>';
1595 }
1596
1597 if ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer')) {
1598 print '<div class="inline-block divButAction"><a class="butAction" href="' . $_SERVER['PHP_SELF'] . '?action=edit_level_price&token='.newToken().'&id=' . $object->id . '">' . $langs->trans("UpdateLevelPrices") . '</a></div>';
1599 } else {
1600 print '<div class="inline-block divButAction"><span class="butActionRefused" title="'.dol_escape_htmltag($langs->trans("NotEnoughPermissions")).'">' . $langs->trans("UpdateLevelPrices") . '</span></div>';
1601 }
1602 }
1603 }
1604 }
1605
1606 print "\n</div>\n";
1607}
1608
1609
1610
1611/*
1612 * Edit price area
1613 */
1614
1615if ($action == 'edit_vat' && ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
1616 print load_fiche_titre($langs->trans("UpdateVAT"), '');
1617
1618 print '<form action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="POST">';
1619 print '<input type="hidden" name="token" value="'.newToken().'">';
1620 print '<input type="hidden" name="action" value="update_vat">';
1621 print '<input type="hidden" name="id" value="'.$object->id.'">';
1622
1623 print dol_get_fiche_head();
1624
1625 print '<table class="border centpercent">';
1626
1627 // VAT
1628 print '<tr><td>'.$langs->trans("DefaultTaxRate").'</td><td>';
1629 print $form->load_tva("tva_tx", $object->default_vat_code ? $object->tva_tx.' ('.$object->default_vat_code.')' : $object->tva_tx, $mysoc, null, $object->id, $object->tva_npr, $object->type, false, 1);
1630 print '</td></tr>';
1631
1632 print '</table>';
1633
1634 print dol_get_fiche_end();
1635
1636 print $form->buttonsSaveCancel();
1637
1638 print '<br></form><br>';
1639}
1640
1641if (($action == 'edit_price' || $action == 'edit_level_price') && $object->getRights()->creer) {
1642 print '<br>';
1643 print load_fiche_titre($langs->trans("NewPrice"), '');
1644
1645 if (!getDolGlobalString('PRODUIT_MULTIPRICES') && $action != 'edit_level_price') {
1646 print '<!-- Edit price -->'."\n";
1647 print '<form action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="POST">';
1648 print '<input type="hidden" name="token" value="'.newToken().'">';
1649 print '<input type="hidden" name="action" value="update_price">';
1650 print '<input type="hidden" name="id" value="'.$object->id.'">';
1651
1652 print dol_get_fiche_head();
1653
1654 print '<div class="div-table-responsive-no-min">';
1655 print '<table class="border centpercent">';
1656
1657 // VAT
1658 print '<tr><td class="titlefield">'.$langs->trans("DefaultTaxRate").'</td><td>';
1659 print $form->load_tva("tva_tx", $object->default_vat_code ? $object->tva_tx.' ('.$object->default_vat_code.')' : $object->tva_tx, $mysoc, null, $object->id, $object->tva_npr, $object->type, false, 1);
1660 print '</td></tr>';
1661
1662 // Price base
1663 print '<tr><td>';
1664 print $langs->trans('PriceBase');
1665 print '</td>';
1666 print '<td>';
1667 print $form->selectPriceBaseType($object->price_base_type, "price_base_type");
1668 print '</td>';
1669 print '</tr>';
1670
1671 // Only show price mode and expression selector if module is enabled
1672 if (isModEnabled('dynamicprices')) {
1673 // Price mode selector
1674 print '<!-- Show price mode of dynamicprices editor -->'."\n";
1675 print '<tr><td>'.$langs->trans("PriceMode").'</td><td>';
1676 print img_picto('', 'dynamicprice', 'class="pictofixedwidth"');
1677 $price_expression = new PriceExpression($db);
1678 $price_expression_list = array(0 => $langs->trans("Numeric").' <span class="opacitymedium">('.$langs->trans("NoDynamicPrice").')</span>'); //Put the numeric mode as first option
1679 foreach ($price_expression->list_price_expression() as $entry) {
1680 $price_expression_list[$entry->id] = $entry->title;
1681 }
1682 $price_expression_preselection = GETPOST('eid') ? GETPOST('eid') : ($object->fk_price_expression ? $object->fk_price_expression : '0');
1683 print $form->selectarray('eid', $price_expression_list, $price_expression_preselection);
1684 print '&nbsp; <a id="expression_editor" class="classlink">'.$langs->trans("PriceExpressionEditor").'</a>';
1685 print '</td></tr>';
1686
1687 // This code hides the numeric price input if is not selected, loads the editor page if editor button is pressed
1688 ?>
1689
1690 <script type="text/javascript">
1691 jQuery(document).ready(function() {
1692 jQuery("#expression_editor").click(function() {
1693 window.location = "<?php echo DOL_URL_ROOT ?>/product/dynamic_price/editor.php?id=<?php echo $id ?>&tab=price&eid=" + $("#eid").val();
1694 });
1695 jQuery("#eid").change(on_change);
1696 on_change();
1697 });
1698 function on_change() {
1699 if ($("#eid").val() == 0) {
1700 jQuery("#price_numeric").show();
1701 } else {
1702 jQuery("#price_numeric").hide();
1703 }
1704 }
1705 </script>
1706 <?php
1707 }
1708
1709 // Price
1710 $product = new Product($db);
1711 $product->fetch($id, $ref, '', '1'); //Ignore the math expression when getting the price
1712 print '<tr id="price_numeric"><td>';
1713 $text = $langs->trans('SellingPrice');
1714 print $form->textwithpicto($text, $langs->trans("PrecisionUnitIsLimitedToXDecimals", getDolGlobalString('MAIN_MAX_DECIMALS_UNIT')), 1, 'help');
1715 print '</td><td>';
1716 if ($object->price_base_type == 'TTC') {
1717 print '<input name="price" size="10" value="'.price($product->price_ttc).'">';
1718 } else {
1719 print '<input name="price" size="10" value="'.price($product->price).'">';
1720 }
1721 print '</td></tr>';
1722
1723 // Price minimum
1724 print '<tr><td>';
1725 $text = $langs->trans('MinPrice');
1726 print $form->textwithpicto($text, $langs->trans("PrecisionUnitIsLimitedToXDecimals", getDolGlobalString('MAIN_MAX_DECIMALS_UNIT')), 1, 'help');
1727 print '</td><td>';
1728 if ($object->price_base_type == 'TTC') {
1729 print '<input name="price_min" size="10" value="'.($object->price_min_ttc ? price($object->price_min_ttc) : '').'">';
1730 } else {
1731 print '<input name="price_min" size="10" value="'.($object->price_min ? price($object->price_min) : '').'">';
1732 }
1733 if (getDolGlobalString('PRODUCT_MINIMUM_RECOMMENDED_PRICE')) {
1734 print ' &nbsp; '.$langs->trans("MinimumRecommendedPrice", price((float) $maxpricesupplier, 0, '', 1, -1, -1, 'auto')).' '.img_warning().'</td>';
1735 }
1736 print '</td>';
1737 print '</tr>';
1738
1739 // Packaging
1740 if (getDolGlobalString('PRODUCT_USE_CUSTOMER_PACKAGING')) {
1741 print '<tr><td>';
1742 print $form->textwithpicto($langs->trans("PackagingForThisProduct"), $langs->trans("PackagingForThisProductDesc"));
1743 print '</td><td>';
1744 $packaging = $object->packaging;
1745 print '<input class="flat" name="packaging" size="5" value="' . ($packaging ? price($packaging, 0, '', 1, -1, 2) : '').'">';
1746 print '</td>';
1747 print '</tr>';
1748 }
1749
1750 // Price Label
1751 print '<tr><td>';
1752 print $langs->trans('PriceLabel');
1753 print '</td><td>';
1754 print '<input name="price_label" maxlength="255" class="minwidth300 maxwidth400onsmartphone" value="'.$object->price_label.'">';
1755 print '</td>';
1756 print '</tr>';
1757
1758 $parameters = array();
1759 $reshook = $hookmanager->executeHooks('formObjectOptions', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
1760
1761 print '</table>';
1762 print '</div>';
1763
1764 print dol_get_fiche_end();
1765
1766 print $form->buttonsSaveCancel();
1767
1768 print '</form>';
1769 } elseif ($action == 'edit_level_price' && $object->getRights()->creer) {
1770 print '<!-- Edit price per level -->'."\n"; ?>
1771 <script>
1772
1773 var showHidePriceRules = function () {
1774 var otherPrices = $('div.fiche form table tbody tr:not(:first)');
1775 var minPrice1 = $('div.fiche form input[name="price_min[1]"]');
1776
1777 if (jQuery('input#usePriceRules').prop('checked')) {
1778 otherPrices.hide();
1779 minPrice1.hide();
1780 } else {
1781 otherPrices.show();
1782 minPrice1.show();
1783 }
1784 }
1785
1786 jQuery(document).ready(function () {
1787 showHidePriceRules();
1788
1789 jQuery('input#usePriceRules').click(showHidePriceRules);
1790 });
1791 </script>
1792 <?php
1793
1794 print '<form action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="POST">';
1795 print '<input type="hidden" name="token" value="'.newToken().'">';
1796 print '<input type="hidden" name="action" value="update_level_price">';
1797 print '<input type="hidden" name="id" value="'.$object->id.'">';
1798
1799 //print dol_get_fiche_head(array(), '', '', -1);
1800
1801 if ((getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) && getDolGlobalString('PRODUIT_MULTIPRICES_ALLOW_AUTOCALC_PRICELEVEL')) {
1802 print $langs->trans('UseMultipriceRules').' <input type="checkbox" id="usePriceRules" name="usePriceRules" '.($object->price_autogen ? 'checked' : '').'><br><br>';
1803 }
1804
1805 print '<div class="div-table-responsive-no-min">';
1806 print '<table class="noborder">';
1807 print '<thead><tr class="liste_titre">';
1808
1809 print '<td>'.$langs->trans("PriceLevel").'</td>';
1810
1811 if (getDolGlobalString('PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL')) {
1812 print '<td style="text-align: center">'.$langs->trans("DefaultTaxRate").'</td>';
1813 } else {
1814 print '<td></td>';
1815 }
1816
1817 print '<td class="center">'.$langs->trans("SellingPrice").'</td>';
1818
1819 print '<td class="center">'.$langs->trans("MinPrice").'</td>';
1820
1821 if (getDolGlobalString('PRODUCT_MINIMUM_RECOMMENDED_PRICE')) {
1822 print '<td></td>';
1823 }
1824
1825 // fetch optionals attributes and labels
1826 $extrafields->fetch_name_optionals_label("product_price");
1827 if ($extrafields->attributes["product_price"] && array_key_exists('label', $extrafields->attributes["product_price"])) {
1828 $extralabels = $extrafields->attributes["product_price"]['label'];
1829 if (!empty($extralabels)) {
1830 foreach ($extralabels as $key => $value) {
1831 // Show field if not hidden
1832 if (!empty($extrafields->attributes["product_price"]['list'][$key]) && $extrafields->attributes["product_price"]['list'][$key] != 3) {
1833 if (!empty($extrafields->attributes["product_price"]['langfile'][$key])) {
1834 $langs->load($extrafields->attributes["product_price"]['langfile'][$key]);
1835 }
1836 if (!empty($extrafields->attributes["product_price"]['help'][$key])) {
1837 $extratitle = $form->textwithpicto($langs->trans($value), $langs->trans($extrafields->attributes["product_price"]['help'][$key]));
1838 } else {
1839 $extratitle = $langs->trans($value);
1840 }
1841 print '<td style="text-align: right">'.$extratitle.'</td>';
1842 }
1843 }
1844 }
1845 }
1846 print '</tr></thead>';
1847
1848 print '<tbody>';
1849
1850 $produit_multiprices_limit = getDolGlobalInt('PRODUIT_MULTIPRICES_LIMIT');
1851 for ($i = 1; $i <= $produit_multiprices_limit; $i++) {
1852 print '<tr class="oddeven">';
1853 print '<td>';
1854 $keyforlabel = 'PRODUIT_MULTIPRICES_LABEL'.$i;
1855 $text = $langs->trans('SellingPrice').' '.$i.' - '.getDolGlobalString($keyforlabel);
1856 print $form->textwithpicto($text, $langs->trans("PrecisionUnitIsLimitedToXDecimals", getDolGlobalString('MAIN_MAX_DECIMALS_UNIT')), 1, 'help');
1857 print '</td>';
1858
1859 // VAT
1860 if (!getDolGlobalString('PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL')) {
1861 print '<td>';
1862 print '<input type="hidden" name="tva_tx['.$i.']" value="'.($object->default_vat_code ? $object->tva_tx.' ('.$object->default_vat_code.')' : $object->tva_tx).'">';
1863 print '<input type="hidden" name="tva_npr['.$i.']" value="'.$object->tva_npr.'">';
1864 print '<input type="hidden" name="localtax1_tx['.$i.']" value="'.$object->localtax1_tx.'">';
1865 print '<input type="hidden" name="localtax1_type['.$i.']" value="'.$object->localtax1_type.'">';
1866 print '<input type="hidden" name="localtax2_tx['.$i.']" value="'.$object->localtax2_tx.'">';
1867 print '<input type="hidden" name="localtax2_type['.$i.']" value="'.$object->localtax2_type.'">';
1868 print '</td>';
1869 } else {
1870 // This option is kept for backward compatibility but has no sense
1871 print '<td style="text-align: center">';
1872 print $form->load_tva("tva_tx[".$i.']', $object->multiprices_tva_tx[$i], $mysoc, null, $object->id, 0, $object->type, false, 1);
1873 print '</td>';
1874 }
1875
1876 // Selling price
1877 print '<td style="text-align: center">';
1878 if ($object->multiprices_base_type [$i] == 'TTC') {
1879 print '<input name="price['.$i.']" size="10" value="'.price($object->multiprices_ttc [$i]).'">';
1880 } else {
1881 print '<input name="price['.$i.']" size="10" value="'.price($object->multiprices [$i]).'">';
1882 }
1883 print '&nbsp;'.$form->selectPriceBaseType($object->multiprices_base_type [$i], "multiprices_base_type[".$i."]");
1884 print '</td>';
1885
1886 // Min price
1887 print '<td style="text-align: center">';
1888 if ($object->multiprices_base_type [$i] == 'TTC') {
1889 print '<input name="price_min['.$i.']" size="10" value="'.price($object->multiprices_min_ttc [$i]).'">';
1890 } else {
1891 print '<input name="price_min['.$i.']" size="10" value="'.price($object->multiprices_min [$i]).'">';
1892 }
1893 if (getDolGlobalString('PRODUCT_MINIMUM_RECOMMENDED_PRICE')) {
1894 print '<td class="left">'.$langs->trans("MinimumRecommendedPrice", price((float) $maxpricesupplier, 0, '', 1, -1, -1, 'auto')).' '.img_warning().'</td>';
1895 }
1896 print '</td>';
1897
1898 if (!empty($extralabels)) {
1899 $sql1 = "SELECT rowid";
1900 $sql1 .= " FROM ".$object->db->prefix()."product_price";
1901 $sql1 .= " WHERE entity IN (".getEntity('productprice').")";
1902 $sql1 .= " AND price_level=".((int) $i);
1903 $sql1 .= " AND fk_product = ".((int) $object->id);
1904 $sql1 .= " ORDER BY date_price DESC, rowid DESC";
1905 $sql1 .= " LIMIT 1";
1906 $resql1 = $object->db->query($sql1);
1907 if ($resql1) {
1908 $lineid = $object->db->fetch_object($resql1);
1909 }
1910 if (empty($lineid->rowid)) {
1911 foreach ($extralabels as $key => $value) {
1912 if (!empty($extrafields->attributes["product_price"]['list'][$key]) && ($extrafields->attributes["product_price"]['list'][$key] == 1 || $extrafields->attributes["product_price"]['list'][$key] == 3 || ($action == "edit_level_price" && $extrafields->attributes["product_price"]['list'][$key] == 4))) {
1913 if (!empty($extrafields->attributes["product_price"]['langfile'][$key])) {
1914 $langs->load($extrafields->attributes["product_price"]['langfile'][$key]);
1915 }
1916
1917 $extravalue = GETPOSTISSET('options_'.$key) ? $extrafield_values['options_'.$key] : $obj->{$key};
1918 print '<td align="center"><input name="'.$key.'['.$i.']" size="10" value="'.$extravalue.'"></td>';
1919 }
1920 }
1921 } else {
1922 $sql = "SELECT";
1923 $sql .= " fk_object";
1924 foreach ($extralabels as $key => $value) {
1925 $sql .= ", ".$db->sanitize($key);
1926 }
1927 $sql .= " FROM ".MAIN_DB_PREFIX."product_price_extrafields";
1928 $sql .= " WHERE fk_object = ".((int) $lineid->rowid);
1929 $resql = $db->query($sql);
1930 if ($resql) {
1931 $obj = $db->fetch_object($resql);
1932 foreach ($extralabels as $key => $value) {
1933 if (!empty($extrafields->attributes["product_price"]['list'][$key]) && ($extrafields->attributes["product_price"]['list'][$key] == 1 || $extrafields->attributes["product_price"]['list'][$key] == 3 || ($action == "edit_level_price" && $extrafields->attributes["product_price"]['list'][$key] == 4))) {
1934 if (!empty($extrafields->attributes["product_price"]['langfile'][$key])) {
1935 $langs->load($extrafields->attributes["product_price"]['langfile'][$key]);
1936 }
1937
1938 $extravalue = (GETPOSTISSET('options_'.$key) ? $extrafield_values['options_'.$key] : $obj->{$key} ?? '');
1939 print '<td align="center"><input name="'.$key.'['.$i.']" size="10" value="'.$extravalue.'"></td>';
1940 }
1941 }
1942 $db->free($resql);
1943 }
1944 }
1945 }
1946 print '</tr>';
1947 }
1948
1949 print '</tbody>';
1950
1951 print '</table>';
1952 print '</div>';
1953
1954 //print dol_get_fiche_end();
1955
1956 print $form->buttonsSaveCancel();
1957
1958 print '</form>';
1959 }
1960}
1961
1962// Add area to show/add/edit a price for a dedicated customer
1963if (getDolGlobalString('PRODUIT_CUSTOMER_PRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
1964 $prodcustprice = new ProductCustomerPrice($db);
1965
1966 $limit = GETPOSTINT('limit') ? GETPOSTINT('limit') : $conf->liste_limit;
1967 $sortfield = GETPOST('sortfield', 'aZ09comma');
1968 $sortorder = GETPOST('sortorder', 'aZ09comma');
1969 $page = (GETPOSTINT("page") ? GETPOSTINT("page") : 0);
1970 if (empty($page) || $page == -1) {
1971 $page = 0;
1972 } // If $page is not defined, or '' or -1
1973 $offset = $limit * $page;
1974 $pageprev = $page - 1;
1975 $pagenext = $page + 1;
1976 if (!$sortorder) {
1977 $sortorder = "ASC,ASC";
1978 }
1979 if (!$sortfield) {
1980 $sortfield = "soc.nom,t.date_begin";
1981 }
1982
1983 // Build filter to display only concerned lines
1984 $filter = array('t.fk_product' => (string) $object->id);
1985
1986 if (!empty($search_soc)) {
1987 $filter['soc.nom'] = (string) $search_soc;
1988 }
1989
1990 if ($action == 'add_customer_price') {
1991 // Form to add a new customer price
1992 $maxpricesupplier = $object->min_recommended_price();
1993
1994 print '<!-- add_customer_price -->';
1995 print load_fiche_titre($langs->trans('AddCustomerPrice'));
1996
1997 print '<form action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="POST">';
1998 print '<input type="hidden" name="token" value="'.newToken().'">';
1999 print '<input type="hidden" name="action" value="add_customer_price_confirm">';
2000 print '<input type="hidden" name="id" value="'.$object->id.'">';
2001
2002 print '<div class="tabBar tabBarWithBottom">';
2003
2004 print '<table class="border centpercent">';
2005 print '<tr>';
2006 print '<td class="fieldrequired">'.$langs->trans('ThirdParty').'</td>';
2007 print '<td>';
2008 $filter = '(s.client:IN:1,2,3)';
2009 print img_picto('', 'company').$form->select_company(GETPOSTINT('socid'), 'socid', $filter, 'SelectThirdParty', 0, 0, array(), 0, 'minwidth300');
2010 print '</td>';
2011 print '</tr>';
2012
2013 // Ref. Customer
2014 print '<tr><td>' . $langs->trans('RefCustomer') . '</td>';
2015 print '<td><input name="ref_customer" size="12"></td></tr>';
2016
2017 // Applied Prices From
2018 $date_begin = dol_mktime(0, 0, 0, GETPOSTINT('date_beginmonth'), GETPOSTINT('date_beginday'), GETPOSTINT('date_beginyear'), 'tzserver'); // If we enter the 02 january, we need to save the 02 january for server;
2019 print '<tr><td>'.$langs->trans("AppliedPricesFrom").'</td><td>';
2020 print $form->selectDate(!empty($date_begin) ? $date_begin : dol_now(), "date_begin", 0, 0, 1, "date_begin");
2021 print '</td></tr>';
2022
2023 // Applied Prices To
2024 $date_end = dol_mktime(0, 0, 0, GETPOSTINT('date_endmonth'), GETPOSTINT('date_endday'), GETPOSTINT('date_endyear'), 'tzserver'); // If we enter the 02 january, we need to save the 02 january for server
2025 print '<tr><td>'.$langs->trans("AppliedPricesTo").'</td><td>';
2026 print $form->selectDate($date_end, "date_end", 0, 0, 1, "date_end");
2027 print '</td></tr>';
2028
2029 // VAT
2030 print '<tr><td class="fieldrequired">'.$langs->trans("DefaultTaxRate").'</td><td>';
2031 print $form->load_tva("tva_tx", $object->default_vat_code ? $object->tva_tx.' ('.$object->default_vat_code.')' : $object->tva_tx, $mysoc, null, $object->id, $object->tva_npr, $object->type, false, 1);
2032 print '</td></tr>';
2033
2034 // Price base
2035 print '<tr><td class="fieldrequired">';
2036 print $langs->trans('PriceBase');
2037 print '</td>';
2038 print '<td>';
2039 print $form->selectPriceBaseType($object->price_base_type, "price_base_type");
2040 print '</td>';
2041 print '</tr>';
2042
2043 // Price
2044 print '<tr><td class="fieldrequired">';
2045 $text = $langs->trans('SellingPrice');
2046 print $form->textwithpicto($text, $langs->trans("PrecisionUnitIsLimitedToXDecimals", getDolGlobalString('MAIN_MAX_DECIMALS_UNIT')), 1, 'help');
2047 print '</td><td>';
2048 if ($object->price_base_type == 'TTC') {
2049 print '<input name="price" size="10" value="'.price($object->price_ttc).'">';
2050 } else {
2051 print '<input name="price" size="10" value="'.price($object->price).'">';
2052 }
2053 print '</td></tr>';
2054
2055 // Price minimum
2056 print '<tr><td>';
2057 $text = $langs->trans('MinPrice');
2058 print $form->textwithpicto($text, $langs->trans("PrecisionUnitIsLimitedToXDecimals", getDolGlobalString('MAIN_MAX_DECIMALS_UNIT')), 1, 'help');
2059 if ($object->price_base_type == 'TTC') {
2060 print '<td><input name="price_min" size="10" value="'.price($object->price_min_ttc).'">';
2061 } else {
2062 print '<td><input name="price_min" size="10" value="'.price($object->price_min).'">';
2063 }
2064 if (getDolGlobalString('PRODUCT_MINIMUM_RECOMMENDED_PRICE')) {
2065 print '<td class="left">'.$langs->trans("MinimumRecommendedPrice", price($maxpricesupplier, 0, '', 1, -1, -1, 'auto')).' '.img_warning().'</td>';
2066 }
2067 print '</td></tr>';
2068
2069 // Price Label
2070 print '<tr><td>';
2071 print $langs->trans('PriceLabel');
2072 print '</td><td>';
2073 print '<input name="price_label" maxlength="255" class="minwidth300 maxwidth400onsmartphone" value="'.$object->price_label.'">';
2074 print '</td>';
2075 print '</tr>';
2076
2077 // Discount
2078 $discount_percent = price2num(GETPOST("discount_percent"));
2079 print '<tr><td>'.$langs->trans("Discount").'</td><td>';
2080 print '<input name="discount_percent" size="10" value="'.price($discount_percent).'">';
2081 print '</td></tr>';
2082
2083 // Extrafields
2084 $extrafields->fetch_name_optionals_label("product_customer_price");
2085 $extralabels = !empty($extrafields->attributes["product_customer_price"]['label']) ? $extrafields->attributes["product_customer_price"]['label'] : '';
2086 $extrafield_values = $extrafields->getOptionalsFromPost("product_customer_price");
2087 if (!empty($extralabels)) {
2088 if (empty($prodcustprice->id)) {
2089 foreach ($extralabels as $key => $value) {
2090 if (!empty($extrafields->attributes["product_customer_price"]['list'][$key]) && ($extrafields->attributes["product_customer_price"]['list'][$key] == 1 || $extrafields->attributes["product_customer_price"]['list'][$key] == 3 || ($action == "add_customer_price" && $extrafields->attributes["product_customer_price"]['list'][$key] == 4))) {
2091 if (!empty($extrafields->attributes["product_customer_price"]['langfile'][$key])) {
2092 $langs->load($extrafields->attributes["product_customer_price"]['langfile'][$key]);
2093 }
2094
2095 print '<tr><td'.($extrafields->attributes["product_customer_price"]['required'][$key] ? ' class="fieldrequired"' : '').'>';
2096 if (!empty($extrafields->attributes["product_customer_price"]['help'][$key])) {
2097 print $form->textwithpicto($langs->trans($value), $langs->trans($extrafields->attributes["product_customer_price"]['help'][$key]));
2098 } else {
2099 print $langs->trans($value);
2100 }
2101 print '</td><td>'.$extrafields->showInputField($key, GETPOSTISSET('options_'.$key) ? $extrafield_values['options_'.$key] : '', '', '', '', '', 0, 'product_customer_price').'</td></tr>';
2102 }
2103 }
2104 }
2105 }
2106
2107 print '</table>';
2108
2109 print '</div>';
2110
2111
2112 print '<div class="center">';
2113
2114 // Update all child soc
2115 print '<div class="marginbottomonly">';
2116 print '<input type="checkbox" name="updatechildprice" id="updatechildprice" value="1"> ';
2117 print '<label for="updatechildprice">'.$langs->trans('ForceUpdateChildPriceSoc').'</label>';
2118 print '</div>';
2119
2120 print $form->buttonsSaveCancel();
2121
2122 print '</form>';
2123 } elseif ($action == 'edit_customer_price') {
2124 // Edit mode
2125 $maxpricesupplier = $object->min_recommended_price();
2126
2127 print '<!-- edit_customer_price -->';
2128 print load_fiche_titre($langs->trans('PriceByCustomer'));
2129
2130 $result = $prodcustprice->fetch(GETPOSTINT('lineid'));
2131 if ($result < 0) {
2132 setEventMessages($prodcustprice->error, $prodcustprice->errors, 'errors');
2133 }
2134
2135 print '<form action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="POST">';
2136 print '<input type="hidden" name="token" value="'.newToken().'">';
2137 print '<input type="hidden" name="action" value="update_customer_price_confirm">';
2138 print '<input type="hidden" name="lineid" value="'.$prodcustprice->id.'">';
2139
2140 print '<table class="liste centpercent">';
2141 print '<tr>';
2142 print '<td class="titlefield fieldrequired">'.$langs->trans('ThirdParty').'</td>';
2143 $staticsoc = new Societe($db);
2144 $staticsoc->fetch($prodcustprice->fk_soc);
2145 print "<td>".$staticsoc->getNomUrl(1)."</td>";
2146 print '</tr>';
2147
2148 // Ref. Customer
2149 print '<tr><td>' . $langs->trans('RefCustomer') . '</td>';
2150 print '<td><input name="ref_customer" size="12" value="' . dol_escape_htmltag($prodcustprice->ref_customer) . '"></td></tr>';
2151
2152 // Applied Prices From
2153 print '<tr><td>'.$langs->trans("AppliedPricesFrom").'</td><td>';
2154 print $form->selectDate($prodcustprice->date_begin, "date_begin", 0, 0, 1, "date_begin");
2155 print '</td></tr>';
2156
2157 // Applied Prices To
2158 print '<tr><td>'.$langs->trans("AppliedPricesTo").'</td><td>';
2159 print $form->selectDate($prodcustprice->date_end, "date_end", 0, 0, 1, "date_end");
2160 print '</td></tr>';
2161
2162 // VAT
2163 print '<tr><td class="fieldrequired">'.$langs->trans("DefaultTaxRate").'</td><td>';
2164 print $form->load_tva("tva_tx", $prodcustprice->default_vat_code ? $prodcustprice->tva_tx.' ('.$prodcustprice->default_vat_code.')' : $prodcustprice->tva_tx, $mysoc, null, $object->id, $prodcustprice->recuperableonly, $object->type, false, 1);
2165 print '</td></tr>';
2166
2167 // Price base
2168 print '<tr><td class="fieldrequired">';
2169 print $langs->trans('PriceBase');
2170 print '</td>';
2171 print '<td>';
2172 print $form->selectPriceBaseType($prodcustprice->price_base_type, "price_base_type");
2173 print '</td>';
2174 print '</tr>';
2175
2176 // Price
2177 print '<tr><td class="fieldrequired">';
2178 $text = $langs->trans('SellingPrice');
2179 print $form->textwithpicto($text, $langs->trans("PrecisionUnitIsLimitedToXDecimals", getDolGlobalString('MAIN_MAX_DECIMALS_UNIT')), 1, 'help');
2180 print '</td><td>';
2181 if ($prodcustprice->price_base_type == 'TTC') {
2182 print '<input name="price" size="10" value="'.price($prodcustprice->price_ttc).'">';
2183 } else {
2184 print '<input name="price" size="10" value="'.price($prodcustprice->price).'">';
2185 }
2186 print '</td></tr>';
2187
2188 // Price minimum
2189 print '<tr><td>';
2190 $text = $langs->trans('MinPrice');
2191 print $form->textwithpicto($text, $langs->trans("PrecisionUnitIsLimitedToXDecimals", getDolGlobalString('MAIN_MAX_DECIMALS_UNIT')), 1, 'help');
2192 print '</td><td>';
2193 if ($prodcustprice->price_base_type == 'TTC') {
2194 print '<input name="price_min" size="10" value="'.price($prodcustprice->price_min_ttc).'">';
2195 } else {
2196 print '<input name="price_min" size="10" value="'.price($prodcustprice->price_min).'">';
2197 }
2198 print '</td>';
2199 if (getDolGlobalString('PRODUCT_MINIMUM_RECOMMENDED_PRICE')) {
2200 print '<td class="left">'.$langs->trans("MinimumRecommendedPrice", price($maxpricesupplier, 0, '', 1, -1, -1, 'auto')).' '.img_warning().'</td>';
2201 }
2202 print '</tr>';
2203
2204 // Price Label
2205 print '<tr><td>';
2206 print $langs->trans('PriceLabel');
2207 print '</td><td>';
2208 print '<input name="price_label" maxlength="255" class="minwidth300 maxwidth400onsmartphone" value="'.$prodcustprice->price_label.'">';
2209 print '</td>';
2210 print '</tr>';
2211
2212 // Discount
2213 print '<tr><td>'.$langs->trans("Discount").'</td><td>';
2214 print '<input name="discount_percent" size="10" value="'.price($prodcustprice->discount_percent).'">';
2215 print '</td></tr>';
2216
2217 // Extrafields
2218 $extrafields->fetch_name_optionals_label("product_customer_price");
2219 $extralabels = !empty($extrafields->attributes["product_customer_price"]['label']) ? $extrafields->attributes["product_customer_price"]['label'] : '';
2220 $extrafield_values = $extrafields->getOptionalsFromPost("product_customer_price");
2221 if (!empty($extralabels)) {
2222 if (empty($object->id)) {
2223 foreach ($extralabels as $key => $value) {
2224 if (!empty($extrafields->attributes["product_customer_price"]['list'][$key]) && ($extrafields->attributes["product_customer_price"]['list'][$key] == 1 || $extrafields->attributes["product_customer_price"]['list'][$key] == 3 || ($action == "edit_price" && $extrafields->attributes["product_customer_price"]['list'][$key] == 4))) {
2225 if (!empty($extrafields->attributes["product_customer_price"]['langfile'][$key])) {
2226 $langs->load($extrafields->attributes["product_customer_price"]['langfile'][$key]);
2227 }
2228
2229 print '<tr><td'.($extrafields->attributes["product_customer_price"]['required'][$key] ? ' class="fieldrequired"' : '').'>';
2230 if (!empty($extrafields->attributes["product_customer_price"]['help'][$key])) {
2231 print $form->textwithpicto($langs->trans($value), $langs->trans($extrafields->attributes["product_customer_price"]['help'][$key]));
2232 } else {
2233 print $langs->trans($value);
2234 }
2235 print '</td><td>'.$extrafields->showInputField($key, GETPOSTISSET('options_'.$key) ? $extrafield_values['options_'.$key] : '', '', '', '', '', 0, 'product_customer_price').'</td></tr>';
2236 }
2237 }
2238 } else {
2239 $sql = "SELECT";
2240 $sql .= " fk_object";
2241 foreach ($extralabels as $key => $value) {
2242 $sql .= ", ".$db->sanitize($key);
2243 }
2244 $sql .= " FROM ".MAIN_DB_PREFIX."product_customer_price_extrafields";
2245 $sql .= " WHERE fk_object = ".((int) $prodcustprice->id);
2246 $resql = $db->query($sql);
2247 if ($resql) {
2248 $obj = $db->fetch_object($resql);
2249 foreach ($extralabels as $key => $value) {
2250 if (!empty($extrafields->attributes["product_customer_price"]['list'][$key]) && ($extrafields->attributes["product_customer_price"]['list'][$key] == 1 || $extrafields->attributes["product_customer_price"]['list'][$key] == 3 || ($action == "edit_price" && $extrafields->attributes["product_customer_price"]['list'][$key] == 4))) {
2251 if (!empty($extrafields->attributes["product_customer_price"]['langfile'][$key])) {
2252 $langs->load($extrafields->attributes["product_customer_price"]['langfile'][$key]);
2253 }
2254
2255 print '<tr><td'.($extrafields->attributes["product_customer_price"]['required'][$key] ? ' class="fieldrequired"' : '').'>';
2256 if (!empty($extrafields->attributes["product_customer_price"]['help'][$key])) {
2257 print $form->textwithpicto($langs->trans($value), $langs->trans($extrafields->attributes["product_customer_price"]['help'][$key]));
2258 } else {
2259 print $langs->trans($value);
2260 }
2261 print '</td><td>'.$extrafields->showInputField($key, GETPOSTISSET('options_'.$key) ? $extrafield_values['options_'.$key] : $obj->{$key}, '', '', '', '', 0, 'product_customer_price');
2262
2263 print '</td></tr>';
2264 }
2265 }
2266 $db->free($resql);
2267 }
2268 }
2269 }
2270
2271 print '</table>';
2272
2273 print '<div class="center">';
2274 print '<div class="marginbottomonly">';
2275 print '<input type="checkbox" name="updatechildprice" id="updatechildprice" value="1"> ';
2276 print '<label for="updatechildprice">'.$langs->trans('ForceUpdateChildPriceSoc').'</label>';
2277 print "</div>";
2278
2279 print $form->buttonsSaveCancel();
2280
2281 print '<br></form>';
2282 } elseif ($action == 'showlog_customer_price') {
2283 // List of all log of prices by customers
2284 print '<!-- list of all log of prices per customer -->'."\n";
2285
2286 $sortfield = 't.datec';
2287 $filter = array('t.fk_product' => (string) $object->id, 't.fk_soc' => (string) GETPOSTINT('socid'));
2288
2289 // Count total nb of records
2290 $nbtotalofrecords = '';
2291 if (!getDolGlobalInt('MAIN_DISABLE_FULL_SCANLIST')) {
2292 $nbtotalofrecords = $prodcustprice->fetchAllLog($sortorder, $sortfield, $conf->liste_limit, $offset, $filter);
2293 }
2294
2295 $result = $prodcustprice->fetchAllLog($sortorder, $sortfield, $conf->liste_limit, $offset, $filter);
2296 if ($result < 0) {
2297 setEventMessages($prodcustprice->error, $prodcustprice->errors, 'errors');
2298 }
2299
2300 $option = '&socid='.GETPOSTINT('socid').'&id='.$object->id;
2301
2302 $staticsoc = new Societe($db);
2303 $staticsoc->fetch(GETPOSTINT('socid'));
2304
2305 $title = $langs->trans('PriceByCustomerLog');
2306 $title .= ' - '.$staticsoc->getNomUrl(1);
2307
2308 $backbutton = '<a class="justalink" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'">'.$langs->trans("GoBack").'</a>';
2309
2310 // @phan-suppress-next-line PhanPluginSuspiciousParamOrder
2311 print_barre_liste($title, $page, $_SERVER['PHP_SELF'], $option, $sortfield, $sortorder, $backbutton, count($prodcustprice->lines), $nbtotalofrecords, 'title_accountancy.png');
2312
2313 if (count($prodcustprice->lines) > 0) {
2314 print '<form action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="POST">';
2315 print '<input type="hidden" name="token" value="'.newToken().'">';
2316 print '<input type="hidden" name="id" value="'.$object->id.'">';
2317
2318 print '<div class="div-table-responsive-no-min">';
2319 print '<table class="liste centpercent">';
2320
2321 print '<tr class="liste_titre">';
2322 print '<td>'.$langs->trans("ThirdParty").'</td>';
2323 print '<td>'.$langs->trans('RefCustomer').'</td>';
2324 print '<td>'.$langs->trans("AppliedPricesFrom").'</td>';
2325 print '<td>'.$langs->trans("AppliedPricesTo").'</td>';
2326 print '<td class="center">'.$langs->trans("PriceBase").'</td>';
2327 print '<td class="right">'.$langs->trans("DefaultTaxRate").'</td>';
2328 print '<td class="right">'.$langs->trans("HT").'</td>';
2329 print '<td class="right">'.$langs->trans("TTC").'</td>';
2330 if ($mysoc->localtax1_assuj == "1" || $mysoc->localtax2_assuj == "1") {
2331 print '<td class="right">'.$langs->trans("INCT").'</td>';
2332 }
2333 print '<td class="right">'.$langs->trans("MinPrice").' '.$langs->trans("HT").'</td>';
2334 print '<td class="right">'.$langs->trans("MinPrice").' '.$langs->trans("TTC").'</td>';
2335 print '<td class="right">'.$langs->trans("PriceLabel").'</td>';
2336 print '<td class="right">'.$langs->trans("Discount").'</td>';
2337 print '<td class="right">'.$langs->trans("ChangedBy").'</td>';
2338 print '<td>'.$langs->trans("DateCreation").'</td>';
2339 print '</tr>';
2340
2341 foreach ($prodcustprice->lines as $line) {
2342 // Date
2343 $staticsoc = new Societe($db);
2344 $staticsoc->fetch($line->fk_soc);
2345
2346 $tva_tx = $line->default_vat_code ? $line->tva_tx.' ('.$line->default_vat_code.')' : $line->tva_tx;
2347
2348 // Line for default price
2349 if ($line->price_base_type == 'HT') {
2350 $pu = $line->price;
2351 } else {
2352 $pu = $line->price_ttc;
2353 }
2354
2355 // Local tax is not saved into table of product. We use value linked to VAT code.
2356 $localtaxarray = getLocalTaxesFromRate($line->tva_tx.($line->default_vat_code ? ' ('.$line->default_vat_code.')' : ''), 0, $staticsoc, $mysoc);
2357 // Define part of HT, VAT, TTC
2358 $resultarray = calcul_price_total(1, $pu, 0, (float) $line->tva_tx, 1, 1, 0, $line->price_base_type, $line->recuperableonly, $object->type, $mysoc, $localtaxarray);
2359 // Calcul du total ht sans remise
2360 $total_ht = $resultarray[0];
2361 $total_vat = $resultarray[1];
2362 $total_localtax1 = $resultarray[9];
2363 $total_localtax2 = $resultarray[10];
2364 $total_ttc = $resultarray[2];
2365
2366 print '<tr class="oddeven">';
2367
2368 print '<td class="tdoverflowmax125">'.$staticsoc->getNomUrl(1)."</td>";
2369 print '<td>'.$line->ref_customer.'</td>';
2370 print "<td>".dol_print_date($line->date_begin, "day", 'tzuserrel')."</td>";
2371 print "<td>".dol_print_date($line->date_end, "day", 'tzuserrel')."</td>";
2372 print '<td class="center">'.$langs->trans($line->price_base_type)."</td>";
2373 print '<td class="right nowraponall">';
2374
2375 $positiverates = '';
2376 if (price2num($line->tva_tx)) {
2377 $positiverates .= ($positiverates ? '/' : '').price2num($line->tva_tx);
2378 }
2379 if (price2num($line->localtax1_type)) {
2380 $positiverates .= ($positiverates ? '/' : '').price2num($line->localtax1_tx);
2381 }
2382 if (price2num($line->localtax2_type)) {
2383 $positiverates .= ($positiverates ? '/' : '').price2num($line->localtax2_tx);
2384 }
2385 if (empty($positiverates)) {
2386 $positiverates = '0';
2387 }
2388
2389 echo vatrate($positiverates.($line->default_vat_code ? ' ('.$line->default_vat_code.')' : ''), true, ($line->tva_npr ? $line->tva_npr : $line->recuperableonly));
2390
2391 //. vatrate($tva_tx, true, $line->recuperableonly) .
2392 print "</td>";
2393 print '<td class="right"><span class="amount">'.price($line->price)."</span></td>";
2394
2395 print '<td class="right"><span class="amount">'.price($line->price_ttc)."</span></td>";
2396 if ($mysoc->localtax1_assuj == "1" || $mysoc->localtax2_assuj == "1") {
2397 print '<td class="right">'.price($resultarray[2]).'</td>';
2398 }
2399
2400 print '<td class="right">'.price($line->price_min).'</td>';
2401 print '<td class="right">'.price($line->price_min_ttc).'</td>';
2402 print '<td class="right">'.dolPrintHTML($line->price_label).'</td>';
2403 print '<td class="right">'.price($line->discount_percent).'</td>';
2404
2405 // User
2406 print '<td class="tdoverflowmax125">';
2407 // @TODO Add a cache on $userstatic
2408 if ($line->fk_user > 0) {
2409 $userstatic = new User($db);
2410 $userstatic->fetch($line->fk_user);
2411 print $userstatic->getNomUrl(1, '', 0, 0, 24, 0, 'login');
2412 }
2413 print '</td>';
2414
2415 print "<td>".dol_print_date($line->datec, "dayhour", 'tzuserrel')."</td>";
2416 print '</tr>';
2417 }
2418 print "</table>";
2419 print '</div>';
2420 } else {
2421 print $langs->trans('None');
2422 }
2423 } elseif ($action != 'showlog_default_price' && $action != 'edit_price' && $action != 'edit_level_price') {
2424 // List of all prices by customers
2425 print '<!-- list of all prices per customer -->'."\n";
2426
2427 // Count total nb of records
2428 $nbtotalofrecords = '';
2429 if (!getDolGlobalInt('MAIN_DISABLE_FULL_SCANLIST')) {
2430 $nbtotalofrecords = $prodcustprice->fetchAll($sortorder, $sortfield, 0, 0, $filter);
2431 }
2432
2433 $result = $prodcustprice->fetchAll($sortorder, $sortfield, $conf->liste_limit, $offset, $filter);
2434 if ($result < 0) {
2435 setEventMessages($prodcustprice->error, $prodcustprice->errors, 'errors');
2436 }
2437
2438 $option = '&search_soc='.$search_soc.'&id='.$object->id;
2439
2440 // @phan-suppress-next-line PhanPluginSuspiciousParamOrder
2441 print_barre_liste($langs->trans('PriceByCustomer'), $page, $_SERVER ['PHP_SELF'], $option, $sortfield, $sortorder, '', count($prodcustprice->lines), $nbtotalofrecords, 'title_accountancy.png');
2442
2443 print '<form action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="POST">';
2444 print '<input type="hidden" name="token" value="'.newToken().'">';
2445 print '<input type="hidden" name="id" value="'.$object->id.'">';
2446
2447 print '<!-- List of prices per customer -->'."\n";
2448 print '<div class="div-table-responsive-no-min">'."\n";
2449 print '<table class="liste centpercent">'."\n";
2450
2451 if (count($prodcustprice->lines) > 0 || $search_soc) {
2452 $extrafields->fetch_name_optionals_label("product_customer_price");
2453 $custom_price_extralabels = !empty($extrafields->attributes["product_customer_price"]['label']) ? $extrafields->attributes["product_customer_price"]['label'] : '';
2454 if (getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
2455 $colspan = 11;
2456 } else {
2457 $colspan = 12;
2458 }
2459 if ($mysoc->localtax1_assuj == "1" || $mysoc->localtax2_assuj == "1") {
2460 $colspan++;
2461 }
2462 if (!empty($custom_price_extralabels) && is_array($custom_price_extralabels)) {
2463 $colspan += count($custom_price_extralabels);
2464 }
2465
2466 print '<tr class="liste_titre">';
2467 print '<td class="liste_titre"><input type="text" class="flat maxwidth125" name="search_soc" value="'.$search_soc.'"></td>';
2468 print '<td class="liste_titre" colspan="'.$colspan.'">&nbsp;</td>';
2469 // Print the search button
2470 print '<td class="liste_titre maxwidthsearch">';
2471 $searchpicto = $form->showFilterAndCheckAddButtons(0);
2472 print $searchpicto;
2473 print '</td>';
2474 print '</tr>';
2475 }
2476
2477 print '<tr class="liste_titre">';
2478 print '<td>'.$langs->trans("ThirdParty").'</td>';
2479 print '<td>'.$langs->trans('RefCustomer').'</td>';
2480 print '<td>'.$langs->trans("AppliedPricesFrom").'</td>';
2481 print '<td>'.$langs->trans("AppliedPricesTo").'</td>';
2482 print '<td class="center">'.$langs->trans("PriceBase").'</td>';
2483 print '<td class="right">'.$langs->trans("DefaultTaxRate").'</td>';
2484 print '<td class="right">'.$langs->trans("HT").'</td>';
2485 print '<td class="right">'.$langs->trans("TTC").'</td>';
2486 if ($mysoc->localtax1_assuj == "1" || $mysoc->localtax2_assuj == "1") {
2487 print '<td class="right">'.$langs->trans("INCT").'</td>';
2488 }
2489 print '<td class="right">'.$langs->trans("MinPrice").' '.$langs->trans("HT").'</td>';
2490 print '<td class="right">'.$langs->trans("MinPrice").' '.$langs->trans("TTC").'</td>';
2491 print '<td class="right">'.$langs->trans("PriceLabel").'</td>';
2492 print '<td class="right">'.$langs->trans("Discount").'</td>';
2493 // fetch optionals attributes and labels
2494 $extrafields->fetch_name_optionals_label("product_customer_price");
2495 if ($extrafields->attributes["product_customer_price"] && array_key_exists('label', $extrafields->attributes["product_customer_price"])) {
2496 $extralabels = $extrafields->attributes["product_customer_price"]['label'];
2497 if (!empty($extralabels)) {
2498 foreach ($extralabels as $key => $value) {
2499 // Show field if not hidden
2500 if (!empty($extrafields->attributes["product_customer_price"]['list'][$key]) && $extrafields->attributes["product_customer_price"]['list'][$key] != 3) {
2501 if (!empty($extrafields->attributes["product_customer_price"]['langfile'][$key])) {
2502 $langs->load($extrafields->attributes["product_customer_price"]['langfile'][$key]);
2503 }
2504 if (!empty($extrafields->attributes["product_customer_price"]['help'][$key])) {
2505 $extratitle = $form->textwithpicto($langs->trans($value), $langs->trans($extrafields->attributes["product_customer_price"]['help'][$key]));
2506 } else {
2507 $extratitle = $langs->trans($value);
2508 }
2509 print '<td style="text-align: right">'.$extratitle.'</td>';
2510 }
2511 }
2512 }
2513 }
2514 print '<td>'.$langs->trans("ChangedBy").'</td>';
2515 print '<td></td>';
2516 print '</tr>';
2517
2518 // Line for default price
2519 if ($object->price_base_type == 'HT') {
2520 $pu = $object->price;
2521 } else {
2522 $pu = $object->price_ttc;
2523 }
2524
2525 // Local tax was not saved into table llx_product on old versions. So we will use the value linked to the VAT code.
2526 $localtaxarray = getLocalTaxesFromRate($object->tva_tx.($object->default_vat_code ? ' ('.$object->default_vat_code.')' : ''), 0, $mysoc, $mysoc);
2527 // Define part of HT, VAT, TTC
2528 $resultarray = calcul_price_total(1, $pu, 0, $object->tva_tx, 1, 1, 0, $object->price_base_type, 0, $object->type, $mysoc, $localtaxarray);
2529 // Calcul du total ht sans remise
2530 $total_ht = $resultarray[0];
2531 $total_vat = $resultarray[1];
2532 $total_localtax1 = $resultarray[9];
2533 $total_localtax2 = $resultarray[10];
2534 $total_ttc = $resultarray[2];
2535
2536 if (!getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
2537 print '<!-- PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES -->'."\n";
2538 print '<tr class="oddeven">';
2539 print '<td colspan="4">' . $langs->trans('Default') . '</td>';
2540
2541 print '<td class="center">'.$langs->trans($object->price_base_type)."</td>";
2542
2543 // VAT Rate
2544 print '<td class="right nowraponall">';
2545
2546 $positiverates = '';
2547 if (price2num($object->tva_tx)) {
2548 $positiverates .= ($positiverates ? '/' : '').price2num($object->tva_tx);
2549 }
2550 if (price2num($object->localtax1_type)) {
2551 $positiverates .= ($positiverates ? '/' : '').price2num($object->localtax1_tx);
2552 }
2553 if (price2num($object->localtax2_type)) {
2554 $positiverates .= ($positiverates ? '/' : '').price2num($object->localtax2_tx);
2555 }
2556 if (empty($positiverates)) {
2557 $positiverates = '0';
2558 }
2559 echo vatrate($positiverates.($object->default_vat_code ? ' ('.$object->default_vat_code.')' : ''), true, $object->tva_npr);
2560
2561 print "</td>";
2562
2563 print '<td class="right"><span class="amount">'.price($object->price)."</span></td>";
2564
2565 print '<td class="right"><span class="amount">'.price($object->price_ttc)."</span></td>";
2566 if ($mysoc->localtax1_assuj == "1" || $mysoc->localtax2_assuj == "1") {
2567 //print '<td class="right">' . price($object->price_ttc) . "</td>";
2568 print '<td class="right"><span class="amount">'.price($resultarray[2]).'</span></td>';
2569 }
2570
2571 print '<td class="right">'.price($object->price_min).'</td>';
2572 print '<td class="right">'.price($object->price_min_ttc).'</td>';
2573 print '<td class="right">'.dolPrintHTML($object->price_label).'</td>';
2574 print '<td class="right"></td>';
2575 print '<td class="right"></td>';
2576 if (!empty($extralabels)) {
2577 foreach ($extralabels as $key) {
2578 // Show field if not hidden
2579 if (!empty($extrafields->attributes["product_customer_price"]['list'][$key]) && $extrafields->attributes["product_customer_price"]['list'][$key] != 3) {
2580 print '<td class="right"></td>';
2581 }
2582 }
2583 }
2584 if ($user->hasRight('produit', 'supprimer') || $user->hasRight('service', 'supprimer')) {
2585 print '<td class="nowraponall">';
2586 print '<a class="marginleftonly marginrightonly" href="'.$_SERVER["PHP_SELF"].'?action=showlog_default_price&token='.newToken().'&id='.$object->id.'">';
2587 print img_info($langs->trans('PriceByCustomerLog'));
2588 print '</a>';
2589 print ' ';
2590 print '<a class="marginleftonly marginrightonly editfielda" href="'.$_SERVER["PHP_SELF"].'?action=edit_price&token='.newToken().'&id='.$object->id.'">';
2591 print img_edit('default', 0, 'style="vertical-align: middle;"');
2592 print '</a>';
2593 print '</td>';
2594 }
2595 print "</tr>\n";
2596 }
2597
2598 if (count($prodcustprice->lines) > 0) {
2599 foreach ($prodcustprice->lines as $line) {
2600 // Date
2601 $staticsoc = new Societe($db);
2602 $staticsoc->fetch($line->fk_soc);
2603
2604 $tva_tx = $line->default_vat_code ? $line->tva_tx.' ('.$line->default_vat_code.')' : $line->tva_tx;
2605
2606 // Line for default price
2607 if ($line->price_base_type == 'HT') {
2608 $pu = $line->price;
2609 } else {
2610 $pu = $line->price_ttc;
2611 }
2612
2613 // Local tax is not saved into table of product. We use value linked to VAT code.
2614 $localtaxarray = getLocalTaxesFromRate($line->tva_tx.($line->default_vat_code ? ' ('.$line->default_vat_code.')' : ''), 0, $staticsoc, $mysoc);
2615 // Define part of HT, VAT, TTC
2616 $resultarray = calcul_price_total(1, $pu, 0, (float) $line->tva_tx, 1, 1, 0, $line->price_base_type, $line->recuperableonly, $object->type, $mysoc, $localtaxarray);
2617 // Calcul du total ht sans remise
2618 $total_ht = $resultarray[0];
2619 $total_vat = $resultarray[1];
2620 $total_localtax1 = $resultarray[9];
2621 $total_localtax2 = $resultarray[10];
2622 $total_ttc = $resultarray[2];
2623
2624 print '<tr class="oddeven">';
2625
2626 print '<td class="tdoverflowmax125">'.$staticsoc->getNomUrl(1)."</td>";
2627 print '<td>'.dolPrintHTML($line->ref_customer).'</td>';
2628 print "<td>".dol_print_date($line->date_begin, "day", 'tzuserrel')."</td>";
2629 print "<td>".dol_print_date($line->date_end, "day", 'tzuserrel')."</td>";
2630 print '<td class="center">'.$langs->trans($line->price_base_type)."</td>";
2631 // VAT Rate
2632 print '<td class="right nowraponall">';
2633
2634 $positiverates = '';
2635 if (price2num($line->tva_tx)) {
2636 $positiverates .= ($positiverates ? '/' : '').price2num($line->tva_tx);
2637 }
2638 if (price2num($line->localtax1_type)) {
2639 $positiverates .= ($positiverates ? '/' : '').price2num($line->localtax1_tx);
2640 }
2641 if (price2num($line->localtax2_type)) {
2642 $positiverates .= ($positiverates ? '/' : '').price2num($line->localtax2_tx);
2643 }
2644 if (empty($positiverates)) {
2645 $positiverates = '0';
2646 }
2647
2648 echo vatrate($positiverates.($line->default_vat_code ? ' ('.$line->default_vat_code.')' : ''), true, (!empty($line->tva_npr) ? $line->tva_npr : $line->recuperableonly));
2649
2650 print "</td>";
2651
2652 print '<td class="right"><span class="amount">'.price($line->price)."</span></td>";
2653
2654 print '<td class="right"><span class="amount">'.price($line->price_ttc)."</span></td>";
2655 if ($mysoc->localtax1_assuj == "1" || $mysoc->localtax2_assuj == "1") {
2656 //print '<td class="right">' . price($line->price_ttc) . "</td>";
2657 print '<td class="right"><span class="amount">'.price($resultarray[2]).'</span></td>';
2658 }
2659
2660 print '<td class="right">'.price($line->price_min).'</td>';
2661 print '<td class="right">'.price($line->price_min_ttc).'</td>';
2662 print '<td class="right">'.dolPrintHTMLForAttribute($line->price_label).'</td>';
2663 print '<td class="right">'.price($line->discount_percent).'</td>';
2664
2665 // Extrafields
2666 $extrafields->fetch_name_optionals_label("product_customer_price");
2667 $extralabels = $extrafields->attributes["product_customer_price"]['label'] ?? array();
2668 if (!empty($extralabels)) {
2669 $sql = "SELECT";
2670 $sql .= " fk_object";
2671 foreach ($extralabels as $key => $value) {
2672 $sql .= ", ".$db->sanitize($key);
2673 }
2674 $sql .= " FROM ".MAIN_DB_PREFIX."product_customer_price_extrafields";
2675 $sql .= " WHERE fk_object = ".((int) $line->id);
2676 $resql = $db->query($sql);
2677 if ($resql) {
2678 if ($db->num_rows($resql) != 1) {
2679 foreach ($extralabels as $key => $value) {
2680 if (!empty($extrafields->attributes["product_customer_price"]['list'][$key]) && $extrafields->attributes["product_customer_price"]['list'][$key] != 3) {
2681 print "<td></td>";
2682 }
2683 }
2684 } else {
2685 $obj = $db->fetch_object($resql);
2686 foreach ($extralabels as $key => $value) {
2687 if (!empty($extrafields->attributes["product_customer_price"]['list'][$key]) && $extrafields->attributes["product_customer_price"]['list'][$key] != 3) {
2688 print '<td align="right">'.$extrafields->showOutputField($key, $obj->{$key}, '', 'product_customer_price')."</td>";
2689 }
2690 }
2691 }
2692 $db->free($resql);
2693 }
2694 }
2695
2696 // User
2697 print '<td class="tdoverflowmax125">';
2698 if ($line->fk_user > 0) {
2699 $userstatic = new User($db);
2700 $userstatic->fetch($line->fk_user);
2701 // @TODO Add a cache on $userstatic object
2702 print $userstatic->getNomUrl(-1, '', 0, 0, 24, 0, 'login');
2703 }
2704 print '</td>';
2705
2706 // Todo Edit or delete button
2707 // Action
2708 print '<td class="right nowraponall">';
2709 if ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer')) {
2710 print '<a href="'.$_SERVER["PHP_SELF"].'?action=showlog_customer_price&token='.newToken().'&id='.$object->id.'&socid='.$line->fk_soc.'">';
2711 print img_info($langs->trans('PriceByCustomerLog'));
2712 print '</a>';
2713 print ' ';
2714 print '<a class="marginleftonly editfielda" href="'.$_SERVER["PHP_SELF"].'?action=edit_customer_price&token='.newToken().'&id='.$object->id.'&lineid='.$line->id.'">';
2715 print img_edit('default', 0, 'style="vertical-align: middle;"');
2716 print '</a>';
2717 print ' ';
2718 }
2719 if ($user->hasRight('produit', 'supprimer') || $user->hasRight('service', 'supprimer')) {
2720 print '<a class="marginleftonly" href="'.$_SERVER["PHP_SELF"].'?action=delete_customer_price&token='.newToken().'&id='.$object->id.'&lineid='.$line->id.'">';
2721 print img_delete('default', 'style="vertical-align: middle;"');
2722 print '</a>';
2723 }
2724 print '</td>';
2725
2726 print "</tr>\n";
2727 }
2728 }
2729
2730 print "</table>";
2731 print '</div>';
2732
2733 print "</form>";
2734 }
2735}
2736
2737// List of price changes - log historic (ordered by descending date)
2738
2739if ((!getDolGlobalString('PRODUIT_CUSTOMER_PRICES') || $action == 'showlog_default_price') && !in_array($action, array('edit_price', 'edit_level_price', 'edit_vat'))) {
2740 $sql = "SELECT p.rowid, p.price, p.price_ttc, p.price_base_type, p.tva_tx, p.default_vat_code, p.recuperableonly, p.localtax1_tx, p.localtax1_type, p.localtax2_tx, p.localtax2_type,";
2741 $sql .= " p.price_level, p.price_min, p.price_min_ttc,p.price_by_qty,";
2742 $sql .= " p.date_price as dp, p.fk_price_expression, u.rowid as user_id, u.login";
2743 $sql .= " ,p.price_label";
2744 $sql .= " FROM ".MAIN_DB_PREFIX."product_price as p,";
2745 $sql .= " ".MAIN_DB_PREFIX."user as u";
2746 $sql .= " WHERE fk_product = ".((int) $object->id);
2747 $sql .= " AND p.entity IN (".getEntity('productprice').")";
2748 $sql .= " AND p.fk_user_author = u.rowid";
2749 if (!empty($socid) && (getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) && $soc !== null) {
2750 $sql .= " AND p.price_level = ".((int) $soc->price_level);
2751 }
2752 $sql .= " ORDER BY p.date_price DESC, p.rowid DESC, p.price_level ASC";
2753 // $sql .= $db->plimit();
2754 //print $sql;
2755
2756 $result = $db->query($sql);
2757 if ($result) {
2758 print '<div class="divlogofpreviouscustomerprice">';
2759
2760 $num = $db->num_rows($result);
2761
2762 if (!$num) {
2763 $db->free($result);
2764
2765 // We must have at least one initial line
2766 // We add it to fix this if not (trouble with old versions)
2767 // We emulate the change of the price from interface with the same value than the one into table llx_product
2768 if (getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
2769 $ret = $object->updatePrice(($object->multiprices_base_type[1] == 'TTC' ? $object->multiprices_ttc[1] : $object->multiprices[1]), $object->multiprices_base_type[1], $user, (empty($object->multiprices_tva_tx[1]) ? 0 : $object->multiprices_tva_tx[1]), ($object->multiprices_base_type[1] == 'TTC' ? $object->multiprices_min_ttc[1] : $object->multiprices_min[1]), 1, 0, 0, 0, array(), $object->default_vat_code);
2770 } else {
2771 $ret = $object->updatePrice(($object->price_base_type == 'TTC' ? $object->price_ttc : $object->price), $object->price_base_type, $user, $object->tva_tx, ($object->price_base_type == 'TTC' ? $object->price_min_ttc : $object->price_min), 0, $object->tva_npr, 0, 0, array(), $object->default_vat_code);
2772 }
2773
2774 if ($ret < 0) {
2775 dol_print_error($db, $object->error, $object->errors);
2776 } else {
2777 $result = $db->query($sql);
2778 $num = $db->num_rows($result);
2779 }
2780 }
2781
2782 if ($num > 0) {
2783 // Default prices or
2784 // Log of previous customer prices
2785 $backbutton = '<a class="justalink" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'">'.$langs->trans("GoBack").'</a>';
2786
2787 if (getDolGlobalString('PRODUIT_CUSTOMER_PRICES')) {
2788 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition, PhanPluginSuspiciousParamOrder
2789 print_barre_liste($langs->trans("DefaultPriceLog"), 0, $_SERVER["PHP_SELF"], '', '', '', $backbutton, 0, $num, 'title_accountancy.png');
2790 } else {
2791 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition, PhanPluginSuspiciousParamOrder
2792 print_barre_liste($langs->trans("PriceByCustomerLog"), 0, $_SERVER["PHP_SELF"], '', '', '', '', 0, $num, 'title_accountancy.png');
2793 }
2794
2795 print '<!-- List of log prices -->'."\n";
2796 print '<div class="div-table-responsive">'."\n";
2797 print '<table class="liste centpercent noborder">'."\n";
2798
2799 print '<tr class="liste_titre">';
2800 print '<th>'.$langs->trans("AppliedPricesFrom").'</tg>';
2801
2802 if (getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
2803 print '<th class="center">'.$langs->trans("PriceLevel").'</th>';
2804 }
2805 if (getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) {
2806 print '<th class="center">'.$langs->trans("Type").'</th>';
2807 }
2808
2809 print '<th class="center">'.$langs->trans("PriceBase").'</th>';
2810 if (!getDolGlobalString('PRODUIT_MULTIPRICES') && !getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) {
2811 print '<th class="right">'.$langs->trans("DefaultTaxRate").'</th>';
2812 }
2813 print '<th class="right">'.$langs->trans("HT").'</th>';
2814 print '<th class="right">'.$langs->trans("TTC").'</th>';
2815 if ($mysoc->localtax1_assuj == "1" || $mysoc->localtax2_assuj == "1") {
2816 print '<th class="right">'.$langs->trans("INCT").'</th>';
2817 }
2818 if (isModEnabled('dynamicprices')) {
2819 print '<th class="right">'.$langs->trans("PriceExpressionSelected").'</th>';
2820 }
2821 print '<th class="right">'.$langs->trans("MinPrice").' '.$langs->trans("HT").'</th>';
2822 print '<th class="right">'.$langs->trans("MinPrice").' '.$langs->trans("TTC").'</th>';
2823 print '<th class="right">'.$langs->trans("Label").'</th>';
2824 print '<th>'.$langs->trans("ChangedBy").'</th>';
2825 if ($user->hasRight('produit', 'supprimer')) {
2826 print '<th class="right">&nbsp;</th>';
2827 }
2828 print '</tr>';
2829
2830 $notfirstlineforlevel = array();
2831
2832 $i = 0;
2833 while ($i < $num) {
2834 $objp = $db->fetch_object($result);
2835
2836 print '<tr class="oddeven">';
2837 // Date
2838 print "<td>".dol_print_date($db->jdate($objp->dp), "dayhour", 'tzuserrel')."</td>";
2839
2840 // Price level
2841 if (getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
2842 print '<td class="center">'.$objp->price_level."</td>";
2843 }
2844 // Price by quantity
2845 if (getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) {
2846 $type = ($objp->price_by_qty == 1) ? 'PriceByQuantity' : 'Standard';
2847 print '<td class="center">'.$langs->trans($type)."</td>";
2848 }
2849
2850 print '<td class="center">';
2851 if (empty($objp->price_by_qty)) {
2852 print $langs->trans($objp->price_base_type);
2853 }
2854 print "</td>";
2855
2856 if (!getDolGlobalString('PRODUIT_MULTIPRICES') && !getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) {
2857 print '<td class="right">';
2858
2859 if (empty($objp->price_by_qty)) {
2860 $positiverates = '';
2861 if (price2num($objp->tva_tx)) {
2862 $positiverates .= ($positiverates ? '/' : '').price2num($objp->tva_tx);
2863 }
2864 if (price2num($objp->localtax1_type)) {
2865 $positiverates .= ($positiverates ? '/' : '').price2num($objp->localtax1_tx);
2866 }
2867 if (price2num($objp->localtax2_type)) {
2868 $positiverates .= ($positiverates ? '/' : '').price2num($objp->localtax2_tx);
2869 }
2870 if (empty($positiverates)) {
2871 $positiverates = '0';
2872 }
2873 echo vatrate($positiverates.($objp->default_vat_code ? ' ('.$objp->default_vat_code.')' : ''), true, !empty($objp->tva_npr) ? $objp->tva_npr : 0);
2874 /*
2875 if ($objp->default_vat_code)
2876 {
2877 print vatrate($objp->tva_tx, true) . ' ('.$objp->default_vat_code.')';
2878 }
2879 else print vatrate($objp->tva_tx, true, $objp->recuperableonly);*/
2880 }
2881
2882 print "</td>";
2883 }
2884
2885 // Line for default price
2886 if ($objp->price_base_type == 'HT') {
2887 $pu = $objp->price;
2888 } else {
2889 $pu = $objp->price_ttc;
2890 }
2891
2892 // Local tax was not saved into table llx_product on old version. So we will use value linked to VAT code.
2893 $localtaxarray = getLocalTaxesFromRate($objp->tva_tx.($object->default_vat_code ? ' ('.$object->default_vat_code.')' : ''), 0, $mysoc, $mysoc);
2894 // Define part of HT, VAT, TTC
2895 $resultarray = calcul_price_total(1, $pu, 0, $objp->tva_tx, 1, 1, 0, $objp->price_base_type, $objp->recuperableonly, $object->type, $mysoc, $localtaxarray);
2896 // Calcul du total ht sans remise
2897 $total_ht = $resultarray[0];
2898 $total_vat = $resultarray[1];
2899 $total_localtax1 = $resultarray[9];
2900 $total_localtax2 = $resultarray[10];
2901 $total_ttc = $resultarray[2];
2902
2903 // Price
2904 if (!empty($objp->fk_price_expression) && !empty($conf->dynamicprices->enabled)) {
2905 $price_expression = new PriceExpression($db);
2906 $res = $price_expression->fetch($objp->fk_price_expression);
2907 $title = $price_expression->title;
2908 print '<td class="right"></td>';
2909 print '<td class="right"></td>';
2910 if ($mysoc->localtax1_assuj == "1" || $mysoc->localtax2_assuj == "1") {
2911 print '<td class="right"></td>';
2912 }
2913 print '<td class="right">'.$title."</td>";
2914 } else {
2915 // Price HT
2916 print '<td class="right">';
2917 if (empty($objp->price_by_qty)) {
2918 print '<span class="amount">'.price($objp->price).'</span>';
2919 }
2920 print "</td>";
2921 // Price TTC
2922 print '<td class="right">';
2923 if (empty($objp->price_by_qty)) {
2924 $price_ttc = $objp->price_ttc;
2925 print '<span class="amount">'.price($price_ttc).'<span>';
2926 }
2927 print "</td>";
2928 if ($mysoc->localtax1_assuj == "1" || $mysoc->localtax2_assuj == "1") {
2929 print '<td class="right">';
2930 print $resultarray[2];
2931 print '</td>';
2932 }
2933 if (isModEnabled('dynamicprices')) { // Only if module is enabled
2934 print '<td class="right"></td>';
2935 }
2936 }
2937
2938 // Price min
2939 print '<td class="right">';
2940 if (empty($objp->price_by_qty)) {
2941 print price($objp->price_min);
2942 }
2943 print '</td>';
2944
2945 // Price min inc tax
2946 print '<td class="right">';
2947 if (empty($objp->price_by_qty)) {
2948 $price_min_ttc = $objp->price_min_ttc;
2949 print price($price_min_ttc);
2950 }
2951 print '</td>';
2952
2953 // Price Label
2954 print '<td>';
2955 print dolPrintHTML($objp->price_label);
2956 print '</td>';
2957
2958 // User
2959 print '<td class="tdoverflowmax125">';
2960 if ($objp->user_id > 0) {
2961 // @TODO Add a cache on $userstatic
2962 $userstatic = new User($db);
2963 $userstatic->fetch($objp->user_id);
2964 print $userstatic->getNomUrl(-1, '', 0, 0, 24, 0, 'login');
2965 }
2966 print '</td>';
2967
2968 // Action
2969 if ($user->hasRight('produit', 'supprimer')) {
2970 $candelete = 0;
2971 if (getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
2972 if (empty($notfirstlineforlevel[$objp->price_level])) {
2973 $notfirstlineforlevel[$objp->price_level] = 1;
2974 } else {
2975 $candelete = 1;
2976 }
2977 } elseif ($i > 0) {
2978 $candelete = 1;
2979 }
2980
2981 print '<td class="right">';
2982 if ($candelete || ($db->jdate($objp->dp) >= dol_now())) { // Test on date is to be able to delete a corrupted record with a date in future
2983 print '<a href="'.$_SERVER["PHP_SELF"].'?action=delete&token='.newToken().'&id='.$object->id.'&lineid='.$objp->rowid.'">';
2984 print img_delete();
2985 print '</a>';
2986 } else {
2987 print '&nbsp;'; // Can not delete last price (it's current price)
2988 }
2989 print '</td>';
2990 }
2991
2992 print "</tr>\n";
2993 $i++;
2994 }
2995
2996 $db->free($result);
2997 print "</table>";
2998 print '</div>';
2999 print "<br>";
3000 }
3001
3002 print '</div>';
3003 } else {
3004 dol_print_error($db);
3005 }
3006}
3007
3008// End of page
3009llxFooter();
3010$db->close();
$id
Support class for third parties, contacts, members, users or resources.
Definition account.php:47
if(! $sortfield) if(! $sortorder) $object
Definition account.php:100
dolibarr_set_const($db, $name, $value, $type='chaine', $visible=0, $note='', $entity=1)
Insert a parameter (key,value) into database (delete old key then insert it again).
llxFooter($comment='', $zone='private', $disabledoutputofmessages=0)
Empty footer.
Definition wrapper.php:91
if(!defined('NOREQUIRESOC')) if(!defined( 'NOREQUIRETRAN')) if(!defined('NOTOKENRENEWAL')) if(!defined( 'NOREQUIREMENU')) if(!defined('NOREQUIREHTML')) if(!defined( 'NOREQUIREAJAX')) llxHeader($head='', $title='', $help_url='', $target='', $disablejs=0, $disablehead=0, $arrayofjs='', $arrayofcss='', $morequerystring='', $morecssonbody='', $replacemainareaby='', $disablenofollow=0, $disablenoindex=0)
Empty header.
Definition wrapper.php:73
Class to manage standard extra fields.
Class to manage generation of HTML components Only common components must be here.
Class for accessing price expression table.
Class to parse product price expressions.
File of class to manage predefined price products or services by customer.
Class to manage products or services.
const TYPE_PRODUCT
Regular product.
const TYPE_SERVICE
Service.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage Dolibarr users.
global $mysoc
dol_now($mode='gmt')
Return date for now.
dol_mktime($hour, $minute, $second, $month, $day, $year, $gm='auto', $check=1)
Return a timestamp date built from detailed information (by default a local PHP server timestamp) Rep...
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
vatrate($rate, $addpercent=false, $info_bits=0, $usestarfornpr=0, $html=0)
Return a string with VAT rate label formatted for view output Used into pdf and HTML pages.
print_barre_liste($title, $page, $file, $options='', $sortfield='', $sortorder='', $morehtmlcenter='', $num=-1, $totalnboflines='', $picto='generic', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limit=-1, $selectlimitsuffix=0, $hidenavigation=0, $pagenavastextinput=0, $morehtmlrightbeforearrow='')
Print a title with navigation controls for pagination.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
img_warning($titlealt='default', $moreatt='', $morecss='pictowarning')
Show warning logo.
img_delete($titlealt='default', $other='class="pictodelete"', $morecss='')
Show delete logo.
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limittoshow=0, $moretabssuffix='', $dragdropfile=0, $morecssdiv='')
Show tabs of a record.
dolPrintHTML($s, $allowiframe=0)
Return a string (that can be on several lines) ready to be output on a HTML page.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_get_fiche_end($notab=0)
Return tab footer of a card.
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.
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
newToken()
Return the value of token currently saved into session with name 'newtoken'.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dol_clone($srcobject, $native=2)
Create a clone of instance of object (new instance with same value for each properties) With native =...
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
load_fiche_titre($title, $morehtmlright='', $picto='generic', $pictoisfullpath=0, $id='', $morecssontable='', $morehtmlcenter='', $morecssonpicto='widthpictotitle')
Load a title with picto.
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '…' if string larger than length.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
img_edit($titlealt='default', $float=0, $other='')
Show logo edit/modify fiche.
get_localtax($vatrate, $local, $thirdparty_buyer=null, $thirdparty_seller=null, $vatnpr=0)
Return localtax rate for a particular VAT rate, when selling a product with vat $vatrate,...
img_info($titlealt='default')
Show info logo.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocaltax1_rate, $uselocaltax2_rate, $remise_percent_global, $price_base_type, $info_bits, $type, $seller=null, $localtaxes_array=[], $progress=100, $multicurrency_tx=1, $pu_devise=0, $multicurrency_code='')
Calculate totals (net, vat, ...) of a line.
Definition price.lib.php:90
product_prepare_head($object)
Prepare array with list of tabs.
if(preg_match('/(crypted|dolcrypt):/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
'integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]',...
Definition repair.php:125
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.