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 == 'delete_customer_price' && $prodcustprice !== null && ($user->hasRight('produit', 'supprimer') || $user->hasRight('service', 'supprimer'))) {
842 // Delete price by customer
843 $prodcustprice->id = GETPOSTINT('lineid');
844 $result = $prodcustprice->delete($user);
845
846 if ($result > 0) {
847 $db->query("DELETE FROM ".MAIN_DB_PREFIX."product_customer_price_extrafields WHERE fk_object = ".((int) $prodcustprice->id));
848 setEventMessages($langs->trans("PriceRemoved"), null, 'mesgs');
849 } else {
850 $error++;
851 setEventMessages($object->error, $object->errors, 'errors');
852 }
853
854 if ($result < 0) {
855 setEventMessages($prodcustprice->error, $prodcustprice->errors, 'errors');
856 } else {
857 setEventMessages($langs->trans('RecordDeleted'), null, 'mesgs');
858 }
859 $action = '';
860 }
861
862 if ($action == 'update_customer_price_confirm' && !$cancel && $prodcustprice !== null && ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
863 $maxpricesupplier = $object->min_recommended_price();
864
865 $update_child_soc = GETPOSTINT('updatechildprice');
866
867 $prodcustprice->fetch(GETPOSTINT('lineid'));
868
869 // update price by customer
870 $prodcustprice->ref_customer = GETPOST('ref_customer', 'alpha');
871 $prodcustprice->price = price2num(GETPOST("price"), 'MU');
872 $prodcustprice->price_min = price2num(GETPOST("price_min"), 'MU');
873 $prodcustprice->price_base_type = GETPOST("price_base_type", 'alpha');
874 $prodcustprice->price_label = GETPOST("price_label", 'alpha');
875 $prodcustprice->discount_percent = price2num(GETPOST("discount_percent"));
876 $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;
877 $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
878
879 $extralabels = $extrafields->fetch_name_optionals_label("product_customer_price");
880 $extrafield_values = $extrafields->getOptionalsFromPost("product_customer_price");
881
882 $tva_tx_txt = GETPOST("tva_tx");
883
884 $tva_tx = $tva_tx_txt;
885 $vatratecode = '';
886 if (preg_match('/\‍((.*)\‍)/', $tva_tx_txt, $reg)) {
887 $vat_src_code = $reg[1];
888 $tva_tx = preg_replace('/\s*\‍(.*\‍)/', '', $tva_tx_txt); // Remove code into vatrate.
889 }
890 $tva_tx = price2num(preg_replace('/\*/', '', $tva_tx)); // keep remove all after the numbers and dot
891
892 $npr = preg_match('/\*/', $tva_tx_txt) ? 1 : 0;
893 $localtax1 = 0;
894 $localtax2 = 0;
895 $localtax1_type = '0';
896 $localtax2_type = '0';
897 // If value contains the unique code of vat line (new recommended method), we use it to find npr and local taxes
898 if (preg_match('/\‍((.*)\‍)/', $tva_tx_txt, $reg)) {
899 // We look into database using code
900 $vatratecode = $reg[1];
901 // Get record from code
902 $sql = "SELECT t.rowid, t.code, t.recuperableonly, t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
903 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
904 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($mysoc->country_code)."'";
905 $sql .= " AND t.taux = ".((float) $tva_tx)." AND t.active = 1";
906 $sql .= " AND t.code ='".$db->escape($vatratecode)."'";
907 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
908 $resql = $db->query($sql);
909 if ($resql) {
910 $obj = $db->fetch_object($resql);
911 if ($obj) {
912 $npr = $obj->recuperableonly;
913 $localtax1 = $obj->localtax1;
914 $localtax2 = $obj->localtax2;
915 $localtax1_type = $obj->localtax1_type;
916 $localtax2_type = $obj->localtax2_type;
917 }
918
919 // If spain, we don't use the localtax found into tax record in database with same code, but using the get_localtax rule.
920 if (in_array($mysoc->country_code, array('ES'))) {
921 $localtax1 = get_localtax($tva_tx, 1);
922 $localtax2 = get_localtax($tva_tx, 2);
923 }
924 }
925 } else {
926 // Get record with empty code
927 $sql = "SELECT t.rowid, t.code, t.recuperableonly, t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
928 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
929 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($mysoc->country_code)."'";
930 $sql .= " AND t.taux = ".((float) $tva_tx)." AND t.active = 1";
931 $sql .= " AND t.code = ''";
932 $resql = $db->query($sql);
933 if ($resql) {
934 $obj = $db->fetch_object($resql);
935 if ($obj) {
936 $npr = $obj->recuperableonly;
937 $localtax1 = $obj->localtax1;
938 $localtax2 = $obj->localtax2;
939 $localtax1_type = $obj->localtax1_type;
940 $localtax2_type = $obj->localtax2_type;
941 }
942 }
943 }
944
945 $prodcustprice->default_vat_code = $vatratecode;
946 $prodcustprice->tva_tx = $tva_tx;
947 $prodcustprice->recuperableonly = $npr;
948 $prodcustprice->localtax1_tx = $localtax1;
949 $prodcustprice->localtax2_tx = $localtax2;
950 $prodcustprice->localtax1_type = $localtax1_type;
951 $prodcustprice->localtax2_type = $localtax2_type;
952
953 if ($prodcustprice->price_min < $maxpricesupplier && getDolGlobalString('PRODUCT_MINIMUM_RECOMMENDED_PRICE')) {
954 setEventMessages($langs->trans("MinimumPriceLimit", price($maxpricesupplier, 0, '', 1, -1, -1, 'auto')), null, 'errors');
955 $error++;
956 $action = 'update_customer_price';
957 }
958
959 if (!$error) {
960 $result = $prodcustprice->update($user, 0, $update_child_soc);
961 if ($result > 0) {
962 if (!empty($extrafield_values) && is_array($extrafield_values)) {
963 $productcustomerprice = new ProductCustomerPrice($db);
964 $res = $productcustomerprice->fetch($prodcustprice->id);
965 if ($res > 0) {
966 foreach ($extrafield_values as $key => $value) {
967 $productcustomerprice->array_options[$key] = $value;
968 }
969 $result2 = $productcustomerprice->insertExtraFields();
970 if ($result2 < 0) {
971 $prodcustprice->error = $productcustomerprice->error;
972 $prodcustprice->errors = $productcustomerprice->errors;
973 $error++;
974 }
975 }
976 }
977 }
978
979 if ($result < 0) {
980 setEventMessages($prodcustprice->error, $prodcustprice->errors, 'errors');
981 $action = 'update_customer_price';
982 } else {
983 setEventMessages($langs->trans("Save"), null, 'mesgs');
984 $action = '';
985 }
986 }
987 }
988}
989
990
991/*
992 * View
993 */
994
995$form = new Form($db);
996
997if (!empty($id) || !empty($ref)) {
998 // fetch updated prices
999 $object->fetch($id, $ref);
1000}
1001
1002$title = $langs->trans('ProductServiceCard');
1003$helpurl = '';
1004$shortlabel = dol_trunc($object->label, 16);
1005if (GETPOST("type") == '0' || ($object->type == Product::TYPE_PRODUCT)) {
1006 $title = $langs->trans('Product')." ".$shortlabel." - ".$langs->trans('SellingPrices');
1007 $helpurl = 'EN:Module_Products|FR:Module_Produits|ES:M&oacute;dulo_Productos';
1008}
1009if (GETPOST("type") == '1' || ($object->type == Product::TYPE_SERVICE)) {
1010 $title = $langs->trans('Service')." ".$shortlabel." - ".$langs->trans('SellingPrices');
1011 $helpurl = 'EN:Module_Services_En|FR:Module_Services|ES:M&oacute;dulo_Servicios';
1012}
1013
1014llxHeader('', $title, $helpurl, '', 0, 0, '', '', '', 'classforhorizontalscrolloftabs mod-product page-price');
1015
1016$head = product_prepare_head($object);
1017$titre = $langs->trans("CardProduct".$object->type);
1018$picto = ($object->type == Product::TYPE_SERVICE ? 'service' : 'product');
1019
1020print dol_get_fiche_head($head, 'price', $titre, -1, $picto);
1021
1022$linkback = '<a href="'.DOL_URL_ROOT.'/product/list.php?restore_lastsearch_values=1&type='.$object->type.'">'.$langs->trans("BackToList").'</a>';
1023$object->next_prev_filter = "(te.fk_product_type:=:".((int) $object->type).")";
1024
1025$shownav = 1;
1026if ($user->socid && !in_array('product', explode(',', getDolGlobalString('MAIN_MODULES_FOR_EXTERNAL')))) {
1027 $shownav = 0;
1028}
1029
1030dol_banner_tab($object, 'ref', $linkback, $shownav, 'ref');
1031
1032
1033print '<div class="fichecenter">';
1034
1035print '<div class="underbanner clearboth"></div>';
1036print '<table class="border tableforfield centpercent">';
1037
1038$soc = null;
1039
1040// Price per customer segment/level
1041if (getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
1042 // Price and min price are variable (depends on level of company).
1043 if (!empty($socid)) {
1044 $soc = new Societe($db);
1045 $soc->id = $socid;
1046 $soc->fetch($socid);
1047
1048 // Type
1049 if (isModEnabled("product") && isModEnabled("service")) {
1050 $typeformat = 'select;0:'.$langs->trans("Product").',1:'.$langs->trans("Service");
1051 print '<tr><td class="">';
1052 print (!getDolGlobalString('PRODUCT_DENY_CHANGE_PRODUCT_TYPE')) ? $form->editfieldkey("Type", 'fk_product_type', (string) $object->type, $object, 0, $typeformat) : $langs->trans('Type');
1053 print '</td><td>';
1054 print $form->editfieldval("Type", 'fk_product_type', $object->type, $object, 0, $typeformat);
1055 print '</td></tr>';
1056 }
1057
1058 // Selling price
1059 print '<tr><td class="titlefieldcreate">';
1060 print $langs->trans("SellingPrice");
1061 print '</td>';
1062 print '<td colspan="2">';
1063 if ($object->multiprices_base_type[$soc->price_level] == 'TTC') {
1064 print '<span class="amount">'.price($object->multiprices_ttc[$soc->price_level]).'</span>';
1065 } else {
1066 print '<span class="amount">'.price($object->multiprices[$soc->price_level]).'</span>';
1067 }
1068 if ($object->multiprices_base_type[$soc->price_level]) {
1069 print ' '.$langs->trans($object->multiprices_base_type[$soc->price_level]);
1070 } else {
1071 print ' '.$langs->trans($object->price_base_type);
1072 }
1073 print '</td></tr>';
1074
1075 // Price min
1076 print '<tr><td>'.$langs->trans("MinPrice").'</td><td colspan="2">';
1077 if ($object->multiprices_base_type[$soc->price_level] == 'TTC') {
1078 print price($object->multiprices_min_ttc[$soc->price_level]).' '.$langs->trans($object->multiprices_base_type[$soc->price_level]);
1079 } else {
1080 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]);
1081 }
1082 print '</td></tr>';
1083
1084 if (getDolGlobalString('PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL')) { // using this option is a bug. kept for backward compatibility
1085 // TVA
1086 print '<tr><td>'.$langs->trans("DefaultTaxRate").'</td><td colspan="2">';
1087
1088 $positiverates = '';
1089 if (price2num($object->multiprices_tva_tx[$soc->price_level])) {
1090 $positiverates .= ($positiverates ? '/' : '').price2num($object->multiprices_tva_tx[$soc->price_level]);
1091 }
1092 if (price2num($object->multiprices_localtax1_type[$soc->price_level])) {
1093 $positiverates .= ($positiverates ? '/' : '').price2num($object->multiprices_localtax1_tx[$soc->price_level]);
1094 }
1095 if (price2num($object->multiprices_localtax2_type[$soc->price_level])) {
1096 $positiverates .= ($positiverates ? '/' : '').price2num($object->multiprices_localtax2_tx[$soc->price_level]);
1097 }
1098 if (empty($positiverates)) {
1099 $positiverates = '0';
1100 }
1101 echo vatrate($positiverates.($object->default_vat_code ? ' ('.$object->default_vat_code.')' : ''), true, $object->tva_npr);
1102 //print vatrate($object->multiprices_tva_tx[$soc->price_level], true);
1103 print '</td></tr>';
1104 } else {
1105 // TVA
1106 print '<tr><td>'.$langs->trans("DefaultTaxRate").'</td><td>';
1107
1108 $positiverates = '';
1109 if (price2num($object->tva_tx)) {
1110 $positiverates .= ($positiverates ? '/' : '').price2num($object->tva_tx);
1111 }
1112 if (price2num($object->localtax1_type)) {
1113 $positiverates .= ($positiverates ? '/' : '').price2num($object->localtax1_tx);
1114 }
1115 if (price2num($object->localtax2_type)) {
1116 $positiverates .= ($positiverates ? '/' : '').price2num($object->localtax2_tx);
1117 }
1118 if (empty($positiverates)) {
1119 $positiverates = '0';
1120 }
1121 echo vatrate($positiverates.($object->default_vat_code ? ' ('.$object->default_vat_code.')' : ''), true, $object->tva_npr);
1122 /*
1123 if ($object->default_vat_code)
1124 {
1125 print vatrate($object->tva_tx, true) . ' ('.$object->default_vat_code.')';
1126 }
1127 else print vatrate($object->tva_tx . ($object->tva_npr ? '*' : ''), true);*/
1128 print '</td></tr>';
1129 }
1130 } else {
1131 if (getDolGlobalString('PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL')) { // using this option is a bug. kept for backward compatibility
1132 // Type
1133 if (isModEnabled("product") && isModEnabled("service")) {
1134 $typeformat = 'select;0:'.$langs->trans("Product").',1:'.$langs->trans("Service");
1135 print '<tr><td class="">';
1136 print (!getDolGlobalString('PRODUCT_DENY_CHANGE_PRODUCT_TYPE')) ? $form->editfieldkey("Type", 'fk_product_type', (string) $object->type, $object, 0, $typeformat) : $langs->trans('Type');
1137 print '</td><td>';
1138 print $form->editfieldval("Type", 'fk_product_type', $object->type, $object, 0, $typeformat);
1139 print '</td></tr>';
1140 }
1141
1142 // We show only vat for level 1
1143 print '<tr><td class="titlefieldcreate">'.$langs->trans("DefaultTaxRate").'</td>';
1144 print '<td colspan="2">'.vatrate($object->multiprices_tva_tx[1], true).'</td>';
1145 print '</tr>';
1146 } else {
1147 // Type
1148 if (isModEnabled("product") && isModEnabled("service")) {
1149 $typeformat = 'select;0:'.$langs->trans("Product").',1:'.$langs->trans("Service");
1150 print '<tr><td class="">';
1151 print (!getDolGlobalString('PRODUCT_DENY_CHANGE_PRODUCT_TYPE')) ? $form->editfieldkey("Type", 'fk_product_type', (string) $object->type, $object, 0, $typeformat) : $langs->trans('Type');
1152 print '</td><td>';
1153 print $form->editfieldval("Type", 'fk_product_type', $object->type, $object, 0, $typeformat);
1154 print '</td></tr>';
1155 }
1156
1157 // TVA
1158 print '<!-- Default VAT Rate -->';
1159 print '<tr><td class="titlefieldcreate">'.$langs->trans("DefaultTaxRate").'</td><td>';
1160
1161 // TODO We show localtax from $object, but this properties may not be correct. Only value $object->default_vat_code is guaranteed.
1162 $positiverates = '';
1163 if (price2num($object->tva_tx)) {
1164 $positiverates .= ($positiverates ? '<span class="opacitymedium">/</span>' : '').price2num($object->tva_tx);
1165 }
1166 if (price2num($object->localtax1_type)) {
1167 $positiverates .= ($positiverates ? '<span class="opacitymedium">/</span>' : '').price2num($object->localtax1_tx);
1168 }
1169 if (price2num($object->localtax2_type)) {
1170 $positiverates .= ($positiverates ? '<span class="opacitymedium">/</span>' : '').price2num($object->localtax2_tx);
1171 }
1172 if (empty($positiverates)) {
1173 $positiverates = '0';
1174 }
1175
1176 print vatrate($positiverates.($object->default_vat_code ? ' ('.$object->default_vat_code.')' : ''), true, $object->tva_npr, 1);
1177 /*
1178 if ($object->default_vat_code)
1179 {
1180 print vatrate($object->tva_tx, true) . ' ('.$object->default_vat_code.')';
1181 }
1182 else print vatrate($object->tva_tx . ($object->tva_npr ? '*' : ''), true);*/
1183 print '</td></tr>';
1184 }
1185 print '</table>';
1186
1187 print '<br>';
1188
1189 print '<table class="liste centpercent">';
1190 print '<tr class="liste_titre"><td>';
1191 print $langs->trans("PriceLevel");
1192 if ($user->admin) {
1193 print ' <a class="editfielda" href="'.$_SERVER["PHP_SELF"].'?action=editlabelsellingprice&token='.newToken().'&pricelevel='.$i.'&id='.$object->id.'">'.img_edit($langs->trans('EditSellingPriceLabel'), 0).'</a>';
1194 }
1195 print '</td>';
1196 print '<td style="text-align: right">'.$langs->trans("SellingPrice").'</td>';
1197 print '<td style="text-align: right">'.$langs->trans("MinPrice").'</td>';
1198 // fetch optionals attributes and labels
1199 $extrafields->fetch_name_optionals_label("product_price");
1200 if ($extrafields->attributes["product_price"] && array_key_exists('label', $extrafields->attributes["product_price"])) {
1201 $extralabels = $extrafields->attributes["product_price"]['label'];
1202 if (!empty($extralabels)) {
1203 foreach ($extralabels as $key => $value) {
1204 // Show field if not hidden
1205 if (!empty($extrafields->attributes["product_price"]['list'][$key]) && $extrafields->attributes["product_price"]['list'][$key] != 3) {
1206 if (!empty($extrafields->attributes["product_price"]['langfile'][$key])) {
1207 $langs->load($extrafields->attributes["product_price"]['langfile'][$key]);
1208 }
1209 if (!empty($extrafields->attributes["product_price"]['help'][$key])) {
1210 $extratitle = $form->textwithpicto($langs->trans($value), $langs->trans($extrafields->attributes["product_price"]['help'][$key]));
1211 } else {
1212 $extratitle = $langs->trans($value);
1213 }
1214 $field = 'ef.' . $key;
1215 print '<td style="text-align: right">'.$extratitle.'</td>';
1216 }
1217 }
1218 }
1219 }
1220 print '</tr>';
1221
1222 $produit_multiprices_limit = getDolGlobalInt('PRODUIT_MULTIPRICES_LIMIT');
1223 for ($i = 1; $i <= $produit_multiprices_limit; $i++) {
1224 print '<tr class="oddeven">';
1225
1226 // Label of price
1227 print '<td>';
1228 $keyforlabel = 'PRODUIT_MULTIPRICES_LABEL'.$i;
1229 if (preg_match('/editlabelsellingprice/', $action)) {
1230 print '<form method="post" action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'">';
1231 print '<input type="hidden" name="token" value="'.newToken().'">';
1232 print '<input type="hidden" name="action" value="setlabelsellingprice">';
1233 print '<input type="hidden" name="pricelevel" value="'.$i.'">';
1234 print $langs->trans("SellingPrice").' '.$i.' - ';
1235 print '<input class="maxwidthonsmartphone" type="text" name="labelsellingprice" value="' . getDolGlobalString($keyforlabel).'">';
1236 print '&nbsp;<input type="submit" class="button smallpaddingimp" value="'.$langs->trans("Modify").'">';
1237 print '</form>';
1238 } else {
1239 print $langs->trans("SellingPrice").' '.$i;
1240 if (getDolGlobalString($keyforlabel)) {
1241 print ' - '.$langs->trans(getDolGlobalString($keyforlabel));
1242 }
1243 }
1244 print '</td>';
1245
1246 if ($object->multiprices_base_type [$i] == 'TTC') {
1247 print '<td class="right"><span class="amount">'.price($object->multiprices_ttc[$i]);
1248 } else {
1249 print '<td class="right"><span class="amount">'.price($object->multiprices[$i]);
1250 }
1251
1252 if ($object->multiprices_base_type[$i]) {
1253 print ' '.$langs->trans($object->multiprices_base_type [$i]).'</span></td>';
1254 } else {
1255 print ' '.$langs->trans($object->price_base_type).'</span></td>';
1256 }
1257
1258 // Prix min
1259 print '<td style="text-align: right">';
1260 if (empty($object->multiprices_base_type[$i])) {
1261 $object->multiprices_base_type[$i] = "HT";
1262 }
1263 if ($object->multiprices_base_type[$i] == 'TTC') {
1264 print price($object->multiprices_min_ttc[$i]).' '.$langs->trans($object->multiprices_base_type[$i]);
1265 } else {
1266 print price($object->multiprices_min[$i]).' '.$langs->trans($object->multiprices_base_type[$i]);
1267 }
1268 print '</td>';
1269 if (!empty($extralabels)) {
1270 $sql1 = "SELECT rowid";
1271 $sql1 .= " FROM ".$object->db->prefix()."product_price";
1272 $sql1 .= " WHERE entity IN (".getEntity('productprice').")";
1273 $sql1 .= " AND price_level=".((int) $i);
1274 $sql1 .= " AND fk_product = ".((int) $object->id);
1275 $sql1 .= " ORDER BY date_price DESC, rowid DESC";
1276 $sql1 .= " LIMIT 1";
1277 $resql1 = $object->db->query($sql1);
1278 if ($resql1) {
1279 $lineid = $object->db->fetch_object($resql1);
1280 }
1281 $sql2 = "SELECT";
1282 $sql2 .= " fk_object";
1283 foreach ($extralabels as $key => $value) {
1284 $sql2 .= ", ".$db->sanitize($key);
1285 }
1286 $sql2 .= " FROM ".MAIN_DB_PREFIX."product_price_extrafields";
1287 $sql2 .= " WHERE fk_object = ".((int) $lineid->rowid);
1288 $resql2 = $db->query($sql2);
1289 if ($resql2) {
1290 if ($db->num_rows($resql2) != 1) {
1291 foreach ($extralabels as $key => $value) {
1292 if (!empty($extrafields->attributes["product_price"]['list'][$key]) && $extrafields->attributes["product_price"]['list'][$key] != 3) {
1293 print '<td align="right"></td>';
1294 }
1295 }
1296 } else {
1297 $obj = $db->fetch_object($resql2);
1298 foreach ($extralabels as $key => $value) {
1299 if (!empty($extrafields->attributes["product_price"]['list'][$key]) && $extrafields->attributes["product_price"]['list'][$key] != 3) {
1300 print '<td align="right">'.$extrafields->showOutputField($key, $obj->{$key}, '', 'product_price')."</td>";
1301 }
1302 }
1303 }
1304 $db->free($resql1);
1305 $db->free($resql2);
1306 }
1307 }
1308 print '</tr>';
1309
1310 // Price by quantity
1311 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
1312 print '<tr><td>'.$langs->trans("PriceByQuantity").' '.$i;
1313 if (getDolGlobalString($keyforlabel)) {
1314 print ' - '.$langs->trans(getDolGlobalString($keyforlabel));
1315 }
1316 print '</td><td colspan="2">';
1317
1318 if ($object->prices_by_qty[$i] == 1) {
1319 print '<table width="50%" class="border" summary="List of quantities">';
1320
1321 print '<tr class="liste_titre">';
1322 print '<td>'.$langs->trans("PriceByQuantityRange").' '.$i.'</td>';
1323 print '<td class="right">'.$langs->trans("HT").'</td>';
1324 print '<td class="right">'.$langs->trans("UnitPrice").'</td>';
1325 print '<td class="right">'.$langs->trans("Discount").'</td>';
1326 print '<td>&nbsp;</td>';
1327 print '</tr>';
1328 $ii = 0;
1329 foreach ($object->prices_by_qty_list[$i] as $ii => $prices) {
1330 if ($action == 'edit_price_by_qty' && $rowid == $prices['rowid'] && ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
1331 print '<form action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="POST">';
1332 print '<input type="hidden" name="token" value="'.newToken().'">';
1333 print '<input type="hidden" name="action" value="update_price_by_qty">';
1334 print '<input type="hidden" name="priceid" value="'.$object->prices_by_qty_id[$i].'">';
1335 print '<input type="hidden" value="'.$prices['rowid'].'" name="rowid">';
1336 print '<tr class="'.($ii % 2 == 0 ? 'pair' : 'impair').'">';
1337 print '<td><input size="5" type="text" value="'.$prices['quantity'].'" name="quantity"></td>';
1338 print '<td class="right" colspan="2"><input size="10" type="text" value="'.price2num($prices['price'], 'MU').'" name="price">&nbsp;'.$object->price_base_type.'</td>';
1339 print '<td class="right nowraponall"><input size="5" type="text" value="'.$prices['remise_percent'].'" name="remise_percent"> %</td>';
1340 print '<td class="center"><input type="submit" value="'.$langs->trans("Modify").'" class="button"></td>';
1341 print '</tr>';
1342 print '</form>';
1343 } else {
1344 print '<tr class="'.($ii % 2 == 0 ? 'pair' : 'impair').'">';
1345 print '<td>'.$prices['quantity'].'</td>';
1346 print '<td class="right">'.price($prices['price']).'</td>';
1347 print '<td class="right">'.price($prices['unitprice']).'</td>';
1348 print '<td class="right">'.price($prices['remise_percent']).' %</td>';
1349 print '<td class="center">';
1350 if (($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
1351 print '<a class="editfielda marginleftonly marginrightonly" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=edit_price_by_qty&token='.newToken().'&rowid='.$prices["rowid"].'">';
1352 print img_edit().'</a>';
1353 print '<a class="marginleftonly marginrightonly" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=delete_price_by_qty&token='.newToken().'&rowid='.$prices["rowid"].'">';
1354 print img_delete().'</a>';
1355 } else {
1356 print '&nbsp;';
1357 }
1358 print '</td>';
1359 print '</tr>';
1360 }
1361 }
1362 if ($action != 'edit_price_by_qty' && ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
1363 print '<form action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="POST">';
1364 print '<input type="hidden" name="token" value="'.newToken().'">';
1365 print '<input type="hidden" name="action" value="update_price_by_qty">';
1366 print '<input type="hidden" name="priceid" value="'.$object->prices_by_qty_id[$i].'">'; // id in product_price
1367 print '<input type="hidden" value="0" name="rowid">'; // id in product_price
1368 print '<tr class="'.($ii % 2 == 0 ? 'pair' : 'impair').'">';
1369 print '<td><input size="5" type="text" value="1" name="quantity"></td>';
1370 print '<td class="right" class="nowrap"><input size="10" type="text" value="0" name="price">&nbsp;'.$object->price_base_type.'</td>';
1371 print '<td class="right">&nbsp;</td>';
1372 print '<td class="right" class="nowraponall"><input size="5" type="text" value="0" name="remise_percent"> %</td>';
1373 print '<td class="center"><input type="submit" value="'.$langs->trans("Add").'" class="button"></td>';
1374 print '</tr>';
1375 print '</form>';
1376 }
1377
1378 print '</table>';
1379 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>';
1380 } else {
1381 print $langs->trans("No");
1382 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>';
1383 }
1384 print '</td></tr>';
1385 }
1386 }
1387 }
1388} else {
1389 // TVA
1390 print '<tr><td class="titlefield">'.$langs->trans("DefaultTaxRate").'</td><td>';
1391
1392 $positiverates = '';
1393 if (price2num($object->tva_tx)) {
1394 $positiverates .= ($positiverates ? '/' : '').price2num($object->tva_tx);
1395 }
1396 if (price2num($object->localtax1_type)) {
1397 $positiverates .= ($positiverates ? '/' : '').price2num($object->localtax1_tx);
1398 }
1399 if (price2num($object->localtax2_type)) {
1400 $positiverates .= ($positiverates ? '/' : '').price2num($object->localtax2_tx);
1401 }
1402 if (empty($positiverates)) {
1403 $positiverates = '0';
1404 }
1405 echo vatrate($positiverates.($object->default_vat_code ? ' ('.$object->default_vat_code.')' : ''), true, $object->tva_npr, 0, 1);
1406 /*
1407 if ($object->default_vat_code)
1408 {
1409 print vatrate($object->tva_tx, true) . ' ('.$object->default_vat_code.')';
1410 }
1411 else print vatrate($object->tva_tx, true, $object->tva_npr, true);*/
1412 print '</td></tr>';
1413
1414 // Price
1415 print '<tr class="field_selling_price"><td>'.$langs->trans("SellingPrice").'</td><td>';
1416 if ($object->price_base_type == 'TTC') {
1417 print price($object->price_ttc).' '.$langs->trans($object->price_base_type);
1418 } else {
1419 print price($object->price).' '.$langs->trans($object->price_base_type);
1420 if (getDolGlobalString('PRODUCT_DISPLAY_VAT_INCL_PRICES') && !empty($object->price_ttc)) {
1421 print '<i class="opacitymedium"> - ' . price($object->price_ttc).' '.$langs->trans('TTC') . '</i>';
1422 }
1423 }
1424
1425 print '</td></tr>';
1426
1427 // Price minimum
1428 print '<tr class="field_min_price"><td>'.$langs->trans("MinPrice").'</td><td>';
1429 if ($object->price_base_type == 'TTC') {
1430 print price($object->price_min_ttc).' '.$langs->trans($object->price_base_type);
1431 } else {
1432 print price($object->price_min).' '.$langs->trans($object->price_base_type);
1433 if (getDolGlobalString('PRODUCT_DISPLAY_VAT_INCL_PRICES') && !empty($object->price_min_ttc)) {
1434 print '<i class="opacitymedium"> - ' . price($object->price_min_ttc).' '.$langs->trans('TTC') . '</i>';
1435 }
1436 }
1437 print '</td></tr>';
1438
1439 // Packaging
1440 if (getDolGlobalString('PRODUCT_USE_CUSTOMER_PACKAGING')) {
1441 print '<tr class="field_price_label"><td>'.$form->textwithpicto($langs->trans("PackagingForThisProduct"), $langs->trans("PackagingForThisProductSellDesc")).'</td><td>';
1442 print $object->packaging;
1443 print '</td></tr>';
1444 }
1445
1446 // Price Label
1447 print '<tr class="field_price_label"><td>'.$langs->trans("PriceLabel").'</td><td>';
1448 print $object->price_label;
1449 print '</td></tr>';
1450
1451 $ii = 0;
1452 // Price by quantity
1453 if (getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) { // TODO Fix the form inside tr instead of td
1454 print '<tr><td>'.$langs->trans("PriceByQuantity");
1455 if ($object->prices_by_qty[0] == 0) {
1456 print '&nbsp; <a href="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'&action=activate_price_by_qty&level=1&token='.newToken().'">('.$langs->trans("Activate").')';
1457 } else {
1458 print '&nbsp; <a href="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'&action=disable_price_by_qty&level=1&token='.newToken().'">('.$langs->trans("DisablePriceByQty").')';
1459 }
1460 print '</td><td>';
1461
1462 if ($object->prices_by_qty[0] == 1) {
1463 print '<table width="50%" class="border" summary="List of quantities">';
1464 print '<tr class="liste_titre">';
1465 //print '<td>' . $langs->trans("PriceByQuantityRange") . '</td>';
1466 print '<td>'.$langs->trans("Quantity").'</td>';
1467 print '<td class="right">'.$langs->trans("Price").'</td>';
1468 print '<td class="right"></td>';
1469 print '<td class="right">'.$langs->trans("UnitPrice").'</td>';
1470 print '<td class="right">'.$langs->trans("Discount").'</td>';
1471 print '<td>&nbsp;</td>';
1472 print '</tr>';
1473 if ($action != 'edit_price_by_qty') {
1474 print '<form action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="POST">'; // FIXME a form into a table is not allowed
1475 print '<input type="hidden" name="token" value="'.newToken().'">';
1476 print '<input type="hidden" name="action" value="update_price_by_qty">';
1477 print '<input type="hidden" name="priceid" value="'.$object->prices_by_qty_id[0].'">'; // id in product_price
1478 print '<input type="hidden" value="0" name="rowid">'; // id in product_price_by_qty
1479
1480 print '<tr class="'.(($ii % 2) == 0 ? 'pair' : 'impair').'">'; // @phpstan-ignore-line
1481 print '<td><input size="5" type="text" value="1" name="quantity"></td>';
1482 print '<td class="right"><input class="width50 right" type="text" value="0" name="price"></td>';
1483 print '<td>';
1484 //print $object->price_base_type;
1485 print '</td>';
1486 print '<td class="right">&nbsp;</td>';
1487 print '<td class="right nowraponall"><input type="text" class="width50 right" value="0" name="remise_percent"> %</td>';
1488 print '<td class="center"><input type="submit" value="'.$langs->trans("Add").'" class="button"></td>';
1489 print '</tr>';
1490
1491 print '</form>';
1492 }
1493 foreach ($object->prices_by_qty_list[0] as $ii => $prices) {
1494 if ($action == 'edit_price_by_qty' && $rowid == $prices['rowid'] && ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
1495 print '<form action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="POST">';
1496 print '<input type="hidden" name="token" value="'.newToken().'">';
1497 print '<input type="hidden" name="action" value="update_price_by_qty">';
1498 print '<input type="hidden" name="priceid" value="'.$object->prices_by_qty_id[0].'">'; // id in product_price
1499 print '<input type="hidden" value="'.$prices['rowid'].'" name="rowid">'; // id in product_price_by_qty
1500 print '<tr class="'.($ii % 2 == 0 ? 'pair' : 'impair').'">';
1501 print '<td><input size="5" type="text" value="'.$prices['quantity'].'" name="quantity"></td>';
1502 print '<td class="right"><input class="width50 right" type="text" value="'.price2num($prices['price'], 'MU').'" name="price"></td>';
1503 print '<td class="right">';
1504 //print $object->price_base_type;
1505 print $prices['price_base_type'];
1506 print '</td>';
1507 print '<td class="right">&nbsp;</td>';
1508 print '<td class="right nowraponall"><input class="width50 right" type="text" value="'.$prices['remise_percent'].'" name="remise_percent"> %</td>';
1509 print '<td class="center"><input type="submit" value="'.$langs->trans("Modify").'" class="button"></td>';
1510 print '</tr>';
1511 print '</form>';
1512 } else {
1513 print '<tr class="'.($ii % 2 == 0 ? 'pair' : 'impair').'">';
1514 print '<td>'.$prices['quantity'].'</td>';
1515 print '<td class="right">'.price($prices['price']).'</td>';
1516 print '<td class="right">';
1517 //print $object->price_base_type;
1518 print $prices['price_base_type'];
1519 print '</td>';
1520 print '<td class="right">'.price($prices['unitprice']).'</td>';
1521 print '<td class="right">'.price($prices['remise_percent']).' %</td>';
1522 print '<td class="center">';
1523 if (($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
1524 print '<a class="editfielda marginleftonly marginrightonly" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=edit_price_by_qty&token='.newToken().'&rowid='.$prices["rowid"].'">';
1525 print img_edit().'</a>';
1526 print '<a class="marginleftonly marginrightonly" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=delete_price_by_qty&token='.newToken().'&rowid='.$prices["rowid"].'">';
1527 print img_delete().'</a>';
1528 } else {
1529 print '&nbsp;';
1530 }
1531 print '</td>';
1532 print '</tr>';
1533 }
1534 }
1535 print '</table>';
1536 } else {
1537 print $langs->trans("No");
1538 }
1539 print '</td></tr>';
1540 }
1541}
1542
1543print "</table>\n";
1544
1545print '</div>';
1546print '<div class="clearboth"></div>';
1547
1548
1549print dol_get_fiche_end();
1550
1551
1552
1553/*
1554 * Action bar
1555 */
1556
1557
1558if (!$action || $action == 'delete' || $action == 'showlog_customer_price' || $action == 'showlog_default_price' || $action == 'add_customer_price'
1559 || $action == 'activate_price_by_qty' || $action == 'disable_price_by_qty') {
1560 print "\n".'<div class="tabsAction">'."\n";
1561
1562
1563 $parameters = array();
1564 $reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been
1565 if (empty($reshook)) {
1566 if ($object->isVariant()) {
1567 if ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer')) {
1568 print '<div class="inline-block divButAction"><a class="butActionRefused classfortooltip" href="#" title="' . dol_escape_htmltag($langs->trans("NoEditVariants")) . '">' . $langs->trans("UpdateDefaultPrice") . '</a></div>';
1569 }
1570 } else {
1571 if (!getDolGlobalString('PRODUIT_MULTIPRICES') && !getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES') && !getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
1572 if ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer')) {
1573 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>';
1574 } else {
1575 print '<div class="inline-block divButAction"><span class="butActionRefused" title="'.dol_escape_htmltag($langs->trans("NotEnoughPermissions")).'">' . $langs->trans("UpdateDefaultPrice") . '</span></div>';
1576 }
1577 }
1578
1579 if (getDolGlobalString('PRODUIT_CUSTOMER_PRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
1580 if ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer')) {
1581 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>';
1582 } else {
1583 print '<div class="inline-block divButAction"><span class="butActionRefused" title="'.dol_escape_htmltag($langs->trans("NotEnoughPermissions")).'">' . $langs->trans("AddCustomerPrice") . '</span></div>';
1584 }
1585 }
1586
1587 if (getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
1588 if ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer')) {
1589 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>';
1590 } else {
1591 print '<div class="inline-block divButAction"><span class="butActionRefused" title="'.dol_escape_htmltag($langs->trans("NotEnoughPermissions")).'">' . $langs->trans("UpdateVAT") . '</span></div>';
1592 }
1593
1594 if ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer')) {
1595 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>';
1596 } else {
1597 print '<div class="inline-block divButAction"><span class="butActionRefused" title="'.dol_escape_htmltag($langs->trans("NotEnoughPermissions")).'">' . $langs->trans("UpdateLevelPrices") . '</span></div>';
1598 }
1599 }
1600 }
1601 }
1602
1603 print "\n</div>\n";
1604}
1605
1606
1607
1608/*
1609 * Edit price area
1610 */
1611
1612if ($action == 'edit_vat' && ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
1613 print load_fiche_titre($langs->trans("UpdateVAT"), '');
1614
1615 print '<form action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="POST">';
1616 print '<input type="hidden" name="token" value="'.newToken().'">';
1617 print '<input type="hidden" name="action" value="update_vat">';
1618 print '<input type="hidden" name="id" value="'.$object->id.'">';
1619
1620 print dol_get_fiche_head();
1621
1622 print '<table class="border centpercent">';
1623
1624 // VAT
1625 print '<tr><td>'.$langs->trans("DefaultTaxRate").'</td><td>';
1626 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);
1627 print '</td></tr>';
1628
1629 print '</table>';
1630
1631 print dol_get_fiche_end();
1632
1633 print $form->buttonsSaveCancel();
1634
1635 print '<br></form><br>';
1636}
1637
1638if (($action == 'edit_price' || $action == 'edit_level_price') && $object->getRights()->creer) {
1639 print '<br>';
1640 print load_fiche_titre($langs->trans("NewPrice"), '');
1641
1642 if (!getDolGlobalString('PRODUIT_MULTIPRICES') && $action != 'edit_level_price') {
1643 print '<!-- Edit price -->'."\n";
1644 print '<form action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="POST">';
1645 print '<input type="hidden" name="token" value="'.newToken().'">';
1646 print '<input type="hidden" name="action" value="update_price">';
1647 print '<input type="hidden" name="id" value="'.$object->id.'">';
1648
1649 print dol_get_fiche_head();
1650
1651 print '<div class="div-table-responsive-no-min">';
1652 print '<table class="border centpercent">';
1653
1654 // VAT
1655 print '<tr><td class="titlefield">'.$langs->trans("DefaultTaxRate").'</td><td>';
1656 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);
1657 print '</td></tr>';
1658
1659 // Price base
1660 print '<tr><td>';
1661 print $langs->trans('PriceBase');
1662 print '</td>';
1663 print '<td>';
1664 print $form->selectPriceBaseType($object->price_base_type, "price_base_type");
1665 print '</td>';
1666 print '</tr>';
1667
1668 // Only show price mode and expression selector if module is enabled
1669 if (isModEnabled('dynamicprices')) {
1670 // Price mode selector
1671 print '<!-- Show price mode of dynamicprices editor -->'."\n";
1672 print '<tr><td>'.$langs->trans("PriceMode").'</td><td>';
1673 print img_picto('', 'dynamicprice', 'class="pictofixedwidth"');
1674 $price_expression = new PriceExpression($db);
1675 $price_expression_list = array(0 => $langs->trans("Numeric").' <span class="opacitymedium">('.$langs->trans("NoDynamicPrice").')</span>'); //Put the numeric mode as first option
1676 foreach ($price_expression->list_price_expression() as $entry) {
1677 $price_expression_list[$entry->id] = $entry->title;
1678 }
1679 $price_expression_preselection = GETPOST('eid') ? GETPOST('eid') : ($object->fk_price_expression ? $object->fk_price_expression : '0');
1680 print $form->selectarray('eid', $price_expression_list, $price_expression_preselection);
1681 print '&nbsp; <a id="expression_editor" class="classlink">'.$langs->trans("PriceExpressionEditor").'</a>';
1682 print '</td></tr>';
1683
1684 // This code hides the numeric price input if is not selected, loads the editor page if editor button is pressed
1685 ?>
1686
1687 <script type="text/javascript">
1688 jQuery(document).ready(function() {
1689 jQuery("#expression_editor").click(function() {
1690 window.location = "<?php echo DOL_URL_ROOT ?>/product/dynamic_price/editor.php?id=<?php echo $id ?>&tab=price&eid=" + $("#eid").val();
1691 });
1692 jQuery("#eid").change(on_change);
1693 on_change();
1694 });
1695 function on_change() {
1696 if ($("#eid").val() == 0) {
1697 jQuery("#price_numeric").show();
1698 } else {
1699 jQuery("#price_numeric").hide();
1700 }
1701 }
1702 </script>
1703 <?php
1704 }
1705
1706 // Price
1707 $product = new Product($db);
1708 $product->fetch($id, $ref, '', '1'); //Ignore the math expression when getting the price
1709 print '<tr id="price_numeric"><td>';
1710 $text = $langs->trans('SellingPrice');
1711 print $form->textwithpicto($text, $langs->trans("PrecisionUnitIsLimitedToXDecimals", getDolGlobalString('MAIN_MAX_DECIMALS_UNIT')), 1, 'help');
1712 print '</td><td>';
1713 if ($object->price_base_type == 'TTC') {
1714 print '<input name="price" size="10" value="'.price($product->price_ttc).'">';
1715 } else {
1716 print '<input name="price" size="10" value="'.price($product->price).'">';
1717 }
1718 print '</td></tr>';
1719
1720 // Price minimum
1721 print '<tr><td>';
1722 $text = $langs->trans('MinPrice');
1723 print $form->textwithpicto($text, $langs->trans("PrecisionUnitIsLimitedToXDecimals", getDolGlobalString('MAIN_MAX_DECIMALS_UNIT')), 1, 'help');
1724 print '</td><td>';
1725 if ($object->price_base_type == 'TTC') {
1726 print '<input name="price_min" size="10" value="'.price($object->price_min_ttc).'">';
1727 } else {
1728 print '<input name="price_min" size="10" value="'.price($object->price_min).'">';
1729 }
1730 if (getDolGlobalString('PRODUCT_MINIMUM_RECOMMENDED_PRICE')) {
1731 print ' &nbsp; '.$langs->trans("MinimumRecommendedPrice", price((float) $maxpricesupplier, 0, '', 1, -1, -1, 'auto')).' '.img_warning().'</td>';
1732 }
1733 print '</td>';
1734 print '</tr>';
1735
1736 // Packaging
1737 if (getDolGlobalString('PRODUCT_USE_CUSTOMER_PACKAGING')) {
1738 print '<tr><td>';
1739 print $form->textwithpicto($langs->trans("PackagingForThisProduct"), $langs->trans("PackagingForThisProductSellDesc"));
1740 print '</td><td>';
1741 $packaging = $object->packaging;
1742 print '<input class="flat" name="packaging" size="5" value="' . price($packaging, 0, '', 1, -1, 2).'">';
1743 print '</td>';
1744 print '</tr>';
1745 }
1746
1747 // Price Label
1748 print '<tr><td>';
1749 print $langs->trans('PriceLabel');
1750 print '</td><td>';
1751 print '<input name="price_label" maxlength="255" class="minwidth300 maxwidth400onsmartphone" value="'.$object->price_label.'">';
1752 print '</td>';
1753 print '</tr>';
1754
1755 $parameters = array();
1756 $reshook = $hookmanager->executeHooks('formObjectOptions', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
1757
1758 print '</table>';
1759 print '</div>';
1760
1761 print dol_get_fiche_end();
1762
1763 print $form->buttonsSaveCancel();
1764
1765 print '</form>';
1766 } elseif ($action == 'edit_level_price' && $object->getRights()->creer) {
1767 print '<!-- Edit price per level -->'."\n"; ?>
1768 <script>
1769
1770 var showHidePriceRules = function () {
1771 var otherPrices = $('div.fiche form table tbody tr:not(:first)');
1772 var minPrice1 = $('div.fiche form input[name="price_min[1]"]');
1773
1774 if (jQuery('input#usePriceRules').prop('checked')) {
1775 otherPrices.hide();
1776 minPrice1.hide();
1777 } else {
1778 otherPrices.show();
1779 minPrice1.show();
1780 }
1781 }
1782
1783 jQuery(document).ready(function () {
1784 showHidePriceRules();
1785
1786 jQuery('input#usePriceRules').click(showHidePriceRules);
1787 });
1788 </script>
1789 <?php
1790
1791 print '<form action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="POST">';
1792 print '<input type="hidden" name="token" value="'.newToken().'">';
1793 print '<input type="hidden" name="action" value="update_level_price">';
1794 print '<input type="hidden" name="id" value="'.$object->id.'">';
1795
1796 //print dol_get_fiche_head(array(), '', '', -1);
1797
1798 if ((getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) && getDolGlobalString('PRODUIT_MULTIPRICES_ALLOW_AUTOCALC_PRICELEVEL')) {
1799 print $langs->trans('UseMultipriceRules').' <input type="checkbox" id="usePriceRules" name="usePriceRules" '.($object->price_autogen ? 'checked' : '').'><br><br>';
1800 }
1801
1802 print '<div class="div-table-responsive-no-min">';
1803 print '<table class="noborder">';
1804 print '<thead><tr class="liste_titre">';
1805
1806 print '<td>'.$langs->trans("PriceLevel").'</td>';
1807
1808 if (getDolGlobalString('PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL')) {
1809 print '<td style="text-align: center">'.$langs->trans("DefaultTaxRate").'</td>';
1810 } else {
1811 print '<td></td>';
1812 }
1813
1814 print '<td class="center">'.$langs->trans("SellingPrice").'</td>';
1815
1816 print '<td class="center">'.$langs->trans("MinPrice").'</td>';
1817
1818 if (getDolGlobalString('PRODUCT_MINIMUM_RECOMMENDED_PRICE')) {
1819 print '<td></td>';
1820 }
1821
1822 // fetch optionals attributes and labels
1823 $extrafields->fetch_name_optionals_label("product_price");
1824 if ($extrafields->attributes["product_price"] && array_key_exists('label', $extrafields->attributes["product_price"])) {
1825 $extralabels = $extrafields->attributes["product_price"]['label'];
1826 if (!empty($extralabels)) {
1827 foreach ($extralabels as $key => $value) {
1828 // Show field if not hidden
1829 if (!empty($extrafields->attributes["product_price"]['list'][$key]) && $extrafields->attributes["product_price"]['list'][$key] != 3) {
1830 if (!empty($extrafields->attributes["product_price"]['langfile'][$key])) {
1831 $langs->load($extrafields->attributes["product_price"]['langfile'][$key]);
1832 }
1833 if (!empty($extrafields->attributes["product_price"]['help'][$key])) {
1834 $extratitle = $form->textwithpicto($langs->trans($value), $langs->trans($extrafields->attributes["product_price"]['help'][$key]));
1835 } else {
1836 $extratitle = $langs->trans($value);
1837 }
1838 print '<td style="text-align: right">'.$extratitle.'</td>';
1839 }
1840 }
1841 }
1842 }
1843 print '</tr></thead>';
1844
1845 print '<tbody>';
1846
1847 $produit_multiprices_limit = getDolGlobalInt('PRODUIT_MULTIPRICES_LIMIT');
1848 for ($i = 1; $i <= $produit_multiprices_limit; $i++) {
1849 print '<tr class="oddeven">';
1850 print '<td>';
1851 $keyforlabel = 'PRODUIT_MULTIPRICES_LABEL'.$i;
1852 $text = $langs->trans('SellingPrice').' '.$i.' - '.getDolGlobalString($keyforlabel);
1853 print $form->textwithpicto($text, $langs->trans("PrecisionUnitIsLimitedToXDecimals", getDolGlobalString('MAIN_MAX_DECIMALS_UNIT')), 1, 'help');
1854 print '</td>';
1855
1856 // VAT
1857 if (!getDolGlobalString('PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL')) {
1858 print '<td>';
1859 print '<input type="hidden" name="tva_tx['.$i.']" value="'.($object->default_vat_code ? $object->tva_tx.' ('.$object->default_vat_code.')' : $object->tva_tx).'">';
1860 print '<input type="hidden" name="tva_npr['.$i.']" value="'.$object->tva_npr.'">';
1861 print '<input type="hidden" name="localtax1_tx['.$i.']" value="'.$object->localtax1_tx.'">';
1862 print '<input type="hidden" name="localtax1_type['.$i.']" value="'.$object->localtax1_type.'">';
1863 print '<input type="hidden" name="localtax2_tx['.$i.']" value="'.$object->localtax2_tx.'">';
1864 print '<input type="hidden" name="localtax2_type['.$i.']" value="'.$object->localtax2_type.'">';
1865 print '</td>';
1866 } else {
1867 // This option is kept for backward compatibility but has no sense
1868 print '<td style="text-align: center">';
1869 print $form->load_tva("tva_tx[".$i.']', $object->multiprices_tva_tx[$i], $mysoc, null, $object->id, 0, $object->type, false, 1);
1870 print '</td>';
1871 }
1872
1873 // Selling price
1874 print '<td style="text-align: center">';
1875 if ($object->multiprices_base_type [$i] == 'TTC') {
1876 print '<input name="price['.$i.']" size="10" value="'.price($object->multiprices_ttc [$i]).'">';
1877 } else {
1878 print '<input name="price['.$i.']" size="10" value="'.price($object->multiprices [$i]).'">';
1879 }
1880 print '&nbsp;'.$form->selectPriceBaseType($object->multiprices_base_type [$i], "multiprices_base_type[".$i."]");
1881 print '</td>';
1882
1883 // Min price
1884 print '<td style="text-align: center">';
1885 if ($object->multiprices_base_type [$i] == 'TTC') {
1886 print '<input name="price_min['.$i.']" size="10" value="'.price($object->multiprices_min_ttc [$i]).'">';
1887 } else {
1888 print '<input name="price_min['.$i.']" size="10" value="'.price($object->multiprices_min [$i]).'">';
1889 }
1890 if (getDolGlobalString('PRODUCT_MINIMUM_RECOMMENDED_PRICE')) {
1891 print '<td class="left">'.$langs->trans("MinimumRecommendedPrice", price((float) $maxpricesupplier, 0, '', 1, -1, -1, 'auto')).' '.img_warning().'</td>';
1892 }
1893 print '</td>';
1894
1895 if (!empty($extralabels)) {
1896 $sql1 = "SELECT rowid";
1897 $sql1 .= " FROM ".$object->db->prefix()."product_price";
1898 $sql1 .= " WHERE entity IN (".getEntity('productprice').")";
1899 $sql1 .= " AND price_level=".((int) $i);
1900 $sql1 .= " AND fk_product = ".((int) $object->id);
1901 $sql1 .= " ORDER BY date_price DESC, rowid DESC";
1902 $sql1 .= " LIMIT 1";
1903 $resql1 = $object->db->query($sql1);
1904 if ($resql1) {
1905 $lineid = $object->db->fetch_object($resql1);
1906 }
1907 if (empty($lineid->rowid)) {
1908 foreach ($extralabels as $key => $value) {
1909 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))) {
1910 if (!empty($extrafields->attributes["product_price"]['langfile'][$key])) {
1911 $langs->load($extrafields->attributes["product_price"]['langfile'][$key]);
1912 }
1913
1914 $extravalue = GETPOSTISSET('options_'.$key) ? $extrafield_values['options_'.$key] : $obj->{$key};
1915 print '<td align="center"><input name="'.$key.'['.$i.']" size="10" value="'.$extravalue.'"></td>';
1916 }
1917 }
1918 } else {
1919 $sql = "SELECT";
1920 $sql .= " fk_object";
1921 foreach ($extralabels as $key => $value) {
1922 $sql .= ", ".$db->sanitize($key);
1923 }
1924 $sql .= " FROM ".MAIN_DB_PREFIX."product_price_extrafields";
1925 $sql .= " WHERE fk_object = ".((int) $lineid->rowid);
1926 $resql = $db->query($sql);
1927 if ($resql) {
1928 $obj = $db->fetch_object($resql);
1929 foreach ($extralabels as $key => $value) {
1930 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))) {
1931 if (!empty($extrafields->attributes["product_price"]['langfile'][$key])) {
1932 $langs->load($extrafields->attributes["product_price"]['langfile'][$key]);
1933 }
1934
1935 $extravalue = (GETPOSTISSET('options_'.$key) ? $extrafield_values['options_'.$key] : $obj->{$key} ?? '');
1936 print '<td align="center"><input name="'.$key.'['.$i.']" size="10" value="'.$extravalue.'"></td>';
1937 }
1938 }
1939 $db->free($resql);
1940 }
1941 }
1942 }
1943 print '</tr>';
1944 }
1945
1946 print '</tbody>';
1947
1948 print '</table>';
1949 print '</div>';
1950
1951 //print dol_get_fiche_end();
1952
1953 print $form->buttonsSaveCancel();
1954
1955 print '</form>';
1956 }
1957}
1958
1959// Add area to show/add/edit a price for a dedicated customer
1960if (getDolGlobalString('PRODUIT_CUSTOMER_PRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
1961 $prodcustprice = new ProductCustomerPrice($db);
1962
1963 $limit = GETPOSTINT('limit') ? GETPOSTINT('limit') : $conf->liste_limit;
1964 $sortfield = GETPOST('sortfield', 'aZ09comma');
1965 $sortorder = GETPOST('sortorder', 'aZ09comma');
1966 $page = (GETPOSTINT("page") ? GETPOSTINT("page") : 0);
1967 if (empty($page) || $page == -1) {
1968 $page = 0;
1969 } // If $page is not defined, or '' or -1
1970 $offset = $limit * $page;
1971 $pageprev = $page - 1;
1972 $pagenext = $page + 1;
1973 if (!$sortorder) {
1974 $sortorder = "ASC,ASC";
1975 }
1976 if (!$sortfield) {
1977 $sortfield = "soc.nom,t.date_begin";
1978 }
1979
1980 // Build filter to display only concerned lines
1981 $filter = array('t.fk_product' => (string) $object->id);
1982
1983 if (!empty($search_soc)) {
1984 $filter['soc.nom'] = (string) $search_soc;
1985 }
1986
1987 if ($action == 'add_customer_price') {
1988 // Form to add a new customer price
1989 $maxpricesupplier = $object->min_recommended_price();
1990
1991 print '<!-- add_customer_price -->';
1992 print load_fiche_titre($langs->trans('AddCustomerPrice'));
1993
1994 print '<form action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="POST">';
1995 print '<input type="hidden" name="token" value="'.newToken().'">';
1996 print '<input type="hidden" name="action" value="add_customer_price_confirm">';
1997 print '<input type="hidden" name="id" value="'.$object->id.'">';
1998
1999 print '<div class="tabBar tabBarWithBottom">';
2000
2001 print '<table class="border centpercent">';
2002 print '<tr>';
2003 print '<td class="fieldrequired">'.$langs->trans('ThirdParty').'</td>';
2004 print '<td>';
2005 $filter = '(s.client:IN:1,2,3)';
2006 print img_picto('', 'company').$form->select_company(GETPOSTINT('socid'), 'socid', $filter, 'SelectThirdParty', 0, 0, array(), 0, 'minwidth300');
2007 print '</td>';
2008 print '</tr>';
2009
2010 // Ref. Customer
2011 print '<tr><td>' . $langs->trans('RefCustomer') . '</td>';
2012 print '<td><input name="ref_customer" size="12"></td></tr>';
2013
2014 // Applied Prices From
2015 $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;
2016 print '<tr><td>'.$langs->trans("AppliedPricesFrom").'</td><td>';
2017 print $form->selectDate(!empty($date_begin) ? $date_begin : dol_now(), "date_begin", 0, 0, 1, "date_begin");
2018 print '</td></tr>';
2019
2020 // Applied Prices To
2021 $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
2022 print '<tr><td>'.$langs->trans("AppliedPricesTo").'</td><td>';
2023 print $form->selectDate($date_end, "date_end", 0, 0, 1, "date_end");
2024 print '</td></tr>';
2025
2026 // VAT
2027 print '<tr><td class="fieldrequired">'.$langs->trans("DefaultTaxRate").'</td><td>';
2028 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);
2029 print '</td></tr>';
2030
2031 // Price base
2032 print '<tr><td class="fieldrequired">';
2033 print $langs->trans('PriceBase');
2034 print '</td>';
2035 print '<td>';
2036 print $form->selectPriceBaseType($object->price_base_type, "price_base_type");
2037 print '</td>';
2038 print '</tr>';
2039
2040 // Price
2041 print '<tr><td class="fieldrequired">';
2042 $text = $langs->trans('SellingPrice');
2043 print $form->textwithpicto($text, $langs->trans("PrecisionUnitIsLimitedToXDecimals", getDolGlobalString('MAIN_MAX_DECIMALS_UNIT')), 1, 'help');
2044 print '</td><td>';
2045 if ($object->price_base_type == 'TTC') {
2046 print '<input name="price" size="10" value="'.price($object->price_ttc).'">';
2047 } else {
2048 print '<input name="price" size="10" value="'.price($object->price).'">';
2049 }
2050 print '</td></tr>';
2051
2052 // Price minimum
2053 print '<tr><td>';
2054 $text = $langs->trans('MinPrice');
2055 print $form->textwithpicto($text, $langs->trans("PrecisionUnitIsLimitedToXDecimals", getDolGlobalString('MAIN_MAX_DECIMALS_UNIT')), 1, 'help');
2056 if ($object->price_base_type == 'TTC') {
2057 print '<td><input name="price_min" size="10" value="'.price($object->price_min_ttc).'">';
2058 } else {
2059 print '<td><input name="price_min" size="10" value="'.price($object->price_min).'">';
2060 }
2061 if (getDolGlobalString('PRODUCT_MINIMUM_RECOMMENDED_PRICE')) {
2062 print '<td class="left">'.$langs->trans("MinimumRecommendedPrice", price($maxpricesupplier, 0, '', 1, -1, -1, 'auto')).' '.img_warning().'</td>';
2063 }
2064 print '</td></tr>';
2065
2066 // Price Label
2067 print '<tr><td>';
2068 print $langs->trans('PriceLabel');
2069 print '</td><td>';
2070 print '<input name="price_label" maxlength="255" class="minwidth300 maxwidth400onsmartphone" value="'.$object->price_label.'">';
2071 print '</td>';
2072 print '</tr>';
2073
2074 // Discount
2075 $discount_percent = price2num(GETPOST("discount_percent"));
2076 print '<tr><td>'.$langs->trans("Discount").'</td><td>';
2077 print '<input name="discount_percent" size="10" value="'.price($discount_percent).'">';
2078 print '</td></tr>';
2079
2080 // Extrafields
2081 $extrafields->fetch_name_optionals_label("product_customer_price");
2082 $extralabels = !empty($extrafields->attributes["product_customer_price"]['label']) ? $extrafields->attributes["product_customer_price"]['label'] : '';
2083 $extrafield_values = $extrafields->getOptionalsFromPost("product_customer_price");
2084 if (!empty($extralabels)) {
2085 if (empty($prodcustprice->id)) {
2086 foreach ($extralabels as $key => $value) {
2087 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))) {
2088 if (!empty($extrafields->attributes["product_customer_price"]['langfile'][$key])) {
2089 $langs->load($extrafields->attributes["product_customer_price"]['langfile'][$key]);
2090 }
2091
2092 print '<tr><td'.($extrafields->attributes["product_customer_price"]['required'][$key] ? ' class="fieldrequired"' : '').'>';
2093 if (!empty($extrafields->attributes["product_customer_price"]['help'][$key])) {
2094 print $form->textwithpicto($langs->trans($value), $langs->trans($extrafields->attributes["product_customer_price"]['help'][$key]));
2095 } else {
2096 print $langs->trans($value);
2097 }
2098 print '</td><td>'.$extrafields->showInputField($key, GETPOSTISSET('options_'.$key) ? $extrafield_values['options_'.$key] : '', '', '', '', '', 0, 'product_customer_price').'</td></tr>';
2099 }
2100 }
2101 }
2102 }
2103
2104 print '</table>';
2105
2106 print '</div>';
2107
2108
2109 print '<div class="center">';
2110
2111 // Update all child soc
2112 print '<div class="marginbottomonly">';
2113 print '<input type="checkbox" name="updatechildprice" id="updatechildprice" value="1"> ';
2114 print '<label for="updatechildprice">'.$langs->trans('ForceUpdateChildPriceSoc').'</label>';
2115 print '</div>';
2116
2117 print $form->buttonsSaveCancel();
2118
2119 print '</form>';
2120 } elseif ($action == 'edit_customer_price') {
2121 // Edit mode
2122 $maxpricesupplier = $object->min_recommended_price();
2123
2124 print '<!-- edit_customer_price -->';
2125 print load_fiche_titre($langs->trans('PriceByCustomer'));
2126
2127 $result = $prodcustprice->fetch(GETPOSTINT('lineid'));
2128 if ($result < 0) {
2129 setEventMessages($prodcustprice->error, $prodcustprice->errors, 'errors');
2130 }
2131
2132 print '<form action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="POST">';
2133 print '<input type="hidden" name="token" value="'.newToken().'">';
2134 print '<input type="hidden" name="action" value="update_customer_price_confirm">';
2135 print '<input type="hidden" name="lineid" value="'.$prodcustprice->id.'">';
2136
2137 print '<table class="liste centpercent">';
2138 print '<tr>';
2139 print '<td class="titlefield fieldrequired">'.$langs->trans('ThirdParty').'</td>';
2140 $staticsoc = new Societe($db);
2141 $staticsoc->fetch($prodcustprice->fk_soc);
2142 print "<td>".$staticsoc->getNomUrl(1)."</td>";
2143 print '</tr>';
2144
2145 // Ref. Customer
2146 print '<tr><td>' . $langs->trans('RefCustomer') . '</td>';
2147 print '<td><input name="ref_customer" size="12" value="' . dol_escape_htmltag($prodcustprice->ref_customer) . '"></td></tr>';
2148
2149 // Applied Prices From
2150 print '<tr><td>'.$langs->trans("AppliedPricesFrom").'</td><td>';
2151 print $form->selectDate($prodcustprice->date_begin, "date_begin", 0, 0, 1, "date_begin");
2152 print '</td></tr>';
2153
2154 // Applied Prices To
2155 print '<tr><td>'.$langs->trans("AppliedPricesTo").'</td><td>';
2156 print $form->selectDate($prodcustprice->date_end, "date_end", 0, 0, 1, "date_end");
2157 print '</td></tr>';
2158
2159 // VAT
2160 print '<tr><td class="fieldrequired">'.$langs->trans("DefaultTaxRate").'</td><td>';
2161 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);
2162 print '</td></tr>';
2163
2164 // Price base
2165 print '<tr><td class="fieldrequired">';
2166 print $langs->trans('PriceBase');
2167 print '</td>';
2168 print '<td>';
2169 print $form->selectPriceBaseType($prodcustprice->price_base_type, "price_base_type");
2170 print '</td>';
2171 print '</tr>';
2172
2173 // Price
2174 print '<tr><td class="fieldrequired">';
2175 $text = $langs->trans('SellingPrice');
2176 print $form->textwithpicto($text, $langs->trans("PrecisionUnitIsLimitedToXDecimals", getDolGlobalString('MAIN_MAX_DECIMALS_UNIT')), 1, 'help');
2177 print '</td><td>';
2178 if ($prodcustprice->price_base_type == 'TTC') {
2179 print '<input name="price" size="10" value="'.price($prodcustprice->price_ttc).'">';
2180 } else {
2181 print '<input name="price" size="10" value="'.price($prodcustprice->price).'">';
2182 }
2183 print '</td></tr>';
2184
2185 // Price minimum
2186 print '<tr><td>';
2187 $text = $langs->trans('MinPrice');
2188 print $form->textwithpicto($text, $langs->trans("PrecisionUnitIsLimitedToXDecimals", getDolGlobalString('MAIN_MAX_DECIMALS_UNIT')), 1, 'help');
2189 print '</td><td>';
2190 if ($prodcustprice->price_base_type == 'TTC') {
2191 print '<input name="price_min" size="10" value="'.price($prodcustprice->price_min_ttc).'">';
2192 } else {
2193 print '<input name="price_min" size="10" value="'.price($prodcustprice->price_min).'">';
2194 }
2195 print '</td>';
2196 if (getDolGlobalString('PRODUCT_MINIMUM_RECOMMENDED_PRICE')) {
2197 print '<td class="left">'.$langs->trans("MinimumRecommendedPrice", price($maxpricesupplier, 0, '', 1, -1, -1, 'auto')).' '.img_warning().'</td>';
2198 }
2199 print '</tr>';
2200
2201 // Price Label
2202 print '<tr><td>';
2203 print $langs->trans('PriceLabel');
2204 print '</td><td>';
2205 print '<input name="price_label" maxlength="255" class="minwidth300 maxwidth400onsmartphone" value="'.$prodcustprice->price_label.'">';
2206 print '</td>';
2207 print '</tr>';
2208
2209 // Discount
2210 print '<tr><td>'.$langs->trans("Discount").'</td><td>';
2211 print '<input name="discount_percent" size="10" value="'.price($prodcustprice->discount_percent).'">';
2212 print '</td></tr>';
2213
2214 // Extrafields
2215 $extrafields->fetch_name_optionals_label("product_customer_price");
2216 $extralabels = !empty($extrafields->attributes["product_customer_price"]['label']) ? $extrafields->attributes["product_customer_price"]['label'] : '';
2217 $extrafield_values = $extrafields->getOptionalsFromPost("product_customer_price");
2218 if (!empty($extralabels)) {
2219 if (empty($object->id)) {
2220 foreach ($extralabels as $key => $value) {
2221 if (!empty($extrafields->attributes["product_customer_price"]['list'][$key]) && ($extrafields->attributes["product_customer_price"]['list'][$key] == 1 || $extrafields->attributes["product_customer_price"]['list'][$key] == 3 || ($action == "edit_price" && $extrafields->attributes["product_customer_price"]['list'][$key] == 4))) {
2222 if (!empty($extrafields->attributes["product_customer_price"]['langfile'][$key])) {
2223 $langs->load($extrafields->attributes["product_customer_price"]['langfile'][$key]);
2224 }
2225
2226 print '<tr><td'.($extrafields->attributes["product_customer_price"]['required'][$key] ? ' class="fieldrequired"' : '').'>';
2227 if (!empty($extrafields->attributes["product_customer_price"]['help'][$key])) {
2228 print $form->textwithpicto($langs->trans($value), $langs->trans($extrafields->attributes["product_customer_price"]['help'][$key]));
2229 } else {
2230 print $langs->trans($value);
2231 }
2232 print '</td><td>'.$extrafields->showInputField($key, GETPOSTISSET('options_'.$key) ? $extrafield_values['options_'.$key] : '', '', '', '', '', 0, 'product_customer_price').'</td></tr>';
2233 }
2234 }
2235 } else {
2236 $sql = "SELECT";
2237 $sql .= " fk_object";
2238 foreach ($extralabels as $key => $value) {
2239 $sql .= ", ".$db->sanitize($key);
2240 }
2241 $sql .= " FROM ".MAIN_DB_PREFIX."product_customer_price_extrafields";
2242 $sql .= " WHERE fk_object = ".((int) $prodcustprice->id);
2243 $resql = $db->query($sql);
2244 if ($resql) {
2245 $obj = $db->fetch_object($resql);
2246 foreach ($extralabels as $key => $value) {
2247 if (!empty($extrafields->attributes["product_customer_price"]['list'][$key]) && ($extrafields->attributes["product_customer_price"]['list'][$key] == 1 || $extrafields->attributes["product_customer_price"]['list'][$key] == 3 || ($action == "edit_price" && $extrafields->attributes["product_customer_price"]['list'][$key] == 4))) {
2248 if (!empty($extrafields->attributes["product_customer_price"]['langfile'][$key])) {
2249 $langs->load($extrafields->attributes["product_customer_price"]['langfile'][$key]);
2250 }
2251
2252 print '<tr><td'.($extrafields->attributes["product_customer_price"]['required'][$key] ? ' class="fieldrequired"' : '').'>';
2253 if (!empty($extrafields->attributes["product_customer_price"]['help'][$key])) {
2254 print $form->textwithpicto($langs->trans($value), $langs->trans($extrafields->attributes["product_customer_price"]['help'][$key]));
2255 } else {
2256 print $langs->trans($value);
2257 }
2258 print '</td><td>'.$extrafields->showInputField($key, GETPOSTISSET('options_'.$key) ? $extrafield_values['options_'.$key] : $obj->{$key}, '', '', '', '', 0, 'product_customer_price');
2259
2260 print '</td></tr>';
2261 }
2262 }
2263 $db->free($resql);
2264 }
2265 }
2266 }
2267
2268 print '</table>';
2269
2270 print '<div class="center">';
2271 print '<div class="marginbottomonly">';
2272 print '<input type="checkbox" name="updatechildprice" id="updatechildprice" value="1"> ';
2273 print '<label for="updatechildprice">'.$langs->trans('ForceUpdateChildPriceSoc').'</label>';
2274 print "</div>";
2275
2276 print $form->buttonsSaveCancel();
2277
2278 print '<br></form>';
2279 } elseif ($action == 'showlog_customer_price') {
2280 // List of all log of prices by customers
2281 print '<!-- list of all log of prices per customer -->'."\n";
2282
2283 $sortfield = 't.datec';
2284 $filter = array('t.fk_product' => (string) $object->id, 't.fk_soc' => (string) GETPOSTINT('socid'));
2285
2286 // Count total nb of records
2287 $nbtotalofrecords = '';
2288 if (!getDolGlobalInt('MAIN_DISABLE_FULL_SCANLIST')) {
2289 $nbtotalofrecords = $prodcustprice->fetchAllLog($sortorder, $sortfield, $conf->liste_limit, $offset, $filter);
2290 }
2291
2292 $result = $prodcustprice->fetchAllLog($sortorder, $sortfield, $conf->liste_limit, $offset, $filter);
2293 if ($result < 0) {
2294 setEventMessages($prodcustprice->error, $prodcustprice->errors, 'errors');
2295 }
2296
2297 $option = '&socid='.GETPOSTINT('socid').'&id='.$object->id;
2298
2299 $staticsoc = new Societe($db);
2300 $staticsoc->fetch(GETPOSTINT('socid'));
2301
2302 $title = $langs->trans('PriceByCustomerLog');
2303 $title .= ' - '.$staticsoc->getNomUrl(1);
2304
2305 $backbutton = '<a class="justalink" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'">'.$langs->trans("Back").'</a>';
2306
2307 // @phan-suppress-next-line PhanPluginSuspiciousParamOrder
2308 print_barre_liste($title, $page, $_SERVER['PHP_SELF'], $option, $sortfield, $sortorder, $backbutton, count($prodcustprice->lines), $nbtotalofrecords, 'title_accountancy.png');
2309
2310 if (count($prodcustprice->lines) > 0) {
2311 print '<form action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="POST">';
2312 print '<input type="hidden" name="token" value="'.newToken().'">';
2313 print '<input type="hidden" name="id" value="'.$object->id.'">';
2314
2315 print '<div class="div-table-responsive-no-min">';
2316 print '<table class="liste centpercent">';
2317
2318 print '<tr class="liste_titre">';
2319 print '<td>'.$langs->trans("ThirdParty").'</td>';
2320 print '<td>'.$langs->trans('RefCustomer').'</td>';
2321 print '<td>'.$langs->trans("AppliedPricesFrom").'</td>';
2322 print '<td>'.$langs->trans("AppliedPricesTo").'</td>';
2323 print '<td class="center">'.$langs->trans("PriceBase").'</td>';
2324 print '<td class="right">'.$langs->trans("DefaultTaxRate").'</td>';
2325 print '<td class="right">'.$langs->trans("HT").'</td>';
2326 print '<td class="right">'.$langs->trans("TTC").'</td>';
2327 if ($mysoc->localtax1_assuj == "1" || $mysoc->localtax2_assuj == "1") {
2328 print '<td class="right">'.$langs->trans("INCT").'</td>';
2329 }
2330 print '<td class="right">'.$langs->trans("MinPrice").' '.$langs->trans("HT").'</td>';
2331 print '<td class="right">'.$langs->trans("MinPrice").' '.$langs->trans("TTC").'</td>';
2332 print '<td class="right">'.$langs->trans("PriceLabel").'</td>';
2333 print '<td class="right">'.$langs->trans("Discount").'</td>';
2334 print '<td class="right">'.$langs->trans("ChangedBy").'</td>';
2335 print '<td>'.$langs->trans("DateCreation").'</td>';
2336 print '</tr>';
2337
2338 foreach ($prodcustprice->lines as $line) {
2339 // Date
2340 $staticsoc = new Societe($db);
2341 $staticsoc->fetch($line->fk_soc);
2342
2343 $tva_tx = $line->default_vat_code ? $line->tva_tx.' ('.$line->default_vat_code.')' : $line->tva_tx;
2344
2345 // Line for default price
2346 if ($line->price_base_type == 'HT') {
2347 $pu = $line->price;
2348 } else {
2349 $pu = $line->price_ttc;
2350 }
2351
2352 // Local tax is not saved into table of product. We use value linked to VAT code.
2353 $localtaxarray = getLocalTaxesFromRate($line->tva_tx.($line->default_vat_code ? ' ('.$line->default_vat_code.')' : ''), 0, $staticsoc, $mysoc);
2354 // Define part of HT, VAT, TTC
2355 $resultarray = calcul_price_total(1, $pu, 0, (float) $line->tva_tx, 1, 1, 0, $line->price_base_type, $line->recuperableonly, $object->type, $mysoc, $localtaxarray);
2356 // Calcul du total ht sans remise
2357 $total_ht = $resultarray[0];
2358 $total_vat = $resultarray[1];
2359 $total_localtax1 = $resultarray[9];
2360 $total_localtax2 = $resultarray[10];
2361 $total_ttc = $resultarray[2];
2362
2363 print '<tr class="oddeven">';
2364
2365 print '<td class="tdoverflowmax125">'.$staticsoc->getNomUrl(1)."</td>";
2366 print '<td>'.$line->ref_customer.'</td>';
2367 print "<td>".dol_print_date($line->date_begin, "day", 'tzuserrel')."</td>";
2368 print "<td>".dol_print_date($line->date_end, "day", 'tzuserrel')."</td>";
2369 print '<td class="center">'.$langs->trans($line->price_base_type)."</td>";
2370 print '<td class="right">';
2371
2372 $positiverates = '';
2373 if (price2num($line->tva_tx)) {
2374 $positiverates .= ($positiverates ? '/' : '').price2num($line->tva_tx);
2375 }
2376 if (price2num($line->localtax1_type)) {
2377 $positiverates .= ($positiverates ? '/' : '').price2num($line->localtax1_tx);
2378 }
2379 if (price2num($line->localtax2_type)) {
2380 $positiverates .= ($positiverates ? '/' : '').price2num($line->localtax2_tx);
2381 }
2382 if (empty($positiverates)) {
2383 $positiverates = '0';
2384 }
2385
2386 echo vatrate($positiverates.($line->default_vat_code ? ' ('.$line->default_vat_code.')' : ''), true, ($line->tva_npr ? $line->tva_npr : $line->recuperableonly));
2387
2388 //. vatrate($tva_tx, true, $line->recuperableonly) .
2389 print "</td>";
2390 print '<td class="right"><span class="amount">'.price($line->price)."</span></td>";
2391
2392 print '<td class="right"><span class="amount">'.price($line->price_ttc)."</span></td>";
2393 if ($mysoc->localtax1_assuj == "1" || $mysoc->localtax2_assuj == "1") {
2394 print '<td class="right">'.price($resultarray[2]).'</td>';
2395 }
2396
2397 print '<td class="right">'.price($line->price_min).'</td>';
2398 print '<td class="right">'.price($line->price_min_ttc).'</td>';
2399 print '<td class="right">'.$line->price_label.'</td>';
2400 print '<td class="right">'.price($line->discount_percent).'</td>';
2401
2402 // User
2403 $userstatic = new User($db);
2404 $userstatic->fetch($line->fk_user);
2405 print '<td class="right">';
2406 print $userstatic->getNomUrl(1, '', 0, 0, 24, 0, 'login');
2407 //print $userstatic->getLoginUrl(1);
2408 print '</td>';
2409 print "<td>".dol_print_date($line->datec, "dayhour", 'tzuserrel')."</td>";
2410 print '</tr>';
2411 }
2412 print "</table>";
2413 print '</div>';
2414 } else {
2415 print $langs->trans('None');
2416 }
2417 } elseif ($action != 'showlog_default_price' && $action != 'edit_price' && $action != 'edit_level_price') {
2418 // List of all prices by customers
2419 print '<!-- list of all prices per customer -->'."\n";
2420
2421 // Count total nb of records
2422 $nbtotalofrecords = '';
2423 if (!getDolGlobalInt('MAIN_DISABLE_FULL_SCANLIST')) {
2424 $nbtotalofrecords = $prodcustprice->fetchAll($sortorder, $sortfield, 0, 0, $filter);
2425 }
2426
2427 $result = $prodcustprice->fetchAll($sortorder, $sortfield, $conf->liste_limit, $offset, $filter);
2428 if ($result < 0) {
2429 setEventMessages($prodcustprice->error, $prodcustprice->errors, 'errors');
2430 }
2431
2432 $option = '&search_soc='.$search_soc.'&id='.$object->id;
2433
2434 // @phan-suppress-next-line PhanPluginSuspiciousParamOrder
2435 print_barre_liste($langs->trans('PriceByCustomer'), $page, $_SERVER ['PHP_SELF'], $option, $sortfield, $sortorder, '', count($prodcustprice->lines), $nbtotalofrecords, 'title_accountancy.png');
2436
2437 print '<form action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="POST">';
2438 print '<input type="hidden" name="token" value="'.newToken().'">';
2439 print '<input type="hidden" name="id" value="'.$object->id.'">';
2440
2441 print '<!-- List of prices per customer -->'."\n";
2442 print '<div class="div-table-responsive-no-min">'."\n";
2443 print '<table class="liste centpercent">'."\n";
2444
2445 if (count($prodcustprice->lines) > 0 || $search_soc) {
2446 $extrafields->fetch_name_optionals_label("product_customer_price");
2447 $custom_price_extralabels = !empty($extrafields->attributes["product_customer_price"]['label']) ? $extrafields->attributes["product_customer_price"]['label'] : '';
2448 if (getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
2449 $colspan = 11;
2450 } else {
2451 $colspan = 12;
2452 }
2453 if ($mysoc->localtax1_assuj == "1" || $mysoc->localtax2_assuj == "1") {
2454 $colspan++;
2455 }
2456 if (!empty($custom_price_extralabels) && is_array($custom_price_extralabels)) {
2457 $colspan += count($custom_price_extralabels);
2458 }
2459
2460 print '<tr class="liste_titre">';
2461 print '<td class="liste_titre"><input type="text" class="flat maxwidth125" name="search_soc" value="'.$search_soc.'"></td>';
2462 print '<td class="liste_titre" colspan="'.$colspan.'">&nbsp;</td>';
2463 // Print the search button
2464 print '<td class="liste_titre maxwidthsearch">';
2465 $searchpicto = $form->showFilterAndCheckAddButtons(0);
2466 print $searchpicto;
2467 print '</td>';
2468 print '</tr>';
2469 }
2470
2471 print '<tr class="liste_titre">';
2472 print '<td>'.$langs->trans("ThirdParty").'</td>';
2473 print '<td>'.$langs->trans('RefCustomer').'</td>';
2474 print '<td>'.$langs->trans("AppliedPricesFrom").'</td>';
2475 print '<td>'.$langs->trans("AppliedPricesTo").'</td>';
2476 print '<td class="center">'.$langs->trans("PriceBase").'</td>';
2477 print '<td class="right">'.$langs->trans("DefaultTaxRate").'</td>';
2478 print '<td class="right">'.$langs->trans("HT").'</td>';
2479 print '<td class="right">'.$langs->trans("TTC").'</td>';
2480 if ($mysoc->localtax1_assuj == "1" || $mysoc->localtax2_assuj == "1") {
2481 print '<td class="right">'.$langs->trans("INCT").'</td>';
2482 }
2483 print '<td class="right">'.$langs->trans("MinPrice").' '.$langs->trans("HT").'</td>';
2484 print '<td class="right">'.$langs->trans("MinPrice").' '.$langs->trans("TTC").'</td>';
2485 print '<td class="right">'.$langs->trans("PriceLabel").'</td>';
2486 print '<td class="right">'.$langs->trans("Discount").'</td>';
2487 // fetch optionals attributes and labels
2488 $extrafields->fetch_name_optionals_label("product_customer_price");
2489 if ($extrafields->attributes["product_customer_price"] && array_key_exists('label', $extrafields->attributes["product_customer_price"])) {
2490 $extralabels = $extrafields->attributes["product_customer_price"]['label'];
2491 if (!empty($extralabels)) {
2492 foreach ($extralabels as $key => $value) {
2493 // Show field if not hidden
2494 if (!empty($extrafields->attributes["product_customer_price"]['list'][$key]) && $extrafields->attributes["product_customer_price"]['list'][$key] != 3) {
2495 if (!empty($extrafields->attributes["product_customer_price"]['langfile'][$key])) {
2496 $langs->load($extrafields->attributes["product_customer_price"]['langfile'][$key]);
2497 }
2498 if (!empty($extrafields->attributes["product_customer_price"]['help'][$key])) {
2499 $extratitle = $form->textwithpicto($langs->trans($value), $langs->trans($extrafields->attributes["product_customer_price"]['help'][$key]));
2500 } else {
2501 $extratitle = $langs->trans($value);
2502 }
2503 print '<td style="text-align: right">'.$extratitle.'</td>';
2504 }
2505 }
2506 }
2507 }
2508 print '<td>'.$langs->trans("ChangedBy").'</td>';
2509 print '<td></td>';
2510 print '</tr>';
2511
2512 // Line for default price
2513 if ($object->price_base_type == 'HT') {
2514 $pu = $object->price;
2515 } else {
2516 $pu = $object->price_ttc;
2517 }
2518
2519 // Local tax was not saved into table llx_product on old versions. So we will use the value linked to the VAT code.
2520 $localtaxarray = getLocalTaxesFromRate($object->tva_tx.($object->default_vat_code ? ' ('.$object->default_vat_code.')' : ''), 0, $mysoc, $mysoc);
2521 // Define part of HT, VAT, TTC
2522 $resultarray = calcul_price_total(1, $pu, 0, $object->tva_tx, 1, 1, 0, $object->price_base_type, 0, $object->type, $mysoc, $localtaxarray);
2523 // Calcul du total ht sans remise
2524 $total_ht = $resultarray[0];
2525 $total_vat = $resultarray[1];
2526 $total_localtax1 = $resultarray[9];
2527 $total_localtax2 = $resultarray[10];
2528 $total_ttc = $resultarray[2];
2529
2530 if (!getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
2531 print '<!-- PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES -->'."\n";
2532 print '<tr class="oddeven">';
2533 print '<td colspan="4">' . $langs->trans('Default') . '</td>';
2534
2535 print '<td class="center">'.$langs->trans($object->price_base_type)."</td>";
2536
2537 // VAT Rate
2538 print '<td class="right">';
2539
2540 $positiverates = '';
2541 if (price2num($object->tva_tx)) {
2542 $positiverates .= ($positiverates ? '/' : '').price2num($object->tva_tx);
2543 }
2544 if (price2num($object->localtax1_type)) {
2545 $positiverates .= ($positiverates ? '/' : '').price2num($object->localtax1_tx);
2546 }
2547 if (price2num($object->localtax2_type)) {
2548 $positiverates .= ($positiverates ? '/' : '').price2num($object->localtax2_tx);
2549 }
2550 if (empty($positiverates)) {
2551 $positiverates = '0';
2552 }
2553 echo vatrate($positiverates.($object->default_vat_code ? ' ('.$object->default_vat_code.')' : ''), true, $object->tva_npr);
2554
2555 //print vatrate($object->tva_tx, true, $object->tva_npr);
2556 //print $object->default_vat_code?' ('.$object->default_vat_code.')':'';
2557 print "</td>";
2558
2559 print '<td class="right"><span class="amount">'.price($object->price)."</span></td>";
2560
2561 print '<td class="right"><span class="amount">'.price($object->price_ttc)."</span></td>";
2562 if ($mysoc->localtax1_assuj == "1" || $mysoc->localtax2_assuj == "1") {
2563 //print '<td class="right">' . price($object->price_ttc) . "</td>";
2564 print '<td class="right"><span class="amount">'.price($resultarray[2]).'</span></td>';
2565 }
2566
2567 print '<td class="right">'.price($object->price_min).'</td>';
2568 print '<td class="right">'.price($object->price_min_ttc).'</td>';
2569 print '<td class="right">'.$object->price_label.'</td>';
2570 print '<td class="right"></td>';
2571 print '<td class="right"></td>';
2572 if (!empty($extralabels)) {
2573 foreach ($extralabels as $key) {
2574 // Show field if not hidden
2575 if (!empty($extrafields->attributes["product_customer_price"]['list'][$key]) && $extrafields->attributes["product_customer_price"]['list'][$key] != 3) {
2576 print '<td class="right"></td>';
2577 }
2578 }
2579 }
2580 if ($user->hasRight('produit', 'supprimer') || $user->hasRight('service', 'supprimer')) {
2581 print '<td class="nowraponall">';
2582 print '<a class="marginleftonly marginrightonly" href="'.$_SERVER["PHP_SELF"].'?action=showlog_default_price&token='.newToken().'&id='.$object->id.'">';
2583 print img_info($langs->trans('PriceByCustomerLog'));
2584 print '</a>';
2585 print ' ';
2586 print '<a class="marginleftonly marginrightonly editfielda" href="'.$_SERVER["PHP_SELF"].'?action=edit_price&token='.newToken().'&id='.$object->id.'">';
2587 print img_edit('default', 0, 'style="vertical-align: middle;"');
2588 print '</a>';
2589 print '</td>';
2590 }
2591 print "</tr>\n";
2592 }
2593
2594 if (count($prodcustprice->lines) > 0) {
2595 foreach ($prodcustprice->lines as $line) {
2596 // Date
2597 $staticsoc = new Societe($db);
2598 $staticsoc->fetch($line->fk_soc);
2599
2600 $tva_tx = $line->default_vat_code ? $line->tva_tx.' ('.$line->default_vat_code.')' : $line->tva_tx;
2601
2602 // Line for default price
2603 if ($line->price_base_type == 'HT') {
2604 $pu = $line->price;
2605 } else {
2606 $pu = $line->price_ttc;
2607 }
2608
2609 // Local tax is not saved into table of product. We use value linked to VAT code.
2610 $localtaxarray = getLocalTaxesFromRate($line->tva_tx.($line->default_vat_code ? ' ('.$line->default_vat_code.')' : ''), 0, $staticsoc, $mysoc);
2611 // Define part of HT, VAT, TTC
2612 $resultarray = calcul_price_total(1, $pu, 0, (float) $line->tva_tx, 1, 1, 0, $line->price_base_type, $line->recuperableonly, $object->type, $mysoc, $localtaxarray);
2613 // Calcul du total ht sans remise
2614 $total_ht = $resultarray[0];
2615 $total_vat = $resultarray[1];
2616 $total_localtax1 = $resultarray[9];
2617 $total_localtax2 = $resultarray[10];
2618 $total_ttc = $resultarray[2];
2619
2620 print '<tr class="oddeven">';
2621
2622 print '<td class="tdoverflowmax125">'.$staticsoc->getNomUrl(1)."</td>";
2623 print '<td>'.dol_escape_htmltag($line->ref_customer).'</td>';
2624 print "<td>".dol_print_date($line->date_begin, "day", 'tzuserrel')."</td>";
2625 print "<td>".dol_print_date($line->date_end, "day", 'tzuserrel')."</td>";
2626 print '<td class="center">'.$langs->trans($line->price_base_type)."</td>";
2627 // VAT Rate
2628 print '<td class="right">';
2629
2630 $positiverates = '';
2631 if (price2num($line->tva_tx)) {
2632 $positiverates .= ($positiverates ? '/' : '').price2num($line->tva_tx);
2633 }
2634 if (price2num($line->localtax1_type)) {
2635 $positiverates .= ($positiverates ? '/' : '').price2num($line->localtax1_tx);
2636 }
2637 if (price2num($line->localtax2_type)) {
2638 $positiverates .= ($positiverates ? '/' : '').price2num($line->localtax2_tx);
2639 }
2640 if (empty($positiverates)) {
2641 $positiverates = '0';
2642 }
2643
2644 echo vatrate($positiverates.($line->default_vat_code ? ' ('.$line->default_vat_code.')' : ''), true, (!empty($line->tva_npr) ? $line->tva_npr : $line->recuperableonly));
2645
2646 print "</td>";
2647
2648 print '<td class="right"><span class="amount">'.price($line->price)."</span></td>";
2649
2650 print '<td class="right"><span class="amount">'.price($line->price_ttc)."</span></td>";
2651 if ($mysoc->localtax1_assuj == "1" || $mysoc->localtax2_assuj == "1") {
2652 //print '<td class="right">' . price($line->price_ttc) . "</td>";
2653 print '<td class="right"><span class="amount">'.price($resultarray[2]).'</span></td>';
2654 }
2655
2656 print '<td class="right">'.price($line->price_min).'</td>';
2657 print '<td class="right">'.price($line->price_min_ttc).'</td>';
2658 print '<td class="right">'.$line->price_label.'</td>';
2659 print '<td class="right">'.price($line->discount_percent).'</td>';
2660
2661 // Extrafields
2662 $extrafields->fetch_name_optionals_label("product_customer_price");
2663 $extralabels = $extrafields->attributes["product_customer_price"]['label'] ?? array();
2664 if (!empty($extralabels)) {
2665 $sql = "SELECT";
2666 $sql .= " fk_object";
2667 foreach ($extralabels as $key => $value) {
2668 $sql .= ", ".$db->sanitize($key);
2669 }
2670 $sql .= " FROM ".MAIN_DB_PREFIX."product_customer_price_extrafields";
2671 $sql .= " WHERE fk_object = ".((int) $line->id);
2672 $resql = $db->query($sql);
2673 if ($resql) {
2674 if ($db->num_rows($resql) != 1) {
2675 foreach ($extralabels as $key => $value) {
2676 if (!empty($extrafields->attributes["product_customer_price"]['list'][$key]) && $extrafields->attributes["product_customer_price"]['list'][$key] != 3) {
2677 print "<td></td>";
2678 }
2679 }
2680 } else {
2681 $obj = $db->fetch_object($resql);
2682 foreach ($extralabels as $key => $value) {
2683 if (!empty($extrafields->attributes["product_customer_price"]['list'][$key]) && $extrafields->attributes["product_customer_price"]['list'][$key] != 3) {
2684 print '<td align="right">'.$extrafields->showOutputField($key, $obj->{$key}, '', 'product_customer_price')."</td>";
2685 }
2686 }
2687 }
2688 $db->free($resql);
2689 }
2690 }
2691
2692 // User
2693 $userstatic = new User($db);
2694 $userstatic->fetch($line->fk_user);
2695 // @TODO Add a cache on $users object
2696 print '<td>';
2697 print $userstatic->getNomUrl(-1, '', 0, 0, 24, 0, 'login');
2698 print '</td>';
2699
2700 // Todo Edit or delete button
2701 // Action
2702 if ($user->hasRight('produit', 'supprimer') || $user->hasRight('service', 'supprimer')) {
2703 print '<td class="right nowraponall">';
2704 print '<a href="'.$_SERVER["PHP_SELF"].'?action=showlog_customer_price&token='.newToken().'&id='.$object->id.'&socid='.$line->fk_soc.'">';
2705 print img_info($langs->trans('PriceByCustomerLog'));
2706 print '</a>';
2707 print ' ';
2708 print '<a class="marginleftonly editfielda" href="'.$_SERVER["PHP_SELF"].'?action=edit_customer_price&token='.newToken().'&id='.$object->id.'&lineid='.$line->id.'">';
2709 print img_edit('default', 0, 'style="vertical-align: middle;"');
2710 print '</a>';
2711 print ' ';
2712 print '<a class="marginleftonly" href="'.$_SERVER["PHP_SELF"].'?action=delete_customer_price&token='.newToken().'&id='.$object->id.'&lineid='.$line->id.'">';
2713 print img_delete('default', 'style="vertical-align: middle;"');
2714 print '</a>';
2715 print '</td>';
2716 }
2717
2718 print "</tr>\n";
2719 }
2720 }
2721
2722 print "</table>";
2723 print '</div>';
2724
2725 print "</form>";
2726 }
2727}
2728
2729// List of price changes - log historic (ordered by descending date)
2730
2731if ((!getDolGlobalString('PRODUIT_CUSTOMER_PRICES') || $action == 'showlog_default_price') && !in_array($action, array('edit_price', 'edit_level_price', 'edit_vat'))) {
2732 $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,";
2733 $sql .= " p.price_level, p.price_min, p.price_min_ttc,p.price_by_qty,";
2734 $sql .= " p.date_price as dp, p.fk_price_expression, u.rowid as user_id, u.login";
2735 $sql .= " ,p.price_label";
2736 $sql .= " FROM ".MAIN_DB_PREFIX."product_price as p,";
2737 $sql .= " ".MAIN_DB_PREFIX."user as u";
2738 $sql .= " WHERE fk_product = ".((int) $object->id);
2739 $sql .= " AND p.entity IN (".getEntity('productprice').")";
2740 $sql .= " AND p.fk_user_author = u.rowid";
2741 if (!empty($socid) && (getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) && $soc !== null) {
2742 $sql .= " AND p.price_level = ".((int) $soc->price_level);
2743 }
2744 $sql .= " ORDER BY p.date_price DESC, p.rowid DESC, p.price_level ASC";
2745 // $sql .= $db->plimit();
2746 //print $sql;
2747
2748 $result = $db->query($sql);
2749 if ($result) {
2750 print '<div class="divlogofpreviouscustomerprice">';
2751
2752 $num = $db->num_rows($result);
2753
2754 if (!$num) {
2755 $db->free($result);
2756
2757 // Il doit au moins y avoir la ligne de prix initial.
2758 // On l'ajoute donc pour remettre a niveau (pb vieilles versions)
2759 // We emulate the change of the price from interface with the same value than the one into table llx_product
2760 if (getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
2761 $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);
2762 } else {
2763 $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));
2764 }
2765
2766 if ($ret < 0) {
2767 dol_print_error($db, $object->error, $object->errors);
2768 } else {
2769 $result = $db->query($sql);
2770 $num = $db->num_rows($result);
2771 }
2772 }
2773
2774 if ($num > 0) {
2775 // Default prices or
2776 // Log of previous customer prices
2777 $backbutton = '<a class="justalink" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'">'.$langs->trans("Back").'</a>';
2778
2779 if (getDolGlobalString('PRODUIT_CUSTOMER_PRICES')) {
2780 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition, PhanPluginSuspiciousParamOrder
2781 print_barre_liste($langs->trans("DefaultPriceLog"), 0, $_SERVER["PHP_SELF"], '', '', '', $backbutton, 0, $num, 'title_accountancy.png');
2782 } else {
2783 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition, PhanPluginSuspiciousParamOrder
2784 print_barre_liste($langs->trans("PriceByCustomerLog"), 0, $_SERVER["PHP_SELF"], '', '', '', '', 0, $num, 'title_accountancy.png');
2785 }
2786
2787 print '<!-- List of log prices -->'."\n";
2788 print '<div class="div-table-responsive">'."\n";
2789 print '<table class="liste centpercent noborder">'."\n";
2790
2791 print '<tr class="liste_titre">';
2792 print '<th>'.$langs->trans("AppliedPricesFrom").'</tg>';
2793
2794 if (getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
2795 print '<th class="center">'.$langs->trans("PriceLevel").'</th>';
2796 }
2797 if (getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) {
2798 print '<th class="center">'.$langs->trans("Type").'</th>';
2799 }
2800
2801 print '<th class="center">'.$langs->trans("PriceBase").'</th>';
2802 if (!getDolGlobalString('PRODUIT_MULTIPRICES') && !getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) {
2803 print '<th class="right">'.$langs->trans("DefaultTaxRate").'</th>';
2804 }
2805 print '<th class="right">'.$langs->trans("HT").'</th>';
2806 print '<th class="right">'.$langs->trans("TTC").'</th>';
2807 if ($mysoc->localtax1_assuj == "1" || $mysoc->localtax2_assuj == "1") {
2808 print '<th class="right">'.$langs->trans("INCT").'</th>';
2809 }
2810 if (isModEnabled('dynamicprices')) {
2811 print '<th class="right">'.$langs->trans("PriceExpressionSelected").'</th>';
2812 }
2813 print '<th class="right">'.$langs->trans("MinPrice").' '.$langs->trans("HT").'</th>';
2814 print '<th class="right">'.$langs->trans("MinPrice").' '.$langs->trans("TTC").'</th>';
2815 print '<th class="right">'.$langs->trans("Label").'</th>';
2816 print '<th>'.$langs->trans("ChangedBy").'</th>';
2817 if ($user->hasRight('produit', 'supprimer')) {
2818 print '<th class="right">&nbsp;</th>';
2819 }
2820 print '</tr>';
2821
2822 $notfirstlineforlevel = array();
2823
2824 $i = 0;
2825 while ($i < $num) {
2826 $objp = $db->fetch_object($result);
2827
2828 print '<tr class="oddeven">';
2829 // Date
2830 print "<td>".dol_print_date($db->jdate($objp->dp), "dayhour", 'tzuserrel')."</td>";
2831
2832 // Price level
2833 if (getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
2834 print '<td class="center">'.$objp->price_level."</td>";
2835 }
2836 // Price by quantity
2837 if (getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) {
2838 $type = ($objp->price_by_qty == 1) ? 'PriceByQuantity' : 'Standard';
2839 print '<td class="center">'.$langs->trans($type)."</td>";
2840 }
2841
2842 print '<td class="center">';
2843 if (empty($objp->price_by_qty)) {
2844 print $langs->trans($objp->price_base_type);
2845 }
2846 print "</td>";
2847
2848 if (!getDolGlobalString('PRODUIT_MULTIPRICES') && !getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) {
2849 print '<td class="right">';
2850
2851 if (empty($objp->price_by_qty)) {
2852 $positiverates = '';
2853 if (price2num($objp->tva_tx)) {
2854 $positiverates .= ($positiverates ? '/' : '').price2num($objp->tva_tx);
2855 }
2856 if (price2num($objp->localtax1_type)) {
2857 $positiverates .= ($positiverates ? '/' : '').price2num($objp->localtax1_tx);
2858 }
2859 if (price2num($objp->localtax2_type)) {
2860 $positiverates .= ($positiverates ? '/' : '').price2num($objp->localtax2_tx);
2861 }
2862 if (empty($positiverates)) {
2863 $positiverates = '0';
2864 }
2865 echo vatrate($positiverates.($objp->default_vat_code ? ' ('.$objp->default_vat_code.')' : ''), true, !empty($objp->tva_npr) ? $objp->tva_npr : 0);
2866 /*
2867 if ($objp->default_vat_code)
2868 {
2869 print vatrate($objp->tva_tx, true) . ' ('.$objp->default_vat_code.')';
2870 }
2871 else print vatrate($objp->tva_tx, true, $objp->recuperableonly);*/
2872 }
2873
2874 print "</td>";
2875 }
2876
2877 // Line for default price
2878 if ($objp->price_base_type == 'HT') {
2879 $pu = $objp->price;
2880 } else {
2881 $pu = $objp->price_ttc;
2882 }
2883
2884 // Local tax was not saved into table llx_product on old version. So we will use value linked to VAT code.
2885 $localtaxarray = getLocalTaxesFromRate($objp->tva_tx.($object->default_vat_code ? ' ('.$object->default_vat_code.')' : ''), 0, $mysoc, $mysoc);
2886 // Define part of HT, VAT, TTC
2887 $resultarray = calcul_price_total(1, $pu, 0, $objp->tva_tx, 1, 1, 0, $objp->price_base_type, $objp->recuperableonly, $object->type, $mysoc, $localtaxarray);
2888 // Calcul du total ht sans remise
2889 $total_ht = $resultarray[0];
2890 $total_vat = $resultarray[1];
2891 $total_localtax1 = $resultarray[9];
2892 $total_localtax2 = $resultarray[10];
2893 $total_ttc = $resultarray[2];
2894
2895 // Price
2896 if (!empty($objp->fk_price_expression) && !empty($conf->dynamicprices->enabled)) {
2897 $price_expression = new PriceExpression($db);
2898 $res = $price_expression->fetch($objp->fk_price_expression);
2899 $title = $price_expression->title;
2900 print '<td class="right"></td>';
2901 print '<td class="right"></td>';
2902 if ($mysoc->localtax1_assuj == "1" || $mysoc->localtax2_assuj == "1") {
2903 print '<td class="right"></td>';
2904 }
2905 print '<td class="right">'.$title."</td>";
2906 } else {
2907 // Price HT
2908 print '<td class="right">';
2909 if (empty($objp->price_by_qty)) {
2910 print '<span class="amount">'.price($objp->price).'</span>';
2911 }
2912 print "</td>";
2913 // Price TTC
2914 print '<td class="right">';
2915 if (empty($objp->price_by_qty)) {
2916 $price_ttc = $objp->price_ttc;
2917 print '<span class="amount">'.price($price_ttc).'<span>';
2918 }
2919 print "</td>";
2920 if ($mysoc->localtax1_assuj == "1" || $mysoc->localtax2_assuj == "1") {
2921 print '<td class="right">';
2922 print $resultarray[2];
2923 print '</td>';
2924 }
2925 if (isModEnabled('dynamicprices')) { // Only if module is enabled
2926 print '<td class="right"></td>';
2927 }
2928 }
2929
2930 // Price min
2931 print '<td class="right">';
2932 if (empty($objp->price_by_qty)) {
2933 print price($objp->price_min);
2934 }
2935 print '</td>';
2936
2937 // Price min inc tax
2938 print '<td class="right">';
2939 if (empty($objp->price_by_qty)) {
2940 $price_min_ttc = $objp->price_min_ttc;
2941 print price($price_min_ttc);
2942 }
2943 print '</td>';
2944
2945 // Price Label
2946 print '<td class="right">';
2947 print $objp->price_label;
2948 print '</td>';
2949
2950 // User
2951 print '<td>';
2952 if ($objp->user_id > 0) {
2953 $userstatic = new User($db);
2954 $userstatic->fetch($objp->user_id);
2955 print $userstatic->getNomUrl(-1, '', 0, 0, 24, 0, 'login');
2956 }
2957 print '</td>';
2958
2959
2960 // Action
2961 if ($user->hasRight('produit', 'supprimer')) {
2962 $candelete = 0;
2963 if (getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
2964 if (empty($notfirstlineforlevel[$objp->price_level])) {
2965 $notfirstlineforlevel[$objp->price_level] = 1;
2966 } else {
2967 $candelete = 1;
2968 }
2969 } elseif ($i > 0) {
2970 $candelete = 1;
2971 }
2972
2973 print '<td class="right">';
2974 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
2975 print '<a href="'.$_SERVER["PHP_SELF"].'?action=delete&token='.newToken().'&id='.$object->id.'&lineid='.$objp->rowid.'">';
2976 print img_delete();
2977 print '</a>';
2978 } else {
2979 print '&nbsp;'; // Can not delete last price (it's current price)
2980 }
2981 print '</td>';
2982 }
2983
2984 print "</tr>\n";
2985 $i++;
2986 }
2987
2988 $db->free($result);
2989 print "</table>";
2990 print '</div>';
2991 print "<br>";
2992 }
2993
2994 print '</div>';
2995 } else {
2996 dol_print_error($db);
2997 }
2998}
2999
3000// End of page
3001llxFooter();
3002$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.