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