dolibarr 22.0.5
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' => price2num($newprice[$i], '', 2),
402 'price_min' => price2num($newprice_min[$i], '', 2),
403 'price_base_type' => $newpricebase[$i],
404 'default_vat_code' => $vatratecode,
405 'vat_tx' => $tva_tx, // default_vat_code should be used in priority in a future
406 'npr' => $npr, // default_vat_code should be used in priority in a future
407 'localtaxes_array' => array('0' => $localtax1_type, '1' => $localtax1, '2' => $localtax2_type, '3' => $localtax2) // default_vat_code should be used in priority in a future
408 );
409
410 //If autogeneration is enabled, then we only set the first level
411 if ($object->price_autogen) {
412 break;
413 }
414 }
415 } elseif (!$error) {
416 $newprice = price2num(GETPOST('price', 'alpha'), '', 2);
417 $newprice_min = price2num(GETPOST('price_min', 'alpha'), '', 2);
418 $newpricebase = GETPOST('price_base_type', 'alpha');
419 $tva_tx_txt = GETPOST('tva_tx', 'alpha'); // tva_tx can be '8.5' or '8.5*' or '8.5 (XXX)' or '8.5* (XXX)'
420 $price_label = GETPOST('price_label', 'alpha');
421
422 $tva_tx = $tva_tx_txt;
423 $vatratecode = '';
424 $reg = array();
425 if (preg_match('/\‍((.*)\‍)/', $tva_tx_txt, $reg)) {
426 $vat_src_code = $reg[1];
427 $tva_tx = preg_replace('/\s*\‍(.*\‍)/', '', $tva_tx_txt); // Remove code into vatrate.
428 }
429 $tva_tx = price2num(preg_replace('/\*/', '', $tva_tx)); // keep remove all after the numbers and dot
430
431 $npr = preg_match('/\*/', $tva_tx_txt) ? 1 : 0;
432 $localtax1 = 0;
433 $localtax2 = 0;
434 $localtax1_type = '0';
435 $localtax2_type = '0';
436 // If value contains the unique code of vat line (new recommended method), we use it to find npr and local taxes
437 if (preg_match('/\‍((.*)\‍)/', $tva_tx_txt, $reg)) {
438 // We look into database using code
439 $vatratecode = $reg[1];
440 // Get record from code
441 $sql = "SELECT t.rowid, t.code, t.recuperableonly, t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
442 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
443 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($mysoc->country_code)."'";
444 $sql .= " AND t.taux = ".((float) $tva_tx)." AND t.active = 1";
445 $sql .= " AND t.code ='".$db->escape($vatratecode)."'";
446 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
447 $resql = $db->query($sql);
448 if ($resql) {
449 $obj = $db->fetch_object($resql);
450 if ($obj) {
451 $npr = $obj->recuperableonly;
452 $localtax1 = $obj->localtax1;
453 $localtax2 = $obj->localtax2;
454 $localtax1_type = $obj->localtax1_type;
455 $localtax2_type = $obj->localtax2_type;
456 }
457
458 // If spain, we don't use the localtax found into tax record in database with same code, but using the get_localtax rule.
459 if (in_array($mysoc->country_code, array('ES'))) {
460 $localtax1 = get_localtax($tva_tx, 1);
461 $localtax2 = get_localtax($tva_tx, 2);
462 }
463 }
464 } else {
465 // Get record with empty code
466 $sql = "SELECT t.rowid, t.code, t.recuperableonly, t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
467 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
468 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($mysoc->country_code)."'";
469 $sql .= " AND t.taux = ".((float) $tva_tx)." AND t.active = 1";
470 $sql .= " AND t.code = ''";
471 $resql = $db->query($sql);
472 if ($resql) {
473 $obj = $db->fetch_object($resql);
474 if ($obj) {
475 $npr = $obj->recuperableonly;
476 $localtax1 = $obj->localtax1;
477 $localtax2 = $obj->localtax2;
478 $localtax1_type = $obj->localtax1_type;
479 $localtax2_type = $obj->localtax2_type;
480 }
481 }
482 }
483
484 $pricestoupdate[0] = array(
485 'price' => $newprice,
486 'price_label' => $price_label,
487 'price_min' => $newprice_min,
488 'price_base_type' => $newpricebase,
489 'default_vat_code' => $vatratecode,
490 'vat_tx' => $tva_tx, // default_vat_code should be used in priority in a future
491 'npr' => $npr, // default_vat_code should be used in priority in a future
492 'localtaxes_array' => array('0' => $localtax1_type, '1' => $localtax1, '2' => $localtax2_type, '3' => $localtax2) // default_vat_code should be used in priority in a future
493 );
494 }
495
496 if (!$error) {
497 $db->begin();
498
499 // Packaging
500 if (getDolGlobalString('PRODUCT_USE_CUSTOMER_PACKAGING')) {
501 $object->packaging = (float) $packaging;
502 }
503
504 foreach ($pricestoupdate as $key => $val) {
505 $newprice = $val['price'];
506
507 if ($val['price'] < $val['price_min'] && !empty($object->fk_price_expression)) {
508 $newprice = $val['price_min']; //Set price same as min, the user will not see the
509 }
510
511 $newprice = price2num($newprice, 'MU');
512 $newprice_min = price2num($val['price_min'], 'MU');
513 $newvattx = price2num($val['vat_tx']);
514
515 if (getDolGlobalString('PRODUCT_MINIMUM_RECOMMENDED_PRICE') && $newprice_min < $maxpricesupplier) {
516 setEventMessages($langs->trans("MinimumPriceLimit", price($maxpricesupplier, 0, '', 1, - 1, - 1, 'auto')), null, 'errors');
517 $error++;
518 break;
519 }
520 // If price has changed, we update it
521 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) {
522 $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']);
523 } else {
524 $res = 0;
525 }
526
527 if ($res < 0) {
528 $error++;
529 setEventMessages($object->error, $object->errors, 'errors');
530 break;
531 }
532
533 // Update level price extrafields
534 $price_extralabels = $extrafields->fetch_name_optionals_label("product_price");
535 if ((getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) && !empty($price_extralabels)) {
536 $sql = "SELECT rowid";
537 $sql .= " FROM ".$object->db->prefix()."product_price";
538 $sql .= " WHERE entity IN (".getEntity('productprice').")";
539 $sql .= " AND price_level=".((int) $key); // $i
540 $sql .= " AND fk_product = ".((int) $object->id);
541 $sql .= " ORDER BY date_price DESC, rowid DESC";
542 $sql .= " LIMIT 1";
543 $resql = $object->db->query($sql);
544 if ($resql) {
545 $lineid = $object->db->fetch_object($resql);
546 $db->free($resql);
547 }
548 if (!empty($lineid->rowid)) {
549 foreach ($price_extralabels as $code => $label) {
550 $code_array = GETPOST($code, 'array');
551 $object->array_options['options_'.$code] = $code_array[$key];
552 }
553 // We need to force table to update product_price and not product extrafields
554 $object->id = $lineid->rowid;
555 $object->table_element = 'product_price';
556 $result = $object->insertExtraFields();
557 // Back to product table
558 $object->id = $id;
559 $object->table_element = 'product';
560 if ($result < 0) {
561 $error++;
562 }
563 }
564 }
565 }
566 }
567
568 if (!$error && $object->update($object->id, $user) < 0) {
569 $error++;
570 setEventMessages($object->error, $object->errors, 'errors');
571 }
572
573 if (empty($error)) {
574 $action = '';
575 setEventMessages($langs->trans("RecordSaved"), null, 'mesgs');
576 $db->commit();
577 } else {
578 $action = 'edit_price';
579 $db->rollback();
580 }
581 }
582
583
584 if ($action == 'delete' && $user->hasRight('produit', 'supprimer')) {
585 $result = $object->log_price_delete($user, GETPOSTINT('lineid'));
586 if ($result < 0) {
587 setEventMessages($object->error, $object->errors, 'errors');
588 }
589 }
590
591 // Set Price by quantity
592 if ($action == 'activate_price_by_qty' && $permissiontoadd) {
593 // Activating product price by quantity add a new price line with price_by_qty set to 1
594 $level = GETPOSTINT('level');
595 $basePrice = ($object->price_base_type == 'HT') ? $object->price : $object->price_ttc;
596 $basePriceMin = ($object->price_base_type == 'HT') ? $object->price_min : $object->price_min_ttc;
597 $ret = $object->updatePrice($basePrice, $object->price_base_type, $user, $object->tva_tx, $basePriceMin, $level, $object->tva_npr, 1);
598
599 if ($ret < 0) {
600 setEventMessages($object->error, $object->errors, 'errors');
601 }
602 }
603 // Unset Price by quantity
604 if ($action == 'disable_price_by_qty' && $permissiontoadd) {
605 // Disabling product price by quantity add a new price line with price_by_qty set to 0
606 $level = GETPOSTINT('level');
607 $basePrice = ($object->price_base_type == 'HT') ? $object->price : $object->price_ttc;
608 $basePriceMin = ($object->price_base_type == 'HT') ? $object->price_min : $object->price_min_ttc;
609 $ret = $object->updatePrice($basePrice, $object->price_base_type, $user, $object->tva_tx, $basePriceMin, $level, $object->tva_npr, 0);
610
611 if ($ret < 0) {
612 setEventMessages($object->error, $object->errors, 'errors');
613 }
614 }
615
616 if ($action == 'edit_price_by_qty') { // Test on permission not required
617 $rowid = GETPOSTINT('rowid');
618 }
619
620 // Add or update price by quantity
621 if ($action == 'update_price_by_qty' && $permissiontoadd) {
622 // Récupération des variables
623 $rowid = GETPOSTINT('rowid');
624 $priceid = GETPOSTINT('priceid');
625 $newprice = price2num(GETPOST("price"), 'MU', 2);
626 // $newminprice=price2num(GETPOST("price_min"),'MU'); // TODO : Add min price management
627 $quantity = price2num(GETPOST('quantity'), 'MS', 2);
628 $remise_percent = price2num(GETPOST('remise_percent'), '', 2);
629 $remise = 0; // TODO : allow discount by amount when available on documents
630
631 if (empty($quantity)) {
632 $error++;
633 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentities("Qty")), null, 'errors');
634 }
635 if (empty($newprice)) {
636 $error++;
637 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentities("Price")), null, 'errors');
638 }
639 if (!$error) {
640 // Calcul du prix HT et du prix unitaire
641 if ($object->price_base_type == 'TTC') {
642 $price = (float) price2num($newprice) / (1 + ($object->tva_tx / 100));
643 }
644
645 $price = price2num($newprice, 'MU');
646 $unitPrice = price2num((float) $price / (float) $quantity, 'MU');
647
648 // Ajout / mise à jour
649 if ($rowid > 0) {
650 $sql = "UPDATE ".MAIN_DB_PREFIX."product_price_by_qty SET";
651 $sql .= " price=".((float) $price).",";
652 $sql .= " unitprice=".((float) $unitPrice).",";
653 $sql .= " quantity=".((float) $quantity).",";
654 $sql .= " remise_percent=".((float) $remise_percent).",";
655 $sql .= " remise=".((float) $remise);
656 $sql .= " WHERE rowid = ".((int) $rowid);
657
658 $result = $db->query($sql);
659 if (!$result) {
660 dol_print_error($db);
661 }
662 } else {
663 $sql = "INSERT INTO ".MAIN_DB_PREFIX."product_price_by_qty (fk_product_price,price,unitprice,quantity,remise_percent,remise) values (";
664 $sql .= ((int) $priceid).','.((float) $price).','.((float) $unitPrice).','.((float) $quantity).','.((float) $remise_percent).','.((float) $remise).')';
665
666 $result = $db->query($sql);
667 if (!$result) {
668 if ($db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
669 setEventMessages($langs->trans("DuplicateRecord"), null, 'errors');
670 } else {
671 dol_print_error($db);
672 }
673 }
674 }
675 }
676 }
677
678 if ($action == 'delete_price_by_qty' && $permissiontoadd) {
679 $rowid = GETPOSTINT('rowid');
680 if (!empty($rowid)) {
681 $sql = "DELETE FROM ".MAIN_DB_PREFIX."product_price_by_qty";
682 $sql .= " WHERE rowid = ".((int) $rowid);
683
684 $result = $db->query($sql);
685 } else {
686 setEventMessages(('delete_price_by_qty'.$langs->transnoentities('MissingIds')), null, 'errors');
687 }
688 }
689
690 if ($action == 'delete_all_price_by_qty' && $permissiontoadd) {
691 $priceid = GETPOSTINT('priceid');
692 if (!empty($rowid)) {
693 $sql = "DELETE FROM ".MAIN_DB_PREFIX."product_price_by_qty";
694 $sql .= " WHERE fk_product_price = ".((int) $priceid);
695
696 $result = $db->query($sql);
697 } else {
698 setEventMessages(('delete_price_by_qty'.$langs->transnoentities('MissingIds')), null, 'errors');
699 }
700 }
701
707 if ($action == 'add_customer_price_confirm' && !$cancel && $prodcustprice !== null && ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
708 $maxpricesupplier = $object->min_recommended_price();
709
710 $update_child_soc = GETPOSTINT('updatechildprice');
711
712 // add price by customer
713 $prodcustprice->fk_soc = GETPOSTINT('socid');
714 $prodcustprice->ref_customer = GETPOST('ref_customer', 'alpha');
715 $prodcustprice->fk_product = $object->id;
716 $prodcustprice->price = price2num(GETPOST("price"), 'MU');
717 $prodcustprice->price_min = price2num(GETPOST("price_min"), 'MU');
718 $prodcustprice->price_base_type = GETPOST("price_base_type", 'alpha');
719 $prodcustprice->price_label = GETPOST("price_label", 'alpha');
720 $prodcustprice->discount_percent = price2num(GETPOST("discount_percent"));
721 $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;
722 $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
723
724 $extralabels = $extrafields->fetch_name_optionals_label("product_customer_price");
725 $extrafield_values = $extrafields->getOptionalsFromPost("product_customer_price");
726
727 $tva_tx_txt = GETPOST("tva_tx", 'alpha');
728
729 $tva_tx = $tva_tx_txt;
730 $vatratecode = '';
731 if (preg_match('/\‍((.*)\‍)/', $tva_tx_txt, $reg)) {
732 $vat_src_code = $reg[1];
733 $tva_tx = preg_replace('/\s*\‍(.*\‍)/', '', $tva_tx_txt); // Remove code into vatrate.
734 }
735 $tva_tx = price2num(preg_replace('/\*/', '', $tva_tx)); // keep remove all after the numbers and dot
736
737 $npr = preg_match('/\*/', $tva_tx_txt) ? 1 : 0;
738 $localtax1 = 0;
739 $localtax2 = 0;
740 $localtax1_type = '0';
741 $localtax2_type = '0';
742 // If value contains the unique code of vat line (new recommended method), we use it to find npr and local taxes
743 if (preg_match('/\‍((.*)\‍)/', $tva_tx_txt, $reg)) {
744 // We look into database using code
745 $vatratecode = $reg[1];
746 // Get record from code
747 $sql = "SELECT t.rowid, t.code, t.recuperableonly, t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
748 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
749 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($mysoc->country_code)."'";
750 $sql .= " AND t.taux = ".((float) $tva_tx)." AND t.active = 1";
751 $sql .= " AND t.code ='".$db->escape($vatratecode)."'";
752 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
753 $resql = $db->query($sql);
754 if ($resql) {
755 $obj = $db->fetch_object($resql);
756 if ($obj) {
757 $npr = $obj->recuperableonly;
758 $localtax1 = $obj->localtax1;
759 $localtax2 = $obj->localtax2;
760 $localtax1_type = $obj->localtax1_type;
761 $localtax2_type = $obj->localtax2_type;
762 }
763
764 // If spain, we don't use the localtax found into tax record in database with same code, but using the get_localtax rule.
765 if (in_array($mysoc->country_code, array('ES'))) {
766 $localtax1 = get_localtax($tva_tx, 1);
767 $localtax2 = get_localtax($tva_tx, 2);
768 }
769 }
770 } else {
771 // Get record with empty code
772 $sql = "SELECT t.rowid, t.code, t.recuperableonly, t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
773 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
774 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($mysoc->country_code)."'";
775 $sql .= " AND t.taux = ".((float) $tva_tx)." AND t.active = 1";
776 $sql .= " AND t.code = ''";
777 $resql = $db->query($sql);
778 if ($resql) {
779 $obj = $db->fetch_object($resql);
780 if ($obj) {
781 $npr = $obj->recuperableonly;
782 $localtax1 = $obj->localtax1;
783 $localtax2 = $obj->localtax2;
784 $localtax1_type = $obj->localtax1_type;
785 $localtax2_type = $obj->localtax2_type;
786 }
787 }
788 }
789
790 $prodcustprice->default_vat_code = $vatratecode;
791 $prodcustprice->tva_tx = $tva_tx;
792 $prodcustprice->recuperableonly = $npr;
793 $prodcustprice->localtax1_tx = $localtax1;
794 $prodcustprice->localtax2_tx = $localtax2;
795 $prodcustprice->localtax1_type = $localtax1_type;
796 $prodcustprice->localtax2_type = $localtax2_type;
797
798 if (!($prodcustprice->fk_soc > 0)) {
799 $langs->load("errors");
800 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("ThirdParty")), null, 'errors');
801 $error++;
802 $action = 'add_customer_price';
803 }
804 if (getDolGlobalString('PRODUCT_MINIMUM_RECOMMENDED_PRICE') && $prodcustprice->price_min < (float) $maxpricesupplier) {
805 $langs->load("errors");
806 setEventMessages($langs->trans("MinimumPriceLimit", price((float) $maxpricesupplier, 0, '', 1, -1, -1, 'auto')), null, 'errors');
807 $error++;
808 $action = 'add_customer_price';
809 }
810
811 if (!$error) {
812 $result = $prodcustprice->create($user, 0, $update_child_soc);
813 if ($result > 0) {
814 if (!empty($extrafield_values) && is_array($extrafield_values)) {
815 $productcustomerprice = new ProductCustomerPrice($db);
816 $res = $productcustomerprice->fetch($prodcustprice->id);
817 if ($res > 0) {
818 foreach ($extrafield_values as $key => $value) {
819 $productcustomerprice->array_options[$key] = $value;
820 }
821 $result2 = $productcustomerprice->insertExtraFields();
822 if ($result2 < 0) {
823 $prodcustprice->error = $productcustomerprice->error;
824 $prodcustprice->errors = $productcustomerprice->errors;
825 $error++;
826 }
827 }
828 }
829 }
830
831 if ($result < 0) {
832 setEventMessages($prodcustprice->error, $prodcustprice->errors, 'errors');
833 $action = 'add_customer_price';
834 } else {
835 setEventMessages($langs->trans('RecordSaved'), null, 'mesgs');
836 $action = '';
837 }
838 }
839 }
840
841 if ($action == 'confirm_remove_customer_price' && $prodcustprice !== null && ($user->hasRight('produit', 'supprimer') || $user->hasRight('service', 'supprimer'))) {
842 // Delete price by customer
843 $update_child_soc = GETPOSTINT('updatechildprice');
844
845 $prodcustprice->fetch(GETPOSTINT('lineid'));
846 $result = $prodcustprice->delete($user, 0, $update_child_soc);
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
1018if ($action == 'ask_remove_customer_price' && ($user->hasRight('produit', 'supprimer') || $user->hasRight('service', 'supprimer'))) {
1019 $formquestion = array(
1020 array('type' => 'checkbox', 'name' => 'updatechildprice', 'label' => $langs->trans('ForceDeleteChildPriceSoc'), 'value' => 0),
1021 );
1022 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id.'&lineid='.GETPOSTINT('lineid'), $langs->trans('DeleteCustomerPrice'), $langs->trans('ConfirmDeleteCustomerPrice'), 'confirm_remove_customer_price', $formquestion, 0, 1);
1023 print $formconfirm;
1024}
1025
1026$head = product_prepare_head($object);
1027$titre = $langs->trans("CardProduct".$object->type);
1028$picto = ($object->type == Product::TYPE_SERVICE ? 'service' : 'product');
1029
1030print dol_get_fiche_head($head, 'price', $titre, -1, $picto);
1031
1032$linkback = '<a href="'.DOL_URL_ROOT.'/product/list.php?restore_lastsearch_values=1&type='.$object->type.'">'.$langs->trans("BackToList").'</a>';
1033$object->next_prev_filter = "(te.fk_product_type:=:".((int) $object->type).")";
1034
1035$shownav = 1;
1036if ($user->socid && !in_array('product', explode(',', getDolGlobalString('MAIN_MODULES_FOR_EXTERNAL')))) {
1037 $shownav = 0;
1038}
1039
1040dol_banner_tab($object, 'ref', $linkback, $shownav, 'ref');
1041
1042
1043print '<div class="fichecenter">';
1044
1045print '<div class="underbanner clearboth"></div>';
1046print '<table class="border tableforfield centpercent">';
1047
1048$soc = null;
1049
1050// Price per customer segment/level
1051if (getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
1052 // Price and min price are variable (depends on level of company).
1053 if (!empty($socid)) {
1054 $soc = new Societe($db);
1055 $soc->id = $socid;
1056 $soc->fetch($socid);
1057
1058 // Type
1059 if (isModEnabled("product") && isModEnabled("service")) {
1060 $typeformat = 'select;0:'.$langs->trans("Product").',1:'.$langs->trans("Service");
1061 print '<tr><td class="">';
1062 print (!getDolGlobalString('PRODUCT_DENY_CHANGE_PRODUCT_TYPE')) ? $form->editfieldkey("Type", 'fk_product_type', (string) $object->type, $object, 0, $typeformat) : $langs->trans('Type');
1063 print '</td><td>';
1064 print $form->editfieldval("Type", 'fk_product_type', $object->type, $object, 0, $typeformat);
1065 print '</td></tr>';
1066 }
1067
1068 // Selling price
1069 print '<tr><td class="titlefieldcreate">';
1070 print $langs->trans("SellingPrice");
1071 print '</td>';
1072 print '<td colspan="2">';
1073 if ($object->multiprices_base_type[$soc->price_level] == 'TTC') {
1074 print '<span class="amount">'.price($object->multiprices_ttc[$soc->price_level]).'</span>';
1075 } else {
1076 print '<span class="amount">'.price($object->multiprices[$soc->price_level]).'</span>';
1077 }
1078 if ($object->multiprices_base_type[$soc->price_level]) {
1079 print ' '.$langs->trans($object->multiprices_base_type[$soc->price_level]);
1080 } else {
1081 print ' '.$langs->trans($object->price_base_type);
1082 }
1083 print '</td></tr>';
1084
1085 // Price min
1086 print '<tr><td>'.$langs->trans("MinPrice").'</td><td colspan="2">';
1087 if ($object->multiprices_base_type[$soc->price_level] == 'TTC') {
1088 print price($object->multiprices_min_ttc[$soc->price_level]).' '.$langs->trans($object->multiprices_base_type[$soc->price_level]);
1089 } else {
1090 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]);
1091 }
1092 print '</td></tr>';
1093
1094 if (getDolGlobalString('PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL')) { // using this option is a bug. kept for backward compatibility
1095 // TVA
1096 print '<tr><td>'.$langs->trans("DefaultTaxRate").'</td><td colspan="2">';
1097
1098 $positiverates = '';
1099 if (price2num($object->multiprices_tva_tx[$soc->price_level])) {
1100 $positiverates .= ($positiverates ? '/' : '').price2num($object->multiprices_tva_tx[$soc->price_level]);
1101 }
1102 if (price2num($object->multiprices_localtax1_type[$soc->price_level])) {
1103 $positiverates .= ($positiverates ? '/' : '').price2num($object->multiprices_localtax1_tx[$soc->price_level]);
1104 }
1105 if (price2num($object->multiprices_localtax2_type[$soc->price_level])) {
1106 $positiverates .= ($positiverates ? '/' : '').price2num($object->multiprices_localtax2_tx[$soc->price_level]);
1107 }
1108 if (empty($positiverates)) {
1109 $positiverates = '0';
1110 }
1111 echo vatrate($positiverates.($object->default_vat_code ? ' ('.$object->default_vat_code.')' : ''), true, $object->tva_npr);
1112 //print vatrate($object->multiprices_tva_tx[$soc->price_level], true);
1113 print '</td></tr>';
1114 } else {
1115 // TVA
1116 print '<tr><td>'.$langs->trans("DefaultTaxRate").'</td><td>';
1117
1118 $positiverates = '';
1119 if (price2num($object->tva_tx)) {
1120 $positiverates .= ($positiverates ? '/' : '').price2num($object->tva_tx);
1121 }
1122 if (price2num($object->localtax1_type)) {
1123 $positiverates .= ($positiverates ? '/' : '').price2num($object->localtax1_tx);
1124 }
1125 if (price2num($object->localtax2_type)) {
1126 $positiverates .= ($positiverates ? '/' : '').price2num($object->localtax2_tx);
1127 }
1128 if (empty($positiverates)) {
1129 $positiverates = '0';
1130 }
1131 echo vatrate($positiverates.($object->default_vat_code ? ' ('.$object->default_vat_code.')' : ''), true, $object->tva_npr);
1132 /*
1133 if ($object->default_vat_code)
1134 {
1135 print vatrate($object->tva_tx, true) . ' ('.$object->default_vat_code.')';
1136 }
1137 else print vatrate($object->tva_tx . ($object->tva_npr ? '*' : ''), true);*/
1138 print '</td></tr>';
1139 }
1140 } else {
1141 if (getDolGlobalString('PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL')) { // using this option is a bug. kept for backward compatibility
1142 // Type
1143 if (isModEnabled("product") && isModEnabled("service")) {
1144 $typeformat = 'select;0:'.$langs->trans("Product").',1:'.$langs->trans("Service");
1145 print '<tr><td class="">';
1146 print (!getDolGlobalString('PRODUCT_DENY_CHANGE_PRODUCT_TYPE')) ? $form->editfieldkey("Type", 'fk_product_type', (string) $object->type, $object, 0, $typeformat) : $langs->trans('Type');
1147 print '</td><td>';
1148 print $form->editfieldval("Type", 'fk_product_type', $object->type, $object, 0, $typeformat);
1149 print '</td></tr>';
1150 }
1151
1152 // We show only vat for level 1
1153 print '<tr><td class="titlefieldcreate">'.$langs->trans("DefaultTaxRate").'</td>';
1154 print '<td colspan="2">'.vatrate($object->multiprices_tva_tx[1], true).'</td>';
1155 print '</tr>';
1156 } else {
1157 // Type
1158 if (isModEnabled("product") && isModEnabled("service")) {
1159 $typeformat = 'select;0:'.$langs->trans("Product").',1:'.$langs->trans("Service");
1160 print '<tr><td class="">';
1161 print (!getDolGlobalString('PRODUCT_DENY_CHANGE_PRODUCT_TYPE')) ? $form->editfieldkey("Type", 'fk_product_type', (string) $object->type, $object, 0, $typeformat) : $langs->trans('Type');
1162 print '</td><td>';
1163 print $form->editfieldval("Type", 'fk_product_type', $object->type, $object, 0, $typeformat);
1164 print '</td></tr>';
1165 }
1166
1167 // TVA
1168 print '<!-- Default VAT Rate -->';
1169 print '<tr><td class="titlefieldcreate">'.$langs->trans("DefaultTaxRate").'</td><td>';
1170
1171 // TODO We show localtax from $object, but this properties may not be correct. Only value $object->default_vat_code is guaranteed.
1172 $positiverates = '';
1173 if (price2num($object->tva_tx)) {
1174 $positiverates .= ($positiverates ? '<span class="opacitymedium">/</span>' : '').price2num($object->tva_tx);
1175 }
1176 if (price2num($object->localtax1_type)) {
1177 $positiverates .= ($positiverates ? '<span class="opacitymedium">/</span>' : '').price2num($object->localtax1_tx);
1178 }
1179 if (price2num($object->localtax2_type)) {
1180 $positiverates .= ($positiverates ? '<span class="opacitymedium">/</span>' : '').price2num($object->localtax2_tx);
1181 }
1182 if (empty($positiverates)) {
1183 $positiverates = '0';
1184 }
1185
1186 print vatrate($positiverates.($object->default_vat_code ? ' ('.$object->default_vat_code.')' : ''), true, $object->tva_npr, 1);
1187 /*
1188 if ($object->default_vat_code)
1189 {
1190 print vatrate($object->tva_tx, true) . ' ('.$object->default_vat_code.')';
1191 }
1192 else print vatrate($object->tva_tx . ($object->tva_npr ? '*' : ''), true);*/
1193 print '</td></tr>';
1194 }
1195 print '</table>';
1196
1197 print '<br>';
1198
1199 print '<table class="liste centpercent">';
1200 print '<tr class="liste_titre"><td>';
1201 print $langs->trans("PriceLevel");
1202 if ($user->admin) {
1203 print ' <a class="editfielda" href="'.$_SERVER["PHP_SELF"].'?action=editlabelsellingprice&token='.newToken().'&pricelevel='.$i.'&id='.$object->id.'">'.img_edit($langs->trans('EditSellingPriceLabel'), 0).'</a>';
1204 }
1205 print '</td>';
1206 print '<td style="text-align: right">'.$langs->trans("SellingPrice").'</td>';
1207 print '<td style="text-align: right">'.$langs->trans("MinPrice").'</td>';
1208 // fetch optionals attributes and labels
1209 $extrafields->fetch_name_optionals_label("product_price");
1210 if ($extrafields->attributes["product_price"] && array_key_exists('label', $extrafields->attributes["product_price"])) {
1211 $extralabels = $extrafields->attributes["product_price"]['label'];
1212 if (!empty($extralabels)) {
1213 foreach ($extralabels as $key => $value) {
1214 // Show field if not hidden
1215 if (!empty($extrafields->attributes["product_price"]['list'][$key]) && $extrafields->attributes["product_price"]['list'][$key] != 3) {
1216 if (!empty($extrafields->attributes["product_price"]['langfile'][$key])) {
1217 $langs->load($extrafields->attributes["product_price"]['langfile'][$key]);
1218 }
1219 if (!empty($extrafields->attributes["product_price"]['help'][$key])) {
1220 $extratitle = $form->textwithpicto($langs->trans($value), $langs->trans($extrafields->attributes["product_price"]['help'][$key]));
1221 } else {
1222 $extratitle = $langs->trans($value);
1223 }
1224 $field = 'ef.' . $key;
1225 print '<td style="text-align: right">'.$extratitle.'</td>';
1226 }
1227 }
1228 }
1229 }
1230 print '</tr>';
1231
1232 $produit_multiprices_limit = getDolGlobalInt('PRODUIT_MULTIPRICES_LIMIT');
1233 for ($i = 1; $i <= $produit_multiprices_limit; $i++) {
1234 print '<tr class="oddeven">';
1235
1236 // Label of price
1237 print '<td>';
1238 $keyforlabel = 'PRODUIT_MULTIPRICES_LABEL'.$i;
1239 if (preg_match('/editlabelsellingprice/', $action)) {
1240 print '<form method="post" action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'">';
1241 print '<input type="hidden" name="token" value="'.newToken().'">';
1242 print '<input type="hidden" name="action" value="setlabelsellingprice">';
1243 print '<input type="hidden" name="pricelevel" value="'.$i.'">';
1244 print $langs->trans("SellingPrice").' '.$i.' - ';
1245 print '<input class="maxwidthonsmartphone" type="text" name="labelsellingprice" value="' . getDolGlobalString($keyforlabel).'">';
1246 print '&nbsp;<input type="submit" class="button smallpaddingimp" value="'.$langs->trans("Modify").'">';
1247 print '</form>';
1248 } else {
1249 print $langs->trans("SellingPrice").' '.$i;
1250 if (getDolGlobalString($keyforlabel)) {
1251 print ' - '.$langs->trans(getDolGlobalString($keyforlabel));
1252 }
1253 }
1254 print '</td>';
1255
1256 if ($object->multiprices_base_type [$i] == 'TTC') {
1257 print '<td class="right"><span class="amount">'.price($object->multiprices_ttc[$i]);
1258 } else {
1259 print '<td class="right"><span class="amount">'.price($object->multiprices[$i]);
1260 }
1261
1262 if ($object->multiprices_base_type[$i]) {
1263 print ' '.$langs->trans($object->multiprices_base_type [$i]).'</span></td>';
1264 } else {
1265 print ' '.$langs->trans($object->price_base_type).'</span></td>';
1266 }
1267
1268 // Prix min
1269 print '<td style="text-align: right">';
1270 if (empty($object->multiprices_base_type[$i])) {
1271 $object->multiprices_base_type[$i] = "HT";
1272 }
1273 if ($object->multiprices_base_type[$i] == 'TTC') {
1274 print price($object->multiprices_min_ttc[$i]).' '.$langs->trans($object->multiprices_base_type[$i]);
1275 } else {
1276 print price($object->multiprices_min[$i]).' '.$langs->trans($object->multiprices_base_type[$i]);
1277 }
1278 print '</td>';
1279 if (!empty($extralabels)) {
1280 $sql1 = "SELECT rowid";
1281 $sql1 .= " FROM ".$object->db->prefix()."product_price";
1282 $sql1 .= " WHERE entity IN (".getEntity('productprice').")";
1283 $sql1 .= " AND price_level=".((int) $i);
1284 $sql1 .= " AND fk_product = ".((int) $object->id);
1285 $sql1 .= " ORDER BY date_price DESC, rowid DESC";
1286 $sql1 .= " LIMIT 1";
1287 $resql1 = $object->db->query($sql1);
1288 if ($resql1) {
1289 $lineid = $object->db->fetch_object($resql1);
1290 }
1291 $sql2 = "SELECT";
1292 $sql2 .= " fk_object";
1293 foreach ($extralabels as $key => $value) {
1294 $sql2 .= ", ".$db->sanitize($key);
1295 }
1296 $sql2 .= " FROM ".MAIN_DB_PREFIX."product_price_extrafields";
1297 $sql2 .= " WHERE fk_object = ".((int) $lineid->rowid);
1298 $resql2 = $db->query($sql2);
1299 if ($resql2) {
1300 if ($db->num_rows($resql2) != 1) {
1301 foreach ($extralabels as $key => $value) {
1302 if (!empty($extrafields->attributes["product_price"]['list'][$key]) && $extrafields->attributes["product_price"]['list'][$key] != 3) {
1303 print '<td align="right"></td>';
1304 }
1305 }
1306 } else {
1307 $obj = $db->fetch_object($resql2);
1308 foreach ($extralabels as $key => $value) {
1309 if (!empty($extrafields->attributes["product_price"]['list'][$key]) && $extrafields->attributes["product_price"]['list'][$key] != 3) {
1310 print '<td align="right">'.$extrafields->showOutputField($key, $obj->{$key}, '', 'product_price')."</td>";
1311 }
1312 }
1313 }
1314 $db->free($resql1);
1315 $db->free($resql2);
1316 }
1317 }
1318 print '</tr>';
1319
1320 // Price by quantity
1321 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
1322 print '<tr><td>'.$langs->trans("PriceByQuantity").' '.$i;
1323 if (getDolGlobalString($keyforlabel)) {
1324 print ' - '.$langs->trans(getDolGlobalString($keyforlabel));
1325 }
1326 print '</td><td colspan="2">';
1327
1328 if ($object->prices_by_qty[$i] == 1) {
1329 print '<table width="50%" class="border" summary="List of quantities">';
1330
1331 print '<tr class="liste_titre">';
1332 print '<td>'.$langs->trans("PriceByQuantityRange").' '.$i.'</td>';
1333 print '<td class="right">'.$langs->trans("HT").'</td>';
1334 print '<td class="right">'.$langs->trans("UnitPrice").'</td>';
1335 print '<td class="right">'.$langs->trans("Discount").'</td>';
1336 print '<td>&nbsp;</td>';
1337 print '</tr>';
1338 $ii = 0;
1339 foreach ($object->prices_by_qty_list[$i] as $ii => $prices) {
1340 if ($action == 'edit_price_by_qty' && $rowid == $prices['rowid'] && ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
1341 print '<form action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="POST">';
1342 print '<input type="hidden" name="token" value="'.newToken().'">';
1343 print '<input type="hidden" name="action" value="update_price_by_qty">';
1344 print '<input type="hidden" name="priceid" value="'.$object->prices_by_qty_id[$i].'">';
1345 print '<input type="hidden" value="'.$prices['rowid'].'" name="rowid">';
1346 print '<tr class="'.($ii % 2 == 0 ? 'pair' : 'impair').'">';
1347 print '<td><input size="5" type="text" value="'.$prices['quantity'].'" name="quantity"></td>';
1348 print '<td class="right" colspan="2"><input size="10" type="text" value="'.price2num($prices['price'], 'MU').'" name="price">&nbsp;'.$object->price_base_type.'</td>';
1349 print '<td class="right nowraponall"><input size="5" type="text" value="'.$prices['remise_percent'].'" name="remise_percent"> %</td>';
1350 print '<td class="center"><input type="submit" value="'.$langs->trans("Modify").'" class="button"></td>';
1351 print '</tr>';
1352 print '</form>';
1353 } else {
1354 print '<tr class="'.($ii % 2 == 0 ? 'pair' : 'impair').'">';
1355 print '<td>'.$prices['quantity'].'</td>';
1356 print '<td class="right">'.price($prices['price']).'</td>';
1357 print '<td class="right">'.price($prices['unitprice']).'</td>';
1358 print '<td class="right">'.price($prices['remise_percent']).' %</td>';
1359 print '<td class="center">';
1360 if (($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
1361 print '<a class="editfielda marginleftonly marginrightonly" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=edit_price_by_qty&token='.newToken().'&rowid='.$prices["rowid"].'">';
1362 print img_edit().'</a>';
1363 print '<a class="marginleftonly marginrightonly" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=delete_price_by_qty&token='.newToken().'&rowid='.$prices["rowid"].'">';
1364 print img_delete().'</a>';
1365 } else {
1366 print '&nbsp;';
1367 }
1368 print '</td>';
1369 print '</tr>';
1370 }
1371 }
1372 if ($action != 'edit_price_by_qty' && ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
1373 print '<form action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="POST">';
1374 print '<input type="hidden" name="token" value="'.newToken().'">';
1375 print '<input type="hidden" name="action" value="update_price_by_qty">';
1376 print '<input type="hidden" name="priceid" value="'.$object->prices_by_qty_id[$i].'">'; // id in product_price
1377 print '<input type="hidden" value="0" name="rowid">'; // id in product_price
1378 print '<tr class="'.($ii % 2 == 0 ? 'pair' : 'impair').'">';
1379 print '<td><input size="5" type="text" value="1" name="quantity"></td>';
1380 print '<td class="right" class="nowrap"><input size="10" type="text" value="0" name="price">&nbsp;'.$object->price_base_type.'</td>';
1381 print '<td class="right">&nbsp;</td>';
1382 print '<td class="right" class="nowraponall"><input size="5" type="text" value="0" name="remise_percent"> %</td>';
1383 print '<td class="center"><input type="submit" value="'.$langs->trans("Add").'" class="button"></td>';
1384 print '</tr>';
1385 print '</form>';
1386 }
1387
1388 print '</table>';
1389 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>';
1390 } else {
1391 print $langs->trans("No");
1392 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>';
1393 }
1394 print '</td></tr>';
1395 }
1396 }
1397 }
1398} else {
1399 // TVA
1400 print '<tr><td class="titlefield">'.$langs->trans("DefaultTaxRate").'</td><td>';
1401
1402 $positiverates = '';
1403 if (price2num($object->tva_tx)) {
1404 $positiverates .= ($positiverates ? '/' : '').price2num($object->tva_tx);
1405 }
1406 if (price2num($object->localtax1_type)) {
1407 $positiverates .= ($positiverates ? '/' : '').price2num($object->localtax1_tx);
1408 }
1409 if (price2num($object->localtax2_type)) {
1410 $positiverates .= ($positiverates ? '/' : '').price2num($object->localtax2_tx);
1411 }
1412 if (empty($positiverates)) {
1413 $positiverates = '0';
1414 }
1415 echo vatrate($positiverates.($object->default_vat_code ? ' ('.$object->default_vat_code.')' : ''), true, $object->tva_npr, 0, 1);
1416 /*
1417 if ($object->default_vat_code)
1418 {
1419 print vatrate($object->tva_tx, true) . ' ('.$object->default_vat_code.')';
1420 }
1421 else print vatrate($object->tva_tx, true, $object->tva_npr, true);*/
1422 print '</td></tr>';
1423
1424 // Price
1425 print '<tr class="field_selling_price"><td>'.$langs->trans("SellingPrice").'</td><td>';
1426 if ($object->price_base_type == 'TTC') {
1427 print price($object->price_ttc).' '.$langs->trans($object->price_base_type);
1428 } else {
1429 print price($object->price).' '.$langs->trans($object->price_base_type);
1430 if (getDolGlobalString('PRODUCT_DISPLAY_VAT_INCL_PRICES') && !empty($object->price_ttc)) {
1431 print '<i class="opacitymedium"> - ' . price($object->price_ttc).' '.$langs->trans('TTC') . '</i>';
1432 }
1433 }
1434
1435 print '</td></tr>';
1436
1437 // Price minimum
1438 print '<tr class="field_min_price"><td>'.$langs->trans("MinPrice").'</td><td>';
1439 if ($object->price_base_type == 'TTC') {
1440 print price($object->price_min_ttc).' '.$langs->trans($object->price_base_type);
1441 } else {
1442 print price($object->price_min).' '.$langs->trans($object->price_base_type);
1443 if (getDolGlobalString('PRODUCT_DISPLAY_VAT_INCL_PRICES') && !empty($object->price_min_ttc)) {
1444 print '<i class="opacitymedium"> - ' . price($object->price_min_ttc).' '.$langs->trans('TTC') . '</i>';
1445 }
1446 }
1447 print '</td></tr>';
1448
1449 // Packaging
1450 if (getDolGlobalString('PRODUCT_USE_CUSTOMER_PACKAGING')) {
1451 print '<tr class="field_price_label"><td>'.$form->textwithpicto($langs->trans("PackagingForThisProduct"), $langs->trans("PackagingForThisProductSellDesc")).'</td><td>';
1452 print $object->packaging;
1453 print '</td></tr>';
1454 }
1455
1456 // Price Label
1457 print '<tr class="field_price_label"><td>'.$langs->trans("PriceLabel").'</td><td>';
1458 print $object->price_label;
1459 print '</td></tr>';
1460
1461 $ii = 0;
1462 // Price by quantity
1463 if (getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) { // TODO Fix the form inside tr instead of td
1464 print '<tr><td>'.$langs->trans("PriceByQuantity");
1465 if ($object->prices_by_qty[0] == 0) {
1466 print '&nbsp; <a href="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'&action=activate_price_by_qty&level=1&token='.newToken().'">('.$langs->trans("Activate").')';
1467 } else {
1468 print '&nbsp; <a href="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'&action=disable_price_by_qty&level=1&token='.newToken().'">('.$langs->trans("DisablePriceByQty").')';
1469 }
1470 print '</td><td>';
1471
1472 if ($object->prices_by_qty[0] == 1) {
1473 print '<table width="50%" class="border" summary="List of quantities">';
1474 print '<tr class="liste_titre">';
1475 //print '<td>' . $langs->trans("PriceByQuantityRange") . '</td>';
1476 print '<td>'.$langs->trans("Quantity").'</td>';
1477 print '<td class="right">'.$langs->trans("Price").'</td>';
1478 print '<td class="right"></td>';
1479 print '<td class="right">'.$langs->trans("UnitPrice").'</td>';
1480 print '<td class="right">'.$langs->trans("Discount").'</td>';
1481 print '<td>&nbsp;</td>';
1482 print '</tr>';
1483 if ($action != 'edit_price_by_qty') {
1484 print '<form action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="POST">'; // FIXME a form into a table is not allowed
1485 print '<input type="hidden" name="token" value="'.newToken().'">';
1486 print '<input type="hidden" name="action" value="update_price_by_qty">';
1487 print '<input type="hidden" name="priceid" value="'.$object->prices_by_qty_id[0].'">'; // id in product_price
1488 print '<input type="hidden" value="0" name="rowid">'; // id in product_price_by_qty
1489
1490 print '<tr class="'.(($ii % 2) == 0 ? 'pair' : 'impair').'">'; // @phpstan-ignore-line
1491 print '<td><input size="5" type="text" value="1" name="quantity"></td>';
1492 print '<td class="right"><input class="width50 right" type="text" value="0" name="price"></td>';
1493 print '<td>';
1494 //print $object->price_base_type;
1495 print '</td>';
1496 print '<td class="right">&nbsp;</td>';
1497 print '<td class="right nowraponall"><input type="text" class="width50 right" value="0" name="remise_percent"> %</td>';
1498 print '<td class="center"><input type="submit" value="'.$langs->trans("Add").'" class="button"></td>';
1499 print '</tr>';
1500
1501 print '</form>';
1502 }
1503 foreach ($object->prices_by_qty_list[0] as $ii => $prices) {
1504 if ($action == 'edit_price_by_qty' && $rowid == $prices['rowid'] && ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
1505 print '<form action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="POST">';
1506 print '<input type="hidden" name="token" value="'.newToken().'">';
1507 print '<input type="hidden" name="action" value="update_price_by_qty">';
1508 print '<input type="hidden" name="priceid" value="'.$object->prices_by_qty_id[0].'">'; // id in product_price
1509 print '<input type="hidden" value="'.$prices['rowid'].'" name="rowid">'; // id in product_price_by_qty
1510 print '<tr class="'.($ii % 2 == 0 ? 'pair' : 'impair').'">';
1511 print '<td><input size="5" type="text" value="'.$prices['quantity'].'" name="quantity"></td>';
1512 print '<td class="right"><input class="width50 right" type="text" value="'.price2num($prices['price'], 'MU').'" name="price"></td>';
1513 print '<td class="right">';
1514 //print $object->price_base_type;
1515 print $prices['price_base_type'];
1516 print '</td>';
1517 print '<td class="right">&nbsp;</td>';
1518 print '<td class="right nowraponall"><input class="width50 right" type="text" value="'.$prices['remise_percent'].'" name="remise_percent"> %</td>';
1519 print '<td class="center"><input type="submit" value="'.$langs->trans("Modify").'" class="button"></td>';
1520 print '</tr>';
1521 print '</form>';
1522 } else {
1523 print '<tr class="'.($ii % 2 == 0 ? 'pair' : 'impair').'">';
1524 print '<td>'.$prices['quantity'].'</td>';
1525 print '<td class="right">'.price($prices['price']).'</td>';
1526 print '<td class="right">';
1527 //print $object->price_base_type;
1528 print $prices['price_base_type'];
1529 print '</td>';
1530 print '<td class="right">'.price($prices['unitprice']).'</td>';
1531 print '<td class="right">'.price($prices['remise_percent']).' %</td>';
1532 print '<td class="center">';
1533 if (($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
1534 print '<a class="editfielda marginleftonly marginrightonly" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=edit_price_by_qty&token='.newToken().'&rowid='.$prices["rowid"].'">';
1535 print img_edit().'</a>';
1536 print '<a class="marginleftonly marginrightonly" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=delete_price_by_qty&token='.newToken().'&rowid='.$prices["rowid"].'">';
1537 print img_delete().'</a>';
1538 } else {
1539 print '&nbsp;';
1540 }
1541 print '</td>';
1542 print '</tr>';
1543 }
1544 }
1545 print '</table>';
1546 } else {
1547 print $langs->trans("No");
1548 }
1549 print '</td></tr>';
1550 }
1551}
1552
1553print "</table>\n";
1554
1555print '</div>';
1556print '<div class="clearboth"></div>';
1557
1558
1559print dol_get_fiche_end();
1560
1561
1562
1563/*
1564 * Action bar
1565 */
1566
1567
1568if (!$action || $action == 'delete' || $action == 'showlog_customer_price' || $action == 'showlog_default_price' || $action == 'add_customer_price'
1569 || $action == 'activate_price_by_qty' || $action == 'disable_price_by_qty') {
1570 print "\n".'<div class="tabsAction">'."\n";
1571
1572
1573 $parameters = array();
1574 $reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been
1575 if (empty($reshook)) {
1576 if ($object->isVariant()) {
1577 if ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer')) {
1578 print '<div class="inline-block divButAction"><a class="butActionRefused classfortooltip" href="#" title="' . dol_escape_htmltag($langs->trans("NoEditVariants")) . '">' . $langs->trans("UpdateDefaultPrice") . '</a></div>';
1579 }
1580 } else {
1581 if (!getDolGlobalString('PRODUIT_MULTIPRICES') && !getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES') && !getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
1582 if ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer')) {
1583 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>';
1584 } else {
1585 print '<div class="inline-block divButAction"><span class="butActionRefused" title="'.dol_escape_htmltag($langs->trans("NotEnoughPermissions")).'">' . $langs->trans("UpdateDefaultPrice") . '</span></div>';
1586 }
1587 }
1588
1589 if (getDolGlobalString('PRODUIT_CUSTOMER_PRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
1590 if ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer')) {
1591 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>';
1592 } else {
1593 print '<div class="inline-block divButAction"><span class="butActionRefused" title="'.dol_escape_htmltag($langs->trans("NotEnoughPermissions")).'">' . $langs->trans("AddCustomerPrice") . '</span></div>';
1594 }
1595 }
1596
1597 if (getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
1598 if ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer')) {
1599 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>';
1600 } else {
1601 print '<div class="inline-block divButAction"><span class="butActionRefused" title="'.dol_escape_htmltag($langs->trans("NotEnoughPermissions")).'">' . $langs->trans("UpdateVAT") . '</span></div>';
1602 }
1603
1604 if ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer')) {
1605 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>';
1606 } else {
1607 print '<div class="inline-block divButAction"><span class="butActionRefused" title="'.dol_escape_htmltag($langs->trans("NotEnoughPermissions")).'">' . $langs->trans("UpdateLevelPrices") . '</span></div>';
1608 }
1609 }
1610 }
1611 }
1612
1613 print "\n</div>\n";
1614}
1615
1616
1617
1618/*
1619 * Edit price area
1620 */
1621
1622if ($action == 'edit_vat' && ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
1623 print load_fiche_titre($langs->trans("UpdateVAT"), '');
1624
1625 print '<form action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="POST">';
1626 print '<input type="hidden" name="token" value="'.newToken().'">';
1627 print '<input type="hidden" name="action" value="update_vat">';
1628 print '<input type="hidden" name="id" value="'.$object->id.'">';
1629
1630 print dol_get_fiche_head();
1631
1632 print '<table class="border centpercent">';
1633
1634 // VAT
1635 print '<tr><td>'.$langs->trans("DefaultTaxRate").'</td><td>';
1636 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);
1637 print '</td></tr>';
1638
1639 print '</table>';
1640
1641 print dol_get_fiche_end();
1642
1643 print $form->buttonsSaveCancel();
1644
1645 print '<br></form><br>';
1646}
1647
1648if (($action == 'edit_price' || $action == 'edit_level_price') && $object->getRights()->creer) {
1649 print '<br>';
1650 print load_fiche_titre($langs->trans("NewPrice"), '');
1651
1652 if (!getDolGlobalString('PRODUIT_MULTIPRICES') && $action != 'edit_level_price') {
1653 print '<!-- Edit price -->'."\n";
1654 print '<form action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="POST">';
1655 print '<input type="hidden" name="token" value="'.newToken().'">';
1656 print '<input type="hidden" name="action" value="update_price">';
1657 print '<input type="hidden" name="id" value="'.$object->id.'">';
1658
1659 print dol_get_fiche_head();
1660
1661 print '<div class="div-table-responsive-no-min">';
1662 print '<table class="border centpercent">';
1663
1664 // VAT
1665 print '<tr><td class="titlefield">'.$langs->trans("DefaultTaxRate").'</td><td>';
1666 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);
1667 print '</td></tr>';
1668
1669 // Price base
1670 print '<tr><td>';
1671 print $langs->trans('PriceBase');
1672 print '</td>';
1673 print '<td>';
1674 print $form->selectPriceBaseType($object->price_base_type, "price_base_type");
1675 print '</td>';
1676 print '</tr>';
1677
1678 // Only show price mode and expression selector if module is enabled
1679 if (isModEnabled('dynamicprices')) {
1680 // Price mode selector
1681 print '<!-- Show price mode of dynamicprices editor -->'."\n";
1682 print '<tr><td>'.$langs->trans("PriceMode").'</td><td>';
1683 print img_picto('', 'dynamicprice', 'class="pictofixedwidth"');
1684 $price_expression = new PriceExpression($db);
1685 $price_expression_list = array(0 => $langs->trans("Numeric").' <span class="opacitymedium">('.$langs->trans("NoDynamicPrice").')</span>'); //Put the numeric mode as first option
1686 foreach ($price_expression->list_price_expression() as $entry) {
1687 $price_expression_list[$entry->id] = $entry->title;
1688 }
1689 $price_expression_preselection = GETPOST('eid') ? GETPOST('eid') : ($object->fk_price_expression ? $object->fk_price_expression : '0');
1690 print $form->selectarray('eid', $price_expression_list, $price_expression_preselection);
1691 print '&nbsp; <a id="expression_editor" class="classlink">'.$langs->trans("PriceExpressionEditor").'</a>';
1692 print '</td></tr>';
1693
1694 // This code hides the numeric price input if is not selected, loads the editor page if editor button is pressed
1695 ?>
1696
1697 <script type="text/javascript">
1698 jQuery(document).ready(function() {
1699 jQuery("#expression_editor").click(function() {
1700 window.location = "<?php echo DOL_URL_ROOT ?>/product/dynamic_price/editor.php?id=<?php echo $id ?>&tab=price&eid=" + $("#eid").val();
1701 });
1702 jQuery("#eid").change(on_change);
1703 on_change();
1704 });
1705 function on_change() {
1706 if ($("#eid").val() == 0) {
1707 jQuery("#price_numeric").show();
1708 } else {
1709 jQuery("#price_numeric").hide();
1710 }
1711 }
1712 </script>
1713 <?php
1714 }
1715
1716 // Price
1717 $product = new Product($db);
1718 $product->fetch($id, $ref, '', '1'); //Ignore the math expression when getting the price
1719 print '<tr id="price_numeric"><td>';
1720 $text = $langs->trans('SellingPrice');
1721 print $form->textwithpicto($text, $langs->trans("PrecisionUnitIsLimitedToXDecimals", getDolGlobalString('MAIN_MAX_DECIMALS_UNIT')), 1, 'help');
1722 print '</td><td>';
1723 if ($object->price_base_type == 'TTC') {
1724 print '<input name="price" size="10" value="'.price($product->price_ttc).'">';
1725 } else {
1726 print '<input name="price" size="10" value="'.price($product->price).'">';
1727 }
1728 print '</td></tr>';
1729
1730 // Price minimum
1731 print '<tr><td>';
1732 $text = $langs->trans('MinPrice');
1733 print $form->textwithpicto($text, $langs->trans("PrecisionUnitIsLimitedToXDecimals", getDolGlobalString('MAIN_MAX_DECIMALS_UNIT')), 1, 'help');
1734 print '</td><td>';
1735 if ($object->price_base_type == 'TTC') {
1736 print '<input name="price_min" size="10" value="'.price($object->price_min_ttc).'">';
1737 } else {
1738 print '<input name="price_min" size="10" value="'.price($object->price_min).'">';
1739 }
1740 if (getDolGlobalString('PRODUCT_MINIMUM_RECOMMENDED_PRICE')) {
1741 print ' &nbsp; '.$langs->trans("MinimumRecommendedPrice", price((float) $maxpricesupplier, 0, '', 1, -1, -1, 'auto')).' '.img_warning().'</td>';
1742 }
1743 print '</td>';
1744 print '</tr>';
1745
1746 // Packaging
1747 if (getDolGlobalString('PRODUCT_USE_CUSTOMER_PACKAGING')) {
1748 print '<tr><td>';
1749 print $form->textwithpicto($langs->trans("PackagingForThisProduct"), $langs->trans("PackagingForThisProductSellDesc"));
1750 print '</td><td>';
1751 $packaging = $object->packaging;
1752 print '<input class="flat" name="packaging" size="5" value="' . price($packaging, 0, '', 1, -1, 2).'">';
1753 print '</td>';
1754 print '</tr>';
1755 }
1756
1757 // Price Label
1758 print '<tr><td>';
1759 print $langs->trans('PriceLabel');
1760 print '</td><td>';
1761 print '<input name="price_label" maxlength="255" class="minwidth300 maxwidth400onsmartphone" value="'.$object->price_label.'">';
1762 print '</td>';
1763 print '</tr>';
1764
1765 $parameters = array();
1766 $reshook = $hookmanager->executeHooks('formObjectOptions', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
1767
1768 print '</table>';
1769 print '</div>';
1770
1771 print dol_get_fiche_end();
1772
1773 print $form->buttonsSaveCancel();
1774
1775 print '</form>';
1776 } elseif ($action == 'edit_level_price' && $object->getRights()->creer) {
1777 print '<!-- Edit price per level -->'."\n"; ?>
1778 <script>
1779
1780 var showHidePriceRules = function () {
1781 var otherPrices = $('div.fiche form table tbody tr:not(:first)');
1782 var minPrice1 = $('div.fiche form input[name="price_min[1]"]');
1783
1784 if (jQuery('input#usePriceRules').prop('checked')) {
1785 otherPrices.hide();
1786 minPrice1.hide();
1787 } else {
1788 otherPrices.show();
1789 minPrice1.show();
1790 }
1791 }
1792
1793 jQuery(document).ready(function () {
1794 showHidePriceRules();
1795
1796 jQuery('input#usePriceRules').click(showHidePriceRules);
1797 });
1798 </script>
1799 <?php
1800
1801 print '<form action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="POST">';
1802 print '<input type="hidden" name="token" value="'.newToken().'">';
1803 print '<input type="hidden" name="action" value="update_level_price">';
1804 print '<input type="hidden" name="id" value="'.$object->id.'">';
1805
1806 //print dol_get_fiche_head(array(), '', '', -1);
1807
1808 if ((getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) && getDolGlobalString('PRODUIT_MULTIPRICES_ALLOW_AUTOCALC_PRICELEVEL')) {
1809 print $langs->trans('UseMultipriceRules').' <input type="checkbox" id="usePriceRules" name="usePriceRules" '.($object->price_autogen ? 'checked' : '').'><br><br>';
1810 }
1811
1812 print '<div class="div-table-responsive-no-min">';
1813 print '<table class="noborder">';
1814 print '<thead><tr class="liste_titre">';
1815
1816 print '<td>'.$langs->trans("PriceLevel").'</td>';
1817
1818 if (getDolGlobalString('PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL')) {
1819 print '<td style="text-align: center">'.$langs->trans("DefaultTaxRate").'</td>';
1820 } else {
1821 print '<td></td>';
1822 }
1823
1824 print '<td class="center">'.$langs->trans("SellingPrice").'</td>';
1825
1826 print '<td class="center">'.$langs->trans("MinPrice").'</td>';
1827
1828 if (getDolGlobalString('PRODUCT_MINIMUM_RECOMMENDED_PRICE')) {
1829 print '<td></td>';
1830 }
1831
1832 // fetch optionals attributes and labels
1833 $extrafields->fetch_name_optionals_label("product_price");
1834 if ($extrafields->attributes["product_price"] && array_key_exists('label', $extrafields->attributes["product_price"])) {
1835 $extralabels = $extrafields->attributes["product_price"]['label'];
1836 if (!empty($extralabels)) {
1837 foreach ($extralabels as $key => $value) {
1838 // Show field if not hidden
1839 if (!empty($extrafields->attributes["product_price"]['list'][$key]) && $extrafields->attributes["product_price"]['list'][$key] != 3) {
1840 if (!empty($extrafields->attributes["product_price"]['langfile'][$key])) {
1841 $langs->load($extrafields->attributes["product_price"]['langfile'][$key]);
1842 }
1843 if (!empty($extrafields->attributes["product_price"]['help'][$key])) {
1844 $extratitle = $form->textwithpicto($langs->trans($value), $langs->trans($extrafields->attributes["product_price"]['help'][$key]));
1845 } else {
1846 $extratitle = $langs->trans($value);
1847 }
1848 print '<td style="text-align: right">'.$extratitle.'</td>';
1849 }
1850 }
1851 }
1852 }
1853 print '</tr></thead>';
1854
1855 print '<tbody>';
1856
1857 $produit_multiprices_limit = getDolGlobalInt('PRODUIT_MULTIPRICES_LIMIT');
1858 for ($i = 1; $i <= $produit_multiprices_limit; $i++) {
1859 print '<tr class="oddeven">';
1860 print '<td>';
1861 $keyforlabel = 'PRODUIT_MULTIPRICES_LABEL'.$i;
1862 $text = $langs->trans('SellingPrice').' '.$i.' - '.getDolGlobalString($keyforlabel);
1863 print $form->textwithpicto($text, $langs->trans("PrecisionUnitIsLimitedToXDecimals", getDolGlobalString('MAIN_MAX_DECIMALS_UNIT')), 1, 'help');
1864 print '</td>';
1865
1866 // VAT
1867 if (!getDolGlobalString('PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL')) {
1868 print '<td>';
1869 print '<input type="hidden" name="tva_tx['.$i.']" value="'.($object->default_vat_code ? $object->tva_tx.' ('.$object->default_vat_code.')' : $object->tva_tx).'">';
1870 print '<input type="hidden" name="tva_npr['.$i.']" value="'.$object->tva_npr.'">';
1871 print '<input type="hidden" name="localtax1_tx['.$i.']" value="'.$object->localtax1_tx.'">';
1872 print '<input type="hidden" name="localtax1_type['.$i.']" value="'.$object->localtax1_type.'">';
1873 print '<input type="hidden" name="localtax2_tx['.$i.']" value="'.$object->localtax2_tx.'">';
1874 print '<input type="hidden" name="localtax2_type['.$i.']" value="'.$object->localtax2_type.'">';
1875 print '</td>';
1876 } else {
1877 // This option is kept for backward compatibility but has no sense
1878 print '<td style="text-align: center">';
1879 print $form->load_tva("tva_tx[".$i.']', $object->multiprices_tva_tx[$i], $mysoc, null, $object->id, 0, $object->type, false, 1);
1880 print '</td>';
1881 }
1882
1883 // Selling price
1884 print '<td style="text-align: center">';
1885 if ($object->multiprices_base_type [$i] == 'TTC') {
1886 print '<input name="price['.$i.']" size="10" value="'.price($object->multiprices_ttc [$i]).'">';
1887 } else {
1888 print '<input name="price['.$i.']" size="10" value="'.price($object->multiprices [$i]).'">';
1889 }
1890 print '&nbsp;'.$form->selectPriceBaseType($object->multiprices_base_type [$i], "multiprices_base_type[".$i."]");
1891 print '</td>';
1892
1893 // Min price
1894 print '<td style="text-align: center">';
1895 if ($object->multiprices_base_type [$i] == 'TTC') {
1896 print '<input name="price_min['.$i.']" size="10" value="'.price($object->multiprices_min_ttc [$i]).'">';
1897 } else {
1898 print '<input name="price_min['.$i.']" size="10" value="'.price($object->multiprices_min [$i]).'">';
1899 }
1900 if (getDolGlobalString('PRODUCT_MINIMUM_RECOMMENDED_PRICE')) {
1901 print '<td class="left">'.$langs->trans("MinimumRecommendedPrice", price((float) $maxpricesupplier, 0, '', 1, -1, -1, 'auto')).' '.img_warning().'</td>';
1902 }
1903 print '</td>';
1904
1905 if (!empty($extralabels)) {
1906 $sql1 = "SELECT rowid";
1907 $sql1 .= " FROM ".$object->db->prefix()."product_price";
1908 $sql1 .= " WHERE entity IN (".getEntity('productprice').")";
1909 $sql1 .= " AND price_level=".((int) $i);
1910 $sql1 .= " AND fk_product = ".((int) $object->id);
1911 $sql1 .= " ORDER BY date_price DESC, rowid DESC";
1912 $sql1 .= " LIMIT 1";
1913 $resql1 = $object->db->query($sql1);
1914 if ($resql1) {
1915 $lineid = $object->db->fetch_object($resql1);
1916 }
1917 if (empty($lineid->rowid)) {
1918 foreach ($extralabels as $key => $value) {
1919 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))) {
1920 if (!empty($extrafields->attributes["product_price"]['langfile'][$key])) {
1921 $langs->load($extrafields->attributes["product_price"]['langfile'][$key]);
1922 }
1923
1924 $extravalue = GETPOSTISSET('options_'.$key) ? $extrafield_values['options_'.$key] : $obj->{$key};
1925 print '<td align="center"><input name="'.$key.'['.$i.']" size="10" value="'.$extravalue.'"></td>';
1926 }
1927 }
1928 } else {
1929 $sql = "SELECT";
1930 $sql .= " fk_object";
1931 foreach ($extralabels as $key => $value) {
1932 $sql .= ", ".$db->sanitize($key);
1933 }
1934 $sql .= " FROM ".MAIN_DB_PREFIX."product_price_extrafields";
1935 $sql .= " WHERE fk_object = ".((int) $lineid->rowid);
1936 $resql = $db->query($sql);
1937 if ($resql) {
1938 $obj = $db->fetch_object($resql);
1939 foreach ($extralabels as $key => $value) {
1940 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))) {
1941 if (!empty($extrafields->attributes["product_price"]['langfile'][$key])) {
1942 $langs->load($extrafields->attributes["product_price"]['langfile'][$key]);
1943 }
1944
1945 $extravalue = (GETPOSTISSET('options_'.$key) ? $extrafield_values['options_'.$key] : $obj->{$key} ?? '');
1946 print '<td align="center"><input name="'.$key.'['.$i.']" size="10" value="'.$extravalue.'"></td>';
1947 }
1948 }
1949 $db->free($resql);
1950 }
1951 }
1952 }
1953 print '</tr>';
1954 }
1955
1956 print '</tbody>';
1957
1958 print '</table>';
1959 print '</div>';
1960
1961 //print dol_get_fiche_end();
1962
1963 print $form->buttonsSaveCancel();
1964
1965 print '</form>';
1966 }
1967}
1968
1969// Add area to show/add/edit a price for a dedicated customer
1970if (getDolGlobalString('PRODUIT_CUSTOMER_PRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
1971 $prodcustprice = new ProductCustomerPrice($db);
1972
1973 $limit = GETPOSTINT('limit') ? GETPOSTINT('limit') : $conf->liste_limit;
1974 $sortfield = GETPOST('sortfield', 'aZ09comma');
1975 $sortorder = GETPOST('sortorder', 'aZ09comma');
1976 $page = (GETPOSTINT("page") ? GETPOSTINT("page") : 0);
1977 if (empty($page) || $page == -1) {
1978 $page = 0;
1979 } // If $page is not defined, or '' or -1
1980 $offset = $limit * $page;
1981 $pageprev = $page - 1;
1982 $pagenext = $page + 1;
1983 if (!$sortorder) {
1984 $sortorder = "ASC,ASC";
1985 }
1986 if (!$sortfield) {
1987 $sortfield = "soc.nom,t.date_begin";
1988 }
1989
1990 // Build filter to display only concerned lines
1991 $filter = array('t.fk_product' => (string) $object->id);
1992
1993 if (!empty($search_soc)) {
1994 $filter['soc.nom'] = (string) $search_soc;
1995 }
1996
1997 if ($action == 'add_customer_price') {
1998 // Form to add a new customer price
1999 $maxpricesupplier = $object->min_recommended_price();
2000
2001 print '<!-- add_customer_price -->';
2002 print load_fiche_titre($langs->trans('AddCustomerPrice'));
2003
2004 print '<form action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="POST">';
2005 print '<input type="hidden" name="token" value="'.newToken().'">';
2006 print '<input type="hidden" name="action" value="add_customer_price_confirm">';
2007 print '<input type="hidden" name="id" value="'.$object->id.'">';
2008
2009 print '<div class="tabBar tabBarWithBottom">';
2010
2011 print '<table class="border centpercent">';
2012 print '<tr>';
2013 print '<td class="fieldrequired">'.$langs->trans('ThirdParty').'</td>';
2014 print '<td>';
2015 $filter = '(s.client:IN:1,2,3)';
2016 print img_picto('', 'company').$form->select_company(GETPOSTINT('socid'), 'socid', $filter, 'SelectThirdParty', 0, 0, array(), 0, 'minwidth300');
2017 print '</td>';
2018 print '</tr>';
2019
2020 // Ref. Customer
2021 print '<tr><td>' . $langs->trans('RefCustomer') . '</td>';
2022 print '<td><input name="ref_customer" size="12"></td></tr>';
2023
2024 // Applied Prices From
2025 $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;
2026 print '<tr><td>'.$langs->trans("AppliedPricesFrom").'</td><td>';
2027 print $form->selectDate(!empty($date_begin) ? $date_begin : dol_now(), "date_begin", 0, 0, 1, "date_begin");
2028 print '</td></tr>';
2029
2030 // Applied Prices To
2031 $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
2032 print '<tr><td>'.$langs->trans("AppliedPricesTo").'</td><td>';
2033 print $form->selectDate($date_end, "date_end", 0, 0, 1, "date_end");
2034 print '</td></tr>';
2035
2036 // VAT
2037 print '<tr><td class="fieldrequired">'.$langs->trans("DefaultTaxRate").'</td><td>';
2038 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);
2039 print '</td></tr>';
2040
2041 // Price base
2042 print '<tr><td class="fieldrequired">';
2043 print $langs->trans('PriceBase');
2044 print '</td>';
2045 print '<td>';
2046 print $form->selectPriceBaseType($object->price_base_type, "price_base_type");
2047 print '</td>';
2048 print '</tr>';
2049
2050 // Price
2051 print '<tr><td class="fieldrequired">';
2052 $text = $langs->trans('SellingPrice');
2053 print $form->textwithpicto($text, $langs->trans("PrecisionUnitIsLimitedToXDecimals", getDolGlobalString('MAIN_MAX_DECIMALS_UNIT')), 1, 'help');
2054 print '</td><td>';
2055 if ($object->price_base_type == 'TTC') {
2056 print '<input name="price" size="10" value="'.price($object->price_ttc).'">';
2057 } else {
2058 print '<input name="price" size="10" value="'.price($object->price).'">';
2059 }
2060 print '</td></tr>';
2061
2062 // Price minimum
2063 print '<tr><td>';
2064 $text = $langs->trans('MinPrice');
2065 print $form->textwithpicto($text, $langs->trans("PrecisionUnitIsLimitedToXDecimals", getDolGlobalString('MAIN_MAX_DECIMALS_UNIT')), 1, 'help');
2066 if ($object->price_base_type == 'TTC') {
2067 print '<td><input name="price_min" size="10" value="'.price($object->price_min_ttc).'">';
2068 } else {
2069 print '<td><input name="price_min" size="10" value="'.price($object->price_min).'">';
2070 }
2071 if (getDolGlobalString('PRODUCT_MINIMUM_RECOMMENDED_PRICE')) {
2072 print '<td class="left">'.$langs->trans("MinimumRecommendedPrice", price($maxpricesupplier, 0, '', 1, -1, -1, 'auto')).' '.img_warning().'</td>';
2073 }
2074 print '</td></tr>';
2075
2076 // Price Label
2077 print '<tr><td>';
2078 print $langs->trans('PriceLabel');
2079 print '</td><td>';
2080 print '<input name="price_label" maxlength="255" class="minwidth300 maxwidth400onsmartphone" value="'.$object->price_label.'">';
2081 print '</td>';
2082 print '</tr>';
2083
2084 // Discount
2085 $discount_percent = price2num(GETPOST("discount_percent"));
2086 print '<tr><td>'.$langs->trans("Discount").'</td><td>';
2087 print '<input name="discount_percent" size="10" value="'.price($discount_percent).'">';
2088 print '</td></tr>';
2089
2090 // Extrafields
2091 $extrafields->fetch_name_optionals_label("product_customer_price");
2092 $extralabels = !empty($extrafields->attributes["product_customer_price"]['label']) ? $extrafields->attributes["product_customer_price"]['label'] : '';
2093 $extrafield_values = $extrafields->getOptionalsFromPost("product_customer_price");
2094 if (!empty($extralabels)) {
2095 if (empty($prodcustprice->id)) {
2096 foreach ($extralabels as $key => $value) {
2097 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))) {
2098 if (!empty($extrafields->attributes["product_customer_price"]['langfile'][$key])) {
2099 $langs->load($extrafields->attributes["product_customer_price"]['langfile'][$key]);
2100 }
2101
2102 print '<tr><td'.($extrafields->attributes["product_customer_price"]['required'][$key] ? ' class="fieldrequired"' : '').'>';
2103 if (!empty($extrafields->attributes["product_customer_price"]['help'][$key])) {
2104 print $form->textwithpicto($langs->trans($value), $langs->trans($extrafields->attributes["product_customer_price"]['help'][$key]));
2105 } else {
2106 print $langs->trans($value);
2107 }
2108 print '</td><td>'.$extrafields->showInputField($key, GETPOSTISSET('options_'.$key) ? $extrafield_values['options_'.$key] : '', '', '', '', '', 0, 'product_customer_price').'</td></tr>';
2109 }
2110 }
2111 }
2112 }
2113
2114 print '</table>';
2115
2116 print '</div>';
2117
2118
2119 print '<div class="center">';
2120
2121 // Update all child soc
2122 print '<div class="marginbottomonly">';
2123 print '<input type="checkbox" name="updatechildprice" id="updatechildprice" value="1"> ';
2124 print '<label for="updatechildprice">'.$langs->trans('ForceUpdateChildPriceSoc').'</label>';
2125 print '</div>';
2126
2127 print $form->buttonsSaveCancel();
2128
2129 print '</form>';
2130 } elseif ($action == 'edit_customer_price') {
2131 // Edit mode
2132 $maxpricesupplier = $object->min_recommended_price();
2133
2134 print '<!-- edit_customer_price -->';
2135 print load_fiche_titre($langs->trans('PriceByCustomer'));
2136
2137 $result = $prodcustprice->fetch(GETPOSTINT('lineid'));
2138 if ($result < 0) {
2139 setEventMessages($prodcustprice->error, $prodcustprice->errors, 'errors');
2140 }
2141
2142 print '<form action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="POST">';
2143 print '<input type="hidden" name="token" value="'.newToken().'">';
2144 print '<input type="hidden" name="action" value="update_customer_price_confirm">';
2145 print '<input type="hidden" name="lineid" value="'.$prodcustprice->id.'">';
2146
2147 print '<table class="liste centpercent">';
2148 print '<tr>';
2149 print '<td class="titlefield fieldrequired">'.$langs->trans('ThirdParty').'</td>';
2150 $staticsoc = new Societe($db);
2151 $staticsoc->fetch($prodcustprice->fk_soc);
2152 print "<td>".$staticsoc->getNomUrl(1)."</td>";
2153 print '</tr>';
2154
2155 // Ref. Customer
2156 print '<tr><td>' . $langs->trans('RefCustomer') . '</td>';
2157 print '<td><input name="ref_customer" size="12" value="' . dol_escape_htmltag($prodcustprice->ref_customer) . '"></td></tr>';
2158
2159 // Applied Prices From
2160 print '<tr><td>'.$langs->trans("AppliedPricesFrom").'</td><td>';
2161 print $form->selectDate($prodcustprice->date_begin, "date_begin", 0, 0, 1, "date_begin");
2162 print '</td></tr>';
2163
2164 // Applied Prices To
2165 print '<tr><td>'.$langs->trans("AppliedPricesTo").'</td><td>';
2166 print $form->selectDate($prodcustprice->date_end, "date_end", 0, 0, 1, "date_end");
2167 print '</td></tr>';
2168
2169 // VAT
2170 print '<tr><td class="fieldrequired">'.$langs->trans("DefaultTaxRate").'</td><td>';
2171 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);
2172 print '</td></tr>';
2173
2174 // Price base
2175 print '<tr><td class="fieldrequired">';
2176 print $langs->trans('PriceBase');
2177 print '</td>';
2178 print '<td>';
2179 print $form->selectPriceBaseType($prodcustprice->price_base_type, "price_base_type");
2180 print '</td>';
2181 print '</tr>';
2182
2183 // Price
2184 print '<tr><td class="fieldrequired">';
2185 $text = $langs->trans('SellingPrice');
2186 print $form->textwithpicto($text, $langs->trans("PrecisionUnitIsLimitedToXDecimals", getDolGlobalString('MAIN_MAX_DECIMALS_UNIT')), 1, 'help');
2187 print '</td><td>';
2188 if ($prodcustprice->price_base_type == 'TTC') {
2189 print '<input name="price" size="10" value="'.price($prodcustprice->price_ttc).'">';
2190 } else {
2191 print '<input name="price" size="10" value="'.price($prodcustprice->price).'">';
2192 }
2193 print '</td></tr>';
2194
2195 // Price minimum
2196 print '<tr><td>';
2197 $text = $langs->trans('MinPrice');
2198 print $form->textwithpicto($text, $langs->trans("PrecisionUnitIsLimitedToXDecimals", getDolGlobalString('MAIN_MAX_DECIMALS_UNIT')), 1, 'help');
2199 print '</td><td>';
2200 if ($prodcustprice->price_base_type == 'TTC') {
2201 print '<input name="price_min" size="10" value="'.price($prodcustprice->price_min_ttc).'">';
2202 } else {
2203 print '<input name="price_min" size="10" value="'.price($prodcustprice->price_min).'">';
2204 }
2205 print '</td>';
2206 if (getDolGlobalString('PRODUCT_MINIMUM_RECOMMENDED_PRICE')) {
2207 print '<td class="left">'.$langs->trans("MinimumRecommendedPrice", price($maxpricesupplier, 0, '', 1, -1, -1, 'auto')).' '.img_warning().'</td>';
2208 }
2209 print '</tr>';
2210
2211 // Price Label
2212 print '<tr><td>';
2213 print $langs->trans('PriceLabel');
2214 print '</td><td>';
2215 print '<input name="price_label" maxlength="255" class="minwidth300 maxwidth400onsmartphone" value="'.$prodcustprice->price_label.'">';
2216 print '</td>';
2217 print '</tr>';
2218
2219 // Discount
2220 print '<tr><td>'.$langs->trans("Discount").'</td><td>';
2221 print '<input name="discount_percent" size="10" value="'.price($prodcustprice->discount_percent).'">';
2222 print '</td></tr>';
2223
2224 // Extrafields
2225 $extrafields->fetch_name_optionals_label("product_customer_price");
2226 $extralabels = !empty($extrafields->attributes["product_customer_price"]['label']) ? $extrafields->attributes["product_customer_price"]['label'] : '';
2227 $extrafield_values = $extrafields->getOptionalsFromPost("product_customer_price");
2228 if (!empty($extralabels)) {
2229 if (empty($object->id)) {
2230 foreach ($extralabels as $key => $value) {
2231 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_customer_price" && $extrafields->attributes["product_customer_price"]['list'][$key] == 4))) {
2232 if (!empty($extrafields->attributes["product_customer_price"]['langfile'][$key])) {
2233 $langs->load($extrafields->attributes["product_customer_price"]['langfile'][$key]);
2234 }
2235
2236 print '<tr><td'.($extrafields->attributes["product_customer_price"]['required'][$key] ? ' class="fieldrequired"' : '').'>';
2237 if (!empty($extrafields->attributes["product_customer_price"]['help'][$key])) {
2238 print $form->textwithpicto($langs->trans($value), $langs->trans($extrafields->attributes["product_customer_price"]['help'][$key]));
2239 } else {
2240 print $langs->trans($value);
2241 }
2242 print '</td><td>'.$extrafields->showInputField($key, GETPOSTISSET('options_'.$key) ? $extrafield_values['options_'.$key] : '', '', '', '', '', 0, 'product_customer_price').'</td></tr>';
2243 }
2244 }
2245 } else {
2246 $sql = "SELECT";
2247 $sql .= " fk_object";
2248 foreach ($extralabels as $key => $value) {
2249 $sql .= ", ".$db->sanitize($key);
2250 }
2251 $sql .= " FROM ".MAIN_DB_PREFIX."product_customer_price_extrafields";
2252 $sql .= " WHERE fk_object = ".((int) $prodcustprice->id);
2253 $resql = $db->query($sql);
2254 if ($resql) {
2255 $obj = $db->fetch_object($resql);
2256 foreach ($extralabels as $key => $value) {
2257 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_customer_price" && $extrafields->attributes["product_customer_price"]['list'][$key] == 4))) {
2258 if (!empty($extrafields->attributes["product_customer_price"]['langfile'][$key])) {
2259 $langs->load($extrafields->attributes["product_customer_price"]['langfile'][$key]);
2260 }
2261
2262 print '<tr><td'.($extrafields->attributes["product_customer_price"]['required'][$key] ? ' class="fieldrequired"' : '').'>';
2263 if (!empty($extrafields->attributes["product_customer_price"]['help'][$key])) {
2264 print $form->textwithpicto($langs->trans($value), $langs->trans($extrafields->attributes["product_customer_price"]['help'][$key]));
2265 } else {
2266 print $langs->trans($value);
2267 }
2268 print '</td><td>'.$extrafields->showInputField($key, GETPOSTISSET('options_'.$key) ? $extrafield_values['options_'.$key] : $obj->{$key}, '', '', '', '', 0, 'product_customer_price');
2269
2270 print '</td></tr>';
2271 }
2272 }
2273 $db->free($resql);
2274 }
2275 }
2276 }
2277
2278 print '</table>';
2279
2280 print '<div class="center">';
2281 print '<div class="marginbottomonly">';
2282 print '<input type="checkbox" name="updatechildprice" id="updatechildprice" value="1"> ';
2283 print '<label for="updatechildprice">'.$langs->trans('ForceUpdateChildPriceSoc').'</label>';
2284 print "</div>";
2285
2286 print $form->buttonsSaveCancel();
2287
2288 print '<br></form>';
2289 } elseif ($action == 'showlog_customer_price') {
2290 // List of all log of prices by customers
2291 print '<!-- list of all log of prices per customer -->'."\n";
2292
2293 $sortfield = 't.datec';
2294 $filter = array('t.fk_product' => (string) $object->id, 't.fk_soc' => (string) GETPOSTINT('socid'));
2295
2296 // Count total nb of records
2297 $nbtotalofrecords = '';
2298 if (!getDolGlobalInt('MAIN_DISABLE_FULL_SCANLIST')) {
2299 $nbtotalofrecords = $prodcustprice->fetchAllLog($sortorder, $sortfield, $conf->liste_limit, $offset, $filter);
2300 }
2301
2302 $result = $prodcustprice->fetchAllLog($sortorder, $sortfield, $conf->liste_limit, $offset, $filter);
2303 if ($result < 0) {
2304 setEventMessages($prodcustprice->error, $prodcustprice->errors, 'errors');
2305 }
2306
2307 $option = '&socid='.GETPOSTINT('socid').'&id='.$object->id;
2308
2309 $staticsoc = new Societe($db);
2310 $staticsoc->fetch(GETPOSTINT('socid'));
2311
2312 $title = $langs->trans('PriceByCustomerLog');
2313 $title .= ' - '.$staticsoc->getNomUrl(1);
2314
2315 $backbutton = '<a class="justalink" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'">'.$langs->trans("Back").'</a>';
2316
2317 // @phan-suppress-next-line PhanPluginSuspiciousParamOrder
2318 print_barre_liste($title, $page, $_SERVER['PHP_SELF'], $option, $sortfield, $sortorder, $backbutton, count($prodcustprice->lines), $nbtotalofrecords, 'title_accountancy.png');
2319
2320 if (count($prodcustprice->lines) > 0) {
2321 print '<form action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="POST">';
2322 print '<input type="hidden" name="token" value="'.newToken().'">';
2323 print '<input type="hidden" name="id" value="'.$object->id.'">';
2324
2325 print '<div class="div-table-responsive-no-min">';
2326 print '<table class="liste centpercent">';
2327
2328 print '<tr class="liste_titre">';
2329 print '<td>'.$langs->trans("ThirdParty").'</td>';
2330 print '<td>'.$langs->trans('RefCustomer').'</td>';
2331 print '<td>'.$langs->trans("AppliedPricesFrom").'</td>';
2332 print '<td>'.$langs->trans("AppliedPricesTo").'</td>';
2333 print '<td class="center">'.$langs->trans("PriceBase").'</td>';
2334 print '<td class="right">'.$langs->trans("DefaultTaxRate").'</td>';
2335 print '<td class="right">'.$langs->trans("HT").'</td>';
2336 print '<td class="right">'.$langs->trans("TTC").'</td>';
2337 if ($mysoc->localtax1_assuj == "1" || $mysoc->localtax2_assuj == "1") {
2338 print '<td class="right">'.$langs->trans("INCT").'</td>';
2339 }
2340 print '<td class="right">'.$langs->trans("MinPrice").' '.$langs->trans("HT").'</td>';
2341 print '<td class="right">'.$langs->trans("MinPrice").' '.$langs->trans("TTC").'</td>';
2342 print '<td class="right">'.$langs->trans("PriceLabel").'</td>';
2343 print '<td class="right">'.$langs->trans("Discount").'</td>';
2344 print '<td class="right">'.$langs->trans("ChangedBy").'</td>';
2345 print '<td>'.$langs->trans("DateCreation").'</td>';
2346 print '</tr>';
2347
2348 foreach ($prodcustprice->lines as $line) {
2349 // Date
2350 $staticsoc = new Societe($db);
2351 $staticsoc->fetch($line->fk_soc);
2352
2353 $tva_tx = $line->default_vat_code ? $line->tva_tx.' ('.$line->default_vat_code.')' : $line->tva_tx;
2354
2355 // Line for default price
2356 if ($line->price_base_type == 'HT') {
2357 $pu = $line->price;
2358 } else {
2359 $pu = $line->price_ttc;
2360 }
2361
2362 // Local tax is not saved into table of product. We use value linked to VAT code.
2363 $localtaxarray = getLocalTaxesFromRate($line->tva_tx.($line->default_vat_code ? ' ('.$line->default_vat_code.')' : ''), 0, $staticsoc, $mysoc);
2364 // Define part of HT, VAT, TTC
2365 $resultarray = calcul_price_total(1, $pu, 0, (float) $line->tva_tx, 1, 1, 0, $line->price_base_type, $line->recuperableonly, $object->type, $mysoc, $localtaxarray);
2366 // Calcul du total ht sans remise
2367 $total_ht = $resultarray[0];
2368 $total_vat = $resultarray[1];
2369 $total_localtax1 = $resultarray[9];
2370 $total_localtax2 = $resultarray[10];
2371 $total_ttc = $resultarray[2];
2372
2373 print '<tr class="oddeven">';
2374
2375 print '<td class="tdoverflowmax125">'.$staticsoc->getNomUrl(1)."</td>";
2376 print '<td>'.$line->ref_customer.'</td>';
2377 print "<td>".dol_print_date($line->date_begin, "day", 'tzuserrel')."</td>";
2378 print "<td>".dol_print_date($line->date_end, "day", 'tzuserrel')."</td>";
2379 print '<td class="center">'.$langs->trans($line->price_base_type)."</td>";
2380 print '<td class="right">';
2381
2382 $positiverates = '';
2383 if (price2num($line->tva_tx)) {
2384 $positiverates .= ($positiverates ? '/' : '').price2num($line->tva_tx);
2385 }
2386 if (price2num($line->localtax1_type)) {
2387 $positiverates .= ($positiverates ? '/' : '').price2num($line->localtax1_tx);
2388 }
2389 if (price2num($line->localtax2_type)) {
2390 $positiverates .= ($positiverates ? '/' : '').price2num($line->localtax2_tx);
2391 }
2392 if (empty($positiverates)) {
2393 $positiverates = '0';
2394 }
2395
2396 echo vatrate($positiverates.($line->default_vat_code ? ' ('.$line->default_vat_code.')' : ''), true, ($line->tva_npr ? $line->tva_npr : $line->recuperableonly));
2397
2398 //. vatrate($tva_tx, true, $line->recuperableonly) .
2399 print "</td>";
2400 print '<td class="right"><span class="amount">'.price($line->price)."</span></td>";
2401
2402 print '<td class="right"><span class="amount">'.price($line->price_ttc)."</span></td>";
2403 if ($mysoc->localtax1_assuj == "1" || $mysoc->localtax2_assuj == "1") {
2404 print '<td class="right">'.price($resultarray[2]).'</td>';
2405 }
2406
2407 print '<td class="right">'.price($line->price_min).'</td>';
2408 print '<td class="right">'.price($line->price_min_ttc).'</td>';
2409 print '<td class="right">'.$line->price_label.'</td>';
2410 print '<td class="right">'.price($line->discount_percent).'</td>';
2411
2412 // User
2413 $userstatic = new User($db);
2414 $userstatic->fetch($line->fk_user);
2415 print '<td class="right">';
2416 print $userstatic->getNomUrl(1, '', 0, 0, 24, 0, 'login');
2417 //print $userstatic->getLoginUrl(1);
2418 print '</td>';
2419 print "<td>".dol_print_date($line->datec, "dayhour", 'tzuserrel')."</td>";
2420 print '</tr>';
2421 }
2422 print "</table>";
2423 print '</div>';
2424 } else {
2425 print $langs->trans('None');
2426 }
2427 } elseif ($action != 'showlog_default_price' && $action != 'edit_price' && $action != 'edit_level_price') {
2428 // List of all prices by customers
2429 print '<!-- list of all prices per customer -->'."\n";
2430
2431 // Count total nb of records
2432 $nbtotalofrecords = '';
2433 if (!getDolGlobalInt('MAIN_DISABLE_FULL_SCANLIST')) {
2434 $nbtotalofrecords = $prodcustprice->fetchAll($sortorder, $sortfield, 0, 0, $filter);
2435 }
2436
2437 $result = $prodcustprice->fetchAll($sortorder, $sortfield, $conf->liste_limit, $offset, $filter);
2438 if ($result < 0) {
2439 setEventMessages($prodcustprice->error, $prodcustprice->errors, 'errors');
2440 }
2441
2442 $option = '&search_soc='.$search_soc.'&id='.$object->id;
2443
2444 // @phan-suppress-next-line PhanPluginSuspiciousParamOrder
2445 print_barre_liste($langs->trans('PriceByCustomer'), $page, $_SERVER ['PHP_SELF'], $option, $sortfield, $sortorder, '', count($prodcustprice->lines), $nbtotalofrecords, 'title_accountancy.png');
2446
2447 print '<form action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="POST">';
2448 print '<input type="hidden" name="token" value="'.newToken().'">';
2449 print '<input type="hidden" name="id" value="'.$object->id.'">';
2450
2451 print '<!-- List of prices per customer -->'."\n";
2452 print '<div class="div-table-responsive-no-min">'."\n";
2453 print '<table class="liste centpercent">'."\n";
2454
2455 if (count($prodcustprice->lines) > 0 || $search_soc) {
2456 $extrafields->fetch_name_optionals_label("product_customer_price");
2457 $custom_price_extralabels = !empty($extrafields->attributes["product_customer_price"]['label']) ? $extrafields->attributes["product_customer_price"]['label'] : '';
2458 if (getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
2459 $colspan = 11;
2460 } else {
2461 $colspan = 12;
2462 }
2463 if ($mysoc->localtax1_assuj == "1" || $mysoc->localtax2_assuj == "1") {
2464 $colspan++;
2465 }
2466 if (!empty($custom_price_extralabels) && is_array($custom_price_extralabels)) {
2467 $colspan += count($custom_price_extralabels);
2468 }
2469
2470 print '<tr class="liste_titre">';
2471 print '<td class="liste_titre"><input type="text" class="flat maxwidth125" name="search_soc" value="'.$search_soc.'"></td>';
2472 print '<td class="liste_titre" colspan="'.$colspan.'">&nbsp;</td>';
2473 // Print the search button
2474 print '<td class="liste_titre maxwidthsearch">';
2475 $searchpicto = $form->showFilterAndCheckAddButtons(0);
2476 print $searchpicto;
2477 print '</td>';
2478 print '</tr>';
2479 }
2480
2481 print '<tr class="liste_titre">';
2482 print '<td>'.$langs->trans("ThirdParty").'</td>';
2483 print '<td>'.$langs->trans('RefCustomer').'</td>';
2484 print '<td>'.$langs->trans("AppliedPricesFrom").'</td>';
2485 print '<td>'.$langs->trans("AppliedPricesTo").'</td>';
2486 print '<td class="center">'.$langs->trans("PriceBase").'</td>';
2487 print '<td class="right">'.$langs->trans("DefaultTaxRate").'</td>';
2488 print '<td class="right">'.$langs->trans("HT").'</td>';
2489 print '<td class="right">'.$langs->trans("TTC").'</td>';
2490 if ($mysoc->localtax1_assuj == "1" || $mysoc->localtax2_assuj == "1") {
2491 print '<td class="right">'.$langs->trans("INCT").'</td>';
2492 }
2493 print '<td class="right">'.$langs->trans("MinPrice").' '.$langs->trans("HT").'</td>';
2494 print '<td class="right">'.$langs->trans("MinPrice").' '.$langs->trans("TTC").'</td>';
2495 print '<td class="right">'.$langs->trans("PriceLabel").'</td>';
2496 print '<td class="right">'.$langs->trans("Discount").'</td>';
2497 // fetch optionals attributes and labels
2498 $extrafields->fetch_name_optionals_label("product_customer_price");
2499 if ($extrafields->attributes["product_customer_price"] && array_key_exists('label', $extrafields->attributes["product_customer_price"])) {
2500 $extralabels = $extrafields->attributes["product_customer_price"]['label'];
2501 if (!empty($extralabels)) {
2502 foreach ($extralabels as $key => $value) {
2503 // Show field if not hidden
2504 if (!empty($extrafields->attributes["product_customer_price"]['list'][$key]) && $extrafields->attributes["product_customer_price"]['list'][$key] != 3) {
2505 if (!empty($extrafields->attributes["product_customer_price"]['langfile'][$key])) {
2506 $langs->load($extrafields->attributes["product_customer_price"]['langfile'][$key]);
2507 }
2508 if (!empty($extrafields->attributes["product_customer_price"]['help'][$key])) {
2509 $extratitle = $form->textwithpicto($langs->trans($value), $langs->trans($extrafields->attributes["product_customer_price"]['help'][$key]));
2510 } else {
2511 $extratitle = $langs->trans($value);
2512 }
2513 print '<td style="text-align: right">'.$extratitle.'</td>';
2514 }
2515 }
2516 }
2517 }
2518 print '<td>'.$langs->trans("ChangedBy").'</td>';
2519 print '<td></td>';
2520 print '</tr>';
2521
2522 // Line for default price
2523 if ($object->price_base_type == 'HT') {
2524 $pu = $object->price;
2525 } else {
2526 $pu = $object->price_ttc;
2527 }
2528
2529 // Local tax was not saved into table llx_product on old versions. So we will use the value linked to the VAT code.
2530 $localtaxarray = getLocalTaxesFromRate($object->tva_tx.($object->default_vat_code ? ' ('.$object->default_vat_code.')' : ''), 0, $mysoc, $mysoc);
2531 // Define part of HT, VAT, TTC
2532 $resultarray = calcul_price_total(1, $pu, 0, $object->tva_tx, 1, 1, 0, $object->price_base_type, 0, $object->type, $mysoc, $localtaxarray);
2533 // Calcul du total ht sans remise
2534 $total_ht = $resultarray[0];
2535 $total_vat = $resultarray[1];
2536 $total_localtax1 = $resultarray[9];
2537 $total_localtax2 = $resultarray[10];
2538 $total_ttc = $resultarray[2];
2539
2540 if (!getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
2541 print '<!-- PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES -->'."\n";
2542 print '<tr class="oddeven">';
2543 print '<td colspan="4">' . $langs->trans('Default') . '</td>';
2544
2545 print '<td class="center">'.$langs->trans($object->price_base_type)."</td>";
2546
2547 // VAT Rate
2548 print '<td class="right">';
2549
2550 $positiverates = '';
2551 if (price2num($object->tva_tx)) {
2552 $positiverates .= ($positiverates ? '/' : '').price2num($object->tva_tx);
2553 }
2554 if (price2num($object->localtax1_type)) {
2555 $positiverates .= ($positiverates ? '/' : '').price2num($object->localtax1_tx);
2556 }
2557 if (price2num($object->localtax2_type)) {
2558 $positiverates .= ($positiverates ? '/' : '').price2num($object->localtax2_tx);
2559 }
2560 if (empty($positiverates)) {
2561 $positiverates = '0';
2562 }
2563 echo vatrate($positiverates.($object->default_vat_code ? ' ('.$object->default_vat_code.')' : ''), true, $object->tva_npr);
2564
2565 //print vatrate($object->tva_tx, true, $object->tva_npr);
2566 //print $object->default_vat_code?' ('.$object->default_vat_code.')':'';
2567 print "</td>";
2568
2569 print '<td class="right"><span class="amount">'.price($object->price)."</span></td>";
2570
2571 print '<td class="right"><span class="amount">'.price($object->price_ttc)."</span></td>";
2572 if ($mysoc->localtax1_assuj == "1" || $mysoc->localtax2_assuj == "1") {
2573 //print '<td class="right">' . price($object->price_ttc) . "</td>";
2574 print '<td class="right"><span class="amount">'.price($resultarray[2]).'</span></td>';
2575 }
2576
2577 print '<td class="right">'.price($object->price_min).'</td>';
2578 print '<td class="right">'.price($object->price_min_ttc).'</td>';
2579 print '<td class="right">'.$object->price_label.'</td>';
2580 print '<td class="right"></td>';
2581 print '<td class="right"></td>';
2582 if (!empty($extralabels)) {
2583 foreach ($extralabels as $key) {
2584 // Show field if not hidden
2585 if (!empty($extrafields->attributes["product_customer_price"]['list'][$key]) && $extrafields->attributes["product_customer_price"]['list'][$key] != 3) {
2586 print '<td class="right"></td>';
2587 }
2588 }
2589 }
2590 if ($user->hasRight('produit', 'supprimer') || $user->hasRight('service', 'supprimer')) {
2591 print '<td class="nowraponall">';
2592 print '<a class="marginleftonly marginrightonly" href="'.$_SERVER["PHP_SELF"].'?action=showlog_default_price&token='.newToken().'&id='.$object->id.'">';
2593 print img_info($langs->trans('PriceByCustomerLog'));
2594 print '</a>';
2595 print ' ';
2596 print '<a class="marginleftonly marginrightonly editfielda" href="'.$_SERVER["PHP_SELF"].'?action=edit_price&token='.newToken().'&id='.$object->id.'">';
2597 print img_edit('default', 0, 'style="vertical-align: middle;"');
2598 print '</a>';
2599 print '</td>';
2600 }
2601 print "</tr>\n";
2602 }
2603
2604 if (count($prodcustprice->lines) > 0) {
2605 foreach ($prodcustprice->lines as $line) {
2606 // Date
2607 $staticsoc = new Societe($db);
2608 $staticsoc->fetch($line->fk_soc);
2609
2610 $tva_tx = $line->default_vat_code ? $line->tva_tx.' ('.$line->default_vat_code.')' : $line->tva_tx;
2611
2612 // Line for default price
2613 if ($line->price_base_type == 'HT') {
2614 $pu = $line->price;
2615 } else {
2616 $pu = $line->price_ttc;
2617 }
2618
2619 // Local tax is not saved into table of product. We use value linked to VAT code.
2620 $localtaxarray = getLocalTaxesFromRate($line->tva_tx.($line->default_vat_code ? ' ('.$line->default_vat_code.')' : ''), 0, $staticsoc, $mysoc);
2621 // Define part of HT, VAT, TTC
2622 $resultarray = calcul_price_total(1, $pu, 0, (float) $line->tva_tx, 1, 1, 0, $line->price_base_type, $line->recuperableonly, $object->type, $mysoc, $localtaxarray);
2623 // Calcul du total ht sans remise
2624 $total_ht = $resultarray[0];
2625 $total_vat = $resultarray[1];
2626 $total_localtax1 = $resultarray[9];
2627 $total_localtax2 = $resultarray[10];
2628 $total_ttc = $resultarray[2];
2629
2630 print '<tr class="oddeven">';
2631
2632 print '<td class="tdoverflowmax125">'.$staticsoc->getNomUrl(1)."</td>";
2633 print '<td>'.dol_escape_htmltag($line->ref_customer).'</td>';
2634 print "<td>".dol_print_date($line->date_begin, "day", 'tzuserrel')."</td>";
2635 print "<td>".dol_print_date($line->date_end, "day", 'tzuserrel')."</td>";
2636 print '<td class="center">'.$langs->trans($line->price_base_type)."</td>";
2637 // VAT Rate
2638 print '<td class="right">';
2639
2640 $positiverates = '';
2641 if (price2num($line->tva_tx)) {
2642 $positiverates .= ($positiverates ? '/' : '').price2num($line->tva_tx);
2643 }
2644 if (price2num($line->localtax1_type)) {
2645 $positiverates .= ($positiverates ? '/' : '').price2num($line->localtax1_tx);
2646 }
2647 if (price2num($line->localtax2_type)) {
2648 $positiverates .= ($positiverates ? '/' : '').price2num($line->localtax2_tx);
2649 }
2650 if (empty($positiverates)) {
2651 $positiverates = '0';
2652 }
2653
2654 echo vatrate($positiverates.($line->default_vat_code ? ' ('.$line->default_vat_code.')' : ''), true, (!empty($line->tva_npr) ? $line->tva_npr : $line->recuperableonly));
2655
2656 print "</td>";
2657
2658 print '<td class="right"><span class="amount">'.price($line->price)."</span></td>";
2659
2660 print '<td class="right"><span class="amount">'.price($line->price_ttc)."</span></td>";
2661 if ($mysoc->localtax1_assuj == "1" || $mysoc->localtax2_assuj == "1") {
2662 //print '<td class="right">' . price($line->price_ttc) . "</td>";
2663 print '<td class="right"><span class="amount">'.price($resultarray[2]).'</span></td>';
2664 }
2665
2666 print '<td class="right">'.price($line->price_min).'</td>';
2667 print '<td class="right">'.price($line->price_min_ttc).'</td>';
2668 print '<td class="right">'.$line->price_label.'</td>';
2669 print '<td class="right">'.price($line->discount_percent).'</td>';
2670
2671 // Extrafields
2672 $extrafields->fetch_name_optionals_label("product_customer_price");
2673 $extralabels = $extrafields->attributes["product_customer_price"]['label'] ?? array();
2674 if (!empty($extralabels)) {
2675 $sql = "SELECT";
2676 $sql .= " fk_object";
2677 foreach ($extralabels as $key => $value) {
2678 $sql .= ", ".$db->sanitize($key);
2679 }
2680 $sql .= " FROM ".MAIN_DB_PREFIX."product_customer_price_extrafields";
2681 $sql .= " WHERE fk_object = ".((int) $line->id);
2682 $resql = $db->query($sql);
2683 if ($resql) {
2684 if ($db->num_rows($resql) != 1) {
2685 foreach ($extralabels as $key => $value) {
2686 if (!empty($extrafields->attributes["product_customer_price"]['list'][$key]) && $extrafields->attributes["product_customer_price"]['list'][$key] != 3) {
2687 print "<td></td>";
2688 }
2689 }
2690 } else {
2691 $obj = $db->fetch_object($resql);
2692 foreach ($extralabels as $key => $value) {
2693 if (!empty($extrafields->attributes["product_customer_price"]['list'][$key]) && $extrafields->attributes["product_customer_price"]['list'][$key] != 3) {
2694 print '<td align="right">'.$extrafields->showOutputField($key, $obj->{$key}, '', 'product_customer_price')."</td>";
2695 }
2696 }
2697 }
2698 $db->free($resql);
2699 }
2700 }
2701
2702 // User
2703 $userstatic = new User($db);
2704 $userstatic->fetch($line->fk_user);
2705 // @TODO Add a cache on $users object
2706 print '<td>';
2707 print $userstatic->getNomUrl(-1, '', 0, 0, 24, 0, 'login');
2708 print '</td>';
2709
2710 // Todo Edit or delete button
2711 // Action
2712 if ($user->hasRight('produit', 'supprimer') || $user->hasRight('service', 'supprimer')) {
2713 print '<td class="right nowraponall">';
2714 print '<a href="'.$_SERVER["PHP_SELF"].'?action=showlog_customer_price&token='.newToken().'&id='.$object->id.'&socid='.$line->fk_soc.'">';
2715 print img_info($langs->trans('PriceByCustomerLog'));
2716 print '</a>';
2717 print ' ';
2718 print '<a class="marginleftonly editfielda" href="'.$_SERVER["PHP_SELF"].'?action=edit_customer_price&token='.newToken().'&id='.$object->id.'&lineid='.$line->id.'">';
2719 print img_edit('default', 0, 'style="vertical-align: middle;"');
2720 print '</a>';
2721 print ' ';
2722 print '<a class="marginleftonly" href="'.$_SERVER["PHP_SELF"].'?action=ask_remove_customer_price&token='.newToken().'&id='.$object->id.'&lineid='.$line->id.'">';
2723 print img_delete('default', 'style="vertical-align: middle;"');
2724 print '</a>';
2725 print '</td>';
2726 }
2727
2728 print "</tr>\n";
2729 }
2730 }
2731
2732 print "</table>";
2733 print '</div>';
2734
2735 print "</form>";
2736 }
2737}
2738
2739// List of price changes - log historic (ordered by descending date)
2740
2741if ((!getDolGlobalString('PRODUIT_CUSTOMER_PRICES') || $action == 'showlog_default_price') && !in_array($action, array('edit_price', 'edit_level_price', 'edit_vat'))) {
2742 $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,";
2743 $sql .= " p.price_level, p.price_min, p.price_min_ttc,p.price_by_qty,";
2744 $sql .= " p.date_price as dp, p.fk_price_expression, u.rowid as user_id, u.login";
2745 $sql .= " ,p.price_label";
2746 $sql .= " FROM ".MAIN_DB_PREFIX."product_price as p,";
2747 $sql .= " ".MAIN_DB_PREFIX."user as u";
2748 $sql .= " WHERE fk_product = ".((int) $object->id);
2749 $sql .= " AND p.entity IN (".getEntity('productprice').")";
2750 $sql .= " AND p.fk_user_author = u.rowid";
2751 if (!empty($socid) && (getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) && $soc !== null) {
2752 $sql .= " AND p.price_level = ".((int) $soc->price_level);
2753 }
2754 $sql .= " ORDER BY p.date_price DESC, p.rowid DESC, p.price_level ASC";
2755 // $sql .= $db->plimit();
2756 //print $sql;
2757
2758 $result = $db->query($sql);
2759 if ($result) {
2760 print '<div class="divlogofpreviouscustomerprice">';
2761
2762 $num = $db->num_rows($result);
2763
2764 if (!$num) {
2765 $db->free($result);
2766
2767 // Il doit au moins y avoir la ligne de prix initial.
2768 // On l'ajoute donc pour remettre a niveau (pb vieilles versions)
2769 // We emulate the change of the price from interface with the same value than the one into table llx_product
2770 if (getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
2771 $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);
2772 } else {
2773 $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));
2774 }
2775
2776 if ($ret < 0) {
2777 dol_print_error($db, $object->error, $object->errors);
2778 } else {
2779 $result = $db->query($sql);
2780 $num = $db->num_rows($result);
2781 }
2782 }
2783
2784 if ($num > 0) {
2785 // Default prices or
2786 // Log of previous customer prices
2787 $backbutton = '<a class="justalink" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'">'.$langs->trans("Back").'</a>';
2788
2789 if (getDolGlobalString('PRODUIT_CUSTOMER_PRICES')) {
2790 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition, PhanPluginSuspiciousParamOrder
2791 print_barre_liste($langs->trans("DefaultPriceLog"), 0, $_SERVER["PHP_SELF"], '', '', '', $backbutton, 0, $num, 'title_accountancy.png');
2792 } else {
2793 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition, PhanPluginSuspiciousParamOrder
2794 print_barre_liste($langs->trans("PriceByCustomerLog"), 0, $_SERVER["PHP_SELF"], '', '', '', '', 0, $num, 'title_accountancy.png');
2795 }
2796
2797 print '<!-- List of log prices -->'."\n";
2798 print '<div class="div-table-responsive">'."\n";
2799 print '<table class="liste centpercent noborder">'."\n";
2800
2801 print '<tr class="liste_titre">';
2802 print '<th>'.$langs->trans("AppliedPricesFrom").'</tg>';
2803
2804 if (getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
2805 print '<th class="center">'.$langs->trans("PriceLevel").'</th>';
2806 }
2807 if (getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) {
2808 print '<th class="center">'.$langs->trans("Type").'</th>';
2809 }
2810
2811 print '<th class="center">'.$langs->trans("PriceBase").'</th>';
2812 if (!getDolGlobalString('PRODUIT_MULTIPRICES') && !getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) {
2813 print '<th class="right">'.$langs->trans("DefaultTaxRate").'</th>';
2814 }
2815 print '<th class="right">'.$langs->trans("HT").'</th>';
2816 print '<th class="right">'.$langs->trans("TTC").'</th>';
2817 if ($mysoc->localtax1_assuj == "1" || $mysoc->localtax2_assuj == "1") {
2818 print '<th class="right">'.$langs->trans("INCT").'</th>';
2819 }
2820 if (isModEnabled('dynamicprices')) {
2821 print '<th class="right">'.$langs->trans("PriceExpressionSelected").'</th>';
2822 }
2823 print '<th class="right">'.$langs->trans("MinPrice").' '.$langs->trans("HT").'</th>';
2824 print '<th class="right">'.$langs->trans("MinPrice").' '.$langs->trans("TTC").'</th>';
2825 print '<th class="right">'.$langs->trans("Label").'</th>';
2826 print '<th>'.$langs->trans("ChangedBy").'</th>';
2827 if ($user->hasRight('produit', 'supprimer')) {
2828 print '<th class="right">&nbsp;</th>';
2829 }
2830 print '</tr>';
2831
2832 $notfirstlineforlevel = array();
2833
2834 $i = 0;
2835 while ($i < $num) {
2836 $objp = $db->fetch_object($result);
2837
2838 print '<tr class="oddeven">';
2839 // Date
2840 print "<td>".dol_print_date($db->jdate($objp->dp), "dayhour", 'tzuserrel')."</td>";
2841
2842 // Price level
2843 if (getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
2844 print '<td class="center">'.$objp->price_level."</td>";
2845 }
2846 // Price by quantity
2847 if (getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) {
2848 $type = ($objp->price_by_qty == 1) ? 'PriceByQuantity' : 'Standard';
2849 print '<td class="center">'.$langs->trans($type)."</td>";
2850 }
2851
2852 print '<td class="center">';
2853 if (empty($objp->price_by_qty)) {
2854 print $langs->trans($objp->price_base_type);
2855 }
2856 print "</td>";
2857
2858 if (!getDolGlobalString('PRODUIT_MULTIPRICES') && !getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) {
2859 print '<td class="right">';
2860
2861 if (empty($objp->price_by_qty)) {
2862 $positiverates = '';
2863 if (price2num($objp->tva_tx)) {
2864 $positiverates .= ($positiverates ? '/' : '').price2num($objp->tva_tx);
2865 }
2866 if (price2num($objp->localtax1_type)) {
2867 $positiverates .= ($positiverates ? '/' : '').price2num($objp->localtax1_tx);
2868 }
2869 if (price2num($objp->localtax2_type)) {
2870 $positiverates .= ($positiverates ? '/' : '').price2num($objp->localtax2_tx);
2871 }
2872 if (empty($positiverates)) {
2873 $positiverates = '0';
2874 }
2875 echo vatrate($positiverates.($objp->default_vat_code ? ' ('.$objp->default_vat_code.')' : ''), true, !empty($objp->tva_npr) ? $objp->tva_npr : 0);
2876 /*
2877 if ($objp->default_vat_code)
2878 {
2879 print vatrate($objp->tva_tx, true) . ' ('.$objp->default_vat_code.')';
2880 }
2881 else print vatrate($objp->tva_tx, true, $objp->recuperableonly);*/
2882 }
2883
2884 print "</td>";
2885 }
2886
2887 // Line for default price
2888 if ($objp->price_base_type == 'HT') {
2889 $pu = $objp->price;
2890 } else {
2891 $pu = $objp->price_ttc;
2892 }
2893
2894 // Local tax was not saved into table llx_product on old version. So we will use value linked to VAT code.
2895 $localtaxarray = getLocalTaxesFromRate($objp->tva_tx.($object->default_vat_code ? ' ('.$object->default_vat_code.')' : ''), 0, $mysoc, $mysoc);
2896 // Define part of HT, VAT, TTC
2897 $resultarray = calcul_price_total(1, $pu, 0, $objp->tva_tx, 1, 1, 0, $objp->price_base_type, $objp->recuperableonly, $object->type, $mysoc, $localtaxarray);
2898 // Calcul du total ht sans remise
2899 $total_ht = $resultarray[0];
2900 $total_vat = $resultarray[1];
2901 $total_localtax1 = $resultarray[9];
2902 $total_localtax2 = $resultarray[10];
2903 $total_ttc = $resultarray[2];
2904
2905 // Price
2906 if (!empty($objp->fk_price_expression) && !empty($conf->dynamicprices->enabled)) {
2907 $price_expression = new PriceExpression($db);
2908 $res = $price_expression->fetch($objp->fk_price_expression);
2909 $title = $price_expression->title;
2910 print '<td class="right"></td>';
2911 print '<td class="right"></td>';
2912 if ($mysoc->localtax1_assuj == "1" || $mysoc->localtax2_assuj == "1") {
2913 print '<td class="right"></td>';
2914 }
2915 print '<td class="right">'.$title."</td>";
2916 } else {
2917 // Price HT
2918 print '<td class="right">';
2919 if (empty($objp->price_by_qty)) {
2920 print '<span class="amount">'.price($objp->price).'</span>';
2921 }
2922 print "</td>";
2923 // Price TTC
2924 print '<td class="right">';
2925 if (empty($objp->price_by_qty)) {
2926 $price_ttc = $objp->price_ttc;
2927 print '<span class="amount">'.price($price_ttc).'<span>';
2928 }
2929 print "</td>";
2930 if ($mysoc->localtax1_assuj == "1" || $mysoc->localtax2_assuj == "1") {
2931 print '<td class="right">';
2932 print $resultarray[2];
2933 print '</td>';
2934 }
2935 if (isModEnabled('dynamicprices')) { // Only if module is enabled
2936 print '<td class="right"></td>';
2937 }
2938 }
2939
2940 // Price min
2941 print '<td class="right">';
2942 if (empty($objp->price_by_qty)) {
2943 print price($objp->price_min);
2944 }
2945 print '</td>';
2946
2947 // Price min inc tax
2948 print '<td class="right">';
2949 if (empty($objp->price_by_qty)) {
2950 $price_min_ttc = $objp->price_min_ttc;
2951 print price($price_min_ttc);
2952 }
2953 print '</td>';
2954
2955 // Price Label
2956 print '<td class="right">';
2957 print $objp->price_label;
2958 print '</td>';
2959
2960 // User
2961 print '<td>';
2962 if ($objp->user_id > 0) {
2963 $userstatic = new User($db);
2964 $userstatic->fetch($objp->user_id);
2965 print $userstatic->getNomUrl(-1, '', 0, 0, 24, 0, 'login');
2966 }
2967 print '</td>';
2968
2969
2970 // Action
2971 if ($user->hasRight('produit', 'supprimer')) {
2972 $candelete = 0;
2973 if (getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
2974 if (empty($notfirstlineforlevel[$objp->price_level])) {
2975 $notfirstlineforlevel[$objp->price_level] = 1;
2976 } else {
2977 $candelete = 1;
2978 }
2979 } elseif ($i > 0) {
2980 $candelete = 1;
2981 }
2982
2983 print '<td class="right">';
2984 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
2985 print '<a href="'.$_SERVER["PHP_SELF"].'?action=delete&token='.newToken().'&id='.$object->id.'&lineid='.$objp->rowid.'">';
2986 print img_delete();
2987 print '</a>';
2988 } else {
2989 print '&nbsp;'; // Can not delete last price (it's current price)
2990 }
2991 print '</td>';
2992 }
2993
2994 print "</tr>\n";
2995 $i++;
2996 }
2997
2998 $db->free($result);
2999 print "</table>";
3000 print '</div>';
3001 print "<br>";
3002 }
3003
3004 print '</div>';
3005 } else {
3006 dol_print_error($db);
3007 }
3008}
3009
3010// End of page
3011llxFooter();
3012$db->close();
$id
Support class for third parties, contacts, members, users or resources.
Definition account.php:48
if( $user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition card.php:67
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.
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...
load_fiche_titre($title, $morehtmlright='', $picto='generic', $pictoisfullpath=0, $id='', $morecssontable='', $morehtmlcenter='')
Load a title with picto.
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.
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.
dol_now($mode='auto')
Return date for now.
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($object, $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...
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.
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, 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...
global $conf
The following vars must be defined: $type2label $form $conf, $lang, The following vars may also be de...
Definition member.php:79
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
Definition repair.php:158
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.