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