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