dolibarr 21.0.0-beta
product_tools.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2012 Regis Houssin <regis.houssin@inodbox.com>
3 * Copyright (C) 2013-2015 Laurent Destailleur <eldy@users.sourceforge.net>
4 * Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
26// TODO We must add a confirmation on button because this will make a mass change
27// FIXME Should also change table product_price for price levels
28
29// Load Dolibarr environment
30require '../../main.inc.php';
31require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
32require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
33require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
34require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
35
45// Load translation files required by the page
46$langs->loadLangs(array('admin', 'products'));
47
48// Security check
49if (!$user->admin) {
51}
52
53$action = GETPOST('action', 'aZ09');
54$oldvatrate = GETPOST('oldvatrate', 'alpha');
55$newvatrate = GETPOST('newvatrate', 'alpha');
56//$price_base_type=GETPOST('price_base_type');
57
58
59
60/*
61 * Actions
62 */
63
64if ($action == 'convert') {
65 $error = 0;
66
67 if ($oldvatrate == $newvatrate) {
68 $langs->load("errors");
69 setEventMessages($langs->trans("ErrorNewValueCantMatchOldValue"), null, 'errors');
70 $error++;
71 }
72
73 if (!$error) {
74 $country_id = $mysoc->country_id; // TODO Allow to choose country into form
75
76 $nbrecordsmodified = 0;
77
78 if (!getDolGlobalInt('VATUPDATE_NO_TRANSACTION')) {
79 $db->begin();
80 }
81
82 // Clean vat code old
83 $vat_src_code_old = '';
84 if (preg_match('/\‍((.*)\‍)/', $oldvatrate, $reg)) {
85 $vat_src_code_old = $reg[1];
86 $oldvatrateclean = preg_replace('/\s*\‍(.*\‍)/', '', $oldvatrate); // Remove code into vatrate.
87 } else {
88 $oldvatrateclean = $oldvatrate;
89 }
90
91 // Clean vat code new
92 $vat_src_code_new = '';
93 if (preg_match('/\‍((.*)\‍)/', $newvatrate, $reg)) {
94 $vat_src_code_new = $reg[1];
95 $newvatrateclean = preg_replace('/\s*\‍(.*\‍)/', '', $newvatrate); // Remove code into vatrate.
96 } else {
97 $newvatrateclean = $newvatrate;
98 }
99
100 // If country to edit is my country, so we change customer prices
101 if ($country_id == $mysoc->country_id) {
102 $sql = 'SELECT rowid';
103 $sql .= ' FROM '.MAIN_DB_PREFIX.'product';
104 $sql .= ' WHERE entity IN ('.getEntity('product').')';
105 $sql .= " AND tva_tx = '".$db->escape($oldvatrateclean)."'";
106 if ($vat_src_code_old) {
107 $sql .= " AND default_vat_code = '".$db->escape($vat_src_code_old)."'";
108 } else {
109 $sql .= " AND default_vat_code = IS NULL";
110 }
111
112 $resql = $db->query($sql);
113 if ($resql) {
114 $num = $db->num_rows($resql);
115
116 $i = 0;
117 while ($i < $num) {
118 $obj = $db->fetch_object($resql);
119
120 $objectstatic = new Product($db); // Object init must be into loop to avoid to get value of previous step
121 $ret = $objectstatic->fetch($obj->rowid);
122 if ($ret > 0) {
123 $ret = 0;
124 $retm = 0;
125 $updatelevel1 = false;
126
127 // Update multiprice
128 $listofmulti = array_reverse($objectstatic->multiprices, true); // To finish with level 1
129 foreach ($listofmulti as $level => $multiprices) {
130 $price_base_type = $objectstatic->multiprices_base_type[$level]; // Get price_base_type of product/service to keep the same for update
131 if (empty($price_base_type)) {
132 continue; // Discard not defined price levels
133 }
134
135 if ($price_base_type == 'TTC') {
136 $newprice = price2num($objectstatic->multiprices_ttc[$level], 'MU'); // Second param must be MU (we want a unit price so 'MU'. If unit price was on 4 decimal, we must keep 4 decimals)
137 $newminprice = $objectstatic->multiprices_min_ttc[$level];
138 } else {
139 $newprice = price2num($objectstatic->multiprices[$level], 'MU'); // Second param must be MU (we want a unit price so 'MU'. If unit price was on 4 decimal, we must keep 4 decimals)
140 $newminprice = $objectstatic->multiprices_min[$level];
141 }
142 if ($newminprice > $newprice) {
143 $newminprice = $newprice;
144 }
145
146 $newvat = str_replace('*', '', $newvatrate);
147 $localtaxes_type = getLocalTaxesFromRate($newvat, 0, $mysoc, $mysoc);
148 $newnpr = $objectstatic->multiprices_recuperableonly[$level];
149 $newdefaultvatcode = $vat_src_code_new;
150 $newlevel = $level;
151
152 //print "$objectstatic->id $newprice, $price_base_type, $newvat, $newminprice, $newlevel, $newnpr<br>\n";
153 $retm = $objectstatic->updatePrice($newprice, $price_base_type, $user, $newvatrateclean, $newminprice, $newlevel, $newnpr, 0, 0, $localtaxes_type, $newdefaultvatcode);
154 if ($retm < 0) {
155 $error++;
156 break;
157 }
158
159 if ($newlevel == 1) {
160 $updatelevel1 = true;
161 }
162 }
163
164 // Update single price
165 $price_base_type = $objectstatic->price_base_type; // Get price_base_type of product/service to keep the same for update
166 if ($price_base_type == 'TTC') {
167 $newprice = price2num($objectstatic->price_ttc, 'MU'); // Second param must be MU (we want a unit price so 'MU'. If unit price was on 4 decimal, we must keep 4 decimals)
168 $newminprice = $objectstatic->price_min_ttc;
169 } else {
170 $newprice = price2num($objectstatic->price, 'MU'); // Second param must be MU (we want a unit price so 'MU'. If unit price was on 4 decimal, we must keep 4 decimals)
171 $newminprice = $objectstatic->price_min;
172 }
173 if ($newminprice > $newprice) {
174 $newminprice = $newprice;
175 }
176 $newvat = str_replace('*', '', $newvatrate);
177 $localtaxes_type = getLocalTaxesFromRate($newvat, 0, $mysoc, $mysoc);
178 $newnpr = $objectstatic->tva_npr;
179 $newdefaultvatcode = $vat_src_code_new;
180 $newlevel = 0;
181 if (!empty($price_base_type) && !$updatelevel1) {
182 //print "$objectstatic->id $newprice, $price_base_type, $newvat, $newminprice, $newlevel, $newnpr<br>\n";
183 $ret = $objectstatic->updatePrice($newprice, $price_base_type, $user, $newvatrateclean, $newminprice, $newlevel, $newnpr, 0, 0, $localtaxes_type, $newdefaultvatcode);
184 }
185
186 if ($ret < 0 || $retm < 0) {
187 $error++;
188 } else {
189 $nbrecordsmodified++;
190 }
191 }
192 unset($objectstatic);
193
194 $i++;
195 }
196 } else {
197 dol_print_error($db);
198 }
199 }
200
201 $fourn = new Fournisseur($db);
202
203 // Change supplier prices
204 $sql = 'SELECT pfp.rowid, pfp.fk_soc, pfp.price as price, pfp.quantity as qty, pfp.fk_availability, pfp.ref_fourn';
205 $sql .= ' FROM '.MAIN_DB_PREFIX.'product_fournisseur_price as pfp, '.MAIN_DB_PREFIX.'societe as s';
206 $sql .= ' WHERE pfp.fk_soc = s.rowid AND pfp.entity IN ('.getEntity('product').')';
207 $sql .= " AND tva_tx = '".$db->escape($oldvatrate)."'";
208 if ($vat_src_code_old) {
209 $sql .= " AND default_vat_code = '".$db->escape($vat_src_code_old)."'";
210 } else {
211 $sql .= " AND default_vat_code = IS NULL";
212 }
213 $sql .= " AND s.fk_pays = ".((int) $country_id);
214
215 $resql = $db->query($sql);
216 if ($resql) {
217 $num = $db->num_rows($resql);
218
219 $i = 0;
220 while ($i < $num) {
221 $obj = $db->fetch_object($resql);
222
223 $objectstatic2 = new ProductFournisseur($db); // Object init must be into loop to avoid to get value of previous step
224 $ret = $objectstatic2->fetch_product_fournisseur_price($obj->rowid);
225 if ($ret > 0) {
226 $ret = 0;
227 $retm = 0;
228 $updatelevel1 = false;
229
230 $price_base_type = 'HT';
231 //$price_base_type = $objectstatic2->price_base_type; // Get price_base_type of product/service to keep the same for update
232 //if ($price_base_type == 'TTC')
233 //{
234 // $newprice=price2num($objectstatic2->price_ttc,'MU'); // Second param must be MU (we want a unit price so 'MU'. If unit price was on 4 decimal, we must keep 4 decimals)
235 // $newminprice=$objectstatic2->price_min_ttc;
236 //}
237 //else
238 //{
239 $newprice = price2num($obj->price, 'MU'); // Second param must be MU (we want a unit price so 'MU'. If unit price was on 4 decimal, we must keep 4 decimals)
240 //$newminprice=$objectstatic2->fourn_price_min;
241 //}
242 //if ($newminprice > $newprice) $newminprice=$newprice;
243 $newvat = str_replace('*', '', $newvatrate);
244 $localtaxes_type = getLocalTaxesFromRate($newvat, 0, $mysoc, $mysoc);
245 //$newnpr=$objectstatic2->tva_npr;
246 $newnpr = 0;
247 $newdefaultvatcode = $vat_src_code_new;
248
249 $newpercent = $objectstatic2->fourn_remise_percent;
250 $newdeliverydelay = $objectstatic2->delivery_time_days;
251 $newsupplierreputation = $objectstatic2->supplier_reputation;
252
253 $newlevel = 0;
254 if (!empty($price_base_type) && !$updatelevel1) {
255 //print "$objectstatic2->id $newprice, $price_base_type, $newvat, $newminprice, $newlevel, $newnpr<br>\n";
256 $fourn->id = $obj->fk_soc;
257 $ret = $objectstatic2->update_buyprice($obj->qty, $newprice, $user, $price_base_type, $fourn, $obj->fk_availability, $obj->ref_fourn, $newvat, '', $newpercent, 0, $newnpr, $newdeliverydelay, $newsupplierreputation, $localtaxes_type, $newdefaultvatcode);
258 }
259
260 if ($ret < 0 || $retm < 0) {
261 $error++;
262 } else {
263 $nbrecordsmodified++;
264 }
265 }
266 unset($objectstatic2);
267
268 $i++;
269 }
270 } else {
271 dol_print_error($db);
272 }
273
274
275 // add hook for external modules
276 $parameters = array('oldvatrate' => $oldvatrate, 'newvatrate' => $newvatrate);
277 $reshook = $hookmanager->executeHooks('hookAfterVatUpdate', $parameters);
278 if ($reshook < 0) {
279 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
280 $error++;
281 }
282
283 if (!getDolGlobalInt('VATUPDATE_NO_TRANSACTION')) {
284 if (!$error) {
285 $db->commit();
286 } else {
287 $db->rollback();
288 }
289 }
290
291 // Output result
292 if (!$error) {
293 if ($nbrecordsmodified > 0) {
294 setEventMessages($langs->trans("RecordsModified", $nbrecordsmodified), null, 'mesgs');
295 } else {
296 setEventMessages($langs->trans("NoRecordFound"), null, 'warnings');
297 }
298 } else {
299 setEventMessages($langs->trans("Error"), null, 'errors');
300 }
301 }
302}
303
304/*
305 * View
306 */
307
308$form = new Form($db);
309
310$title = $langs->trans('ProductVatMassChange');
311
312llxHeader('', $title, '', '', 0, 0, '', '', '', 'mod-product page-admin_product_tools');
313
314print load_fiche_titre($title, '', 'title_setup');
315
316print $langs->trans("ProductVatMassChangeDesc").'<br><br>';
317
318if (empty($mysoc->country_code)) {
319 $langs->load("errors");
320 $warnpicto = img_error($langs->trans("WarningMandatorySetupNotComplete"));
321 print '<br><a href="'.DOL_URL_ROOT.'/admin/company.php?mainmenu=home">'.$warnpicto.' '.$langs->trans("WarningMandatorySetupNotComplete").'</a>';
322} else {
323 print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'">';
324 print '<input type="hidden" name="token" value="'.newToken().'" />';
325 print '<input type="hidden" name="action" value="convert" />';
326
327 print '<table class="noborder centpercent">';
328 print '<tr class="liste_titre">';
329 print '<td>'.$langs->trans("Parameters").'</td>'."\n";
330 print '<td class="right" width="60">'.$langs->trans("Value").'</td>'."\n";
331 print '</tr>'."\n";
332
333
334 print '<tr class="oddeven">'."\n";
335 print '<td>'.$langs->trans("OldVATRates").'</td>'."\n";
336 print '<td width="60" class="right">'."\n";
337 print $form->load_tva('oldvatrate', $oldvatrate, $mysoc, null, 0, 0, '', false, 1);
338 print '</td>'."\n";
339 print '</tr>'."\n";
340
341
342 print '<tr class="oddeven">'."\n";
343 print '<td>'.$langs->trans("NewVATRates").'</td>'."\n";
344 print '<td width="60" class="right">'."\n";
345 print $form->load_tva('newvatrate', $newvatrate, $mysoc, null, 0, 0, '', false, 1);
346 print '</td>'."\n";
347 print '</tr>'."\n";
348
349 /*
350
351 print '<tr class="oddeven">'."\n";
352 print '<td>'.$langs->trans("PriceBaseTypeToChange").'</td>'."\n";
353 print '<td width="60" class="right">'."\n";
354 print $form->selectPriceBaseType($price_base_type);
355 print '</td>'."\n";
356 print '</tr>'."\n";
357 */
358
359 print '</table>';
360
361 print '<br>';
362
363 // Buttons for actions
364
365 print '<div class="center">';
366 print '<input type="submit" id="convert_vatrate" name="convert_vatrate" value="'.$langs->trans("MassConvert").'" class="button" />';
367 print '</div>';
368
369 print '</form>';
370}
371
372// End of page
373llxFooter();
374$db->close();
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:71
Class to manage generation of HTML components Only common components must be here.
Class to manage suppliers.
Class to manage predefined suppliers products.
Class to manage products or services.
llxFooter()
Footer empty.
Definition document.php:107
load_fiche_titre($title, $morehtmlright='', $picto='generic', $pictoisfullpath=0, $id='', $morecssontable='', $morehtmlcenter='')
Load a title with picto.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
img_error($titlealt='default')
Show error logo.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
accessforbidden($message='', $printheader=1, $printfooter=1, $showonlymessage=0, $params=null)
Show a message to say access is forbidden and stop program.