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