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