dolibarr 21.0.0-alpha
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
36// Load translation files required by the page
37$langs->loadLangs(array('admin', 'products'));
38
39// Security check
40if (!$user->admin) {
42}
43
44$action = GETPOST('action', 'aZ09');
45$oldvatrate = GETPOST('oldvatrate', 'alpha');
46$newvatrate = GETPOST('newvatrate', 'alpha');
47//$price_base_type=GETPOST('price_base_type');
48
49
50
51/*
52 * Actions
53 */
54
55if ($action == 'convert') {
56 $error = 0;
57
58 if ($oldvatrate == $newvatrate) {
59 $langs->load("errors");
60 setEventMessages($langs->trans("ErrorNewValueCantMatchOldValue"), null, 'errors');
61 $error++;
62 }
63
64 if (!$error) {
65 $country_id = $mysoc->country_id; // TODO Allow to choose country into form
66
67 $nbrecordsmodified = 0;
68
69 if (!getDolGlobalInt('VATUPDATE_NO_TRANSACTION')) {
70 $db->begin();
71 }
72
73 // Clean vat code old
74 $vat_src_code_old = '';
75 if (preg_match('/\‍((.*)\‍)/', $oldvatrate, $reg)) {
76 $vat_src_code_old = $reg[1];
77 $oldvatrateclean = preg_replace('/\s*\‍(.*\‍)/', '', $oldvatrate); // Remove code into vatrate.
78 } else {
79 $oldvatrateclean = $oldvatrate;
80 }
81
82 // Clean vat code new
83 $vat_src_code_new = '';
84 if (preg_match('/\‍((.*)\‍)/', $newvatrate, $reg)) {
85 $vat_src_code_new = $reg[1];
86 $newvatrateclean = preg_replace('/\s*\‍(.*\‍)/', '', $newvatrate); // Remove code into vatrate.
87 } else {
88 $newvatrateclean = $newvatrate;
89 }
90
91 // If country to edit is my country, so we change customer prices
92 if ($country_id == $mysoc->country_id) {
93 $sql = 'SELECT rowid';
94 $sql .= ' FROM '.MAIN_DB_PREFIX.'product';
95 $sql .= ' WHERE entity IN ('.getEntity('product').')';
96 $sql .= " AND tva_tx = '".$db->escape($oldvatrateclean)."'";
97 if ($vat_src_code_old) {
98 $sql .= " AND default_vat_code = '".$db->escape($vat_src_code_old)."'";
99 } else {
100 $sql .= " AND default_vat_code = IS NULL";
101 }
102
103 $resql = $db->query($sql);
104 if ($resql) {
105 $num = $db->num_rows($resql);
106
107 $i = 0;
108 while ($i < $num) {
109 $obj = $db->fetch_object($resql);
110
111 $objectstatic = new Product($db); // Object init must be into loop to avoid to get value of previous step
112 $ret = $objectstatic->fetch($obj->rowid);
113 if ($ret > 0) {
114 $ret = 0;
115 $retm = 0;
116 $updatelevel1 = false;
117
118 // Update multiprice
119 $listofmulti = array_reverse($objectstatic->multiprices, true); // To finish with level 1
120 foreach ($listofmulti as $level => $multiprices) {
121 $price_base_type = $objectstatic->multiprices_base_type[$level]; // Get price_base_type of product/service to keep the same for update
122 if (empty($price_base_type)) {
123 continue; // Discard not defined price levels
124 }
125
126 if ($price_base_type == 'TTC') {
127 $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)
128 $newminprice = $objectstatic->multiprices_min_ttc[$level];
129 } else {
130 $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)
131 $newminprice = $objectstatic->multiprices_min[$level];
132 }
133 if ($newminprice > $newprice) {
134 $newminprice = $newprice;
135 }
136
137 $newvat = str_replace('*', '', $newvatrate);
138 $localtaxes_type = getLocalTaxesFromRate($newvat, 0, $mysoc, $mysoc);
139 $newnpr = $objectstatic->multiprices_recuperableonly[$level];
140 $newdefaultvatcode = $vat_src_code_new;
141 $newlevel = $level;
142
143 //print "$objectstatic->id $newprice, $price_base_type, $newvat, $newminprice, $newlevel, $newnpr<br>\n";
144 $retm = $objectstatic->updatePrice($newprice, $price_base_type, $user, $newvatrateclean, $newminprice, $newlevel, $newnpr, 0, 0, $localtaxes_type, $newdefaultvatcode);
145 if ($retm < 0) {
146 $error++;
147 break;
148 }
149
150 if ($newlevel == 1) {
151 $updatelevel1 = true;
152 }
153 }
154
155 // Update single price
156 $price_base_type = $objectstatic->price_base_type; // Get price_base_type of product/service to keep the same for update
157 if ($price_base_type == 'TTC') {
158 $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)
159 $newminprice = $objectstatic->price_min_ttc;
160 } else {
161 $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)
162 $newminprice = $objectstatic->price_min;
163 }
164 if ($newminprice > $newprice) {
165 $newminprice = $newprice;
166 }
167 $newvat = str_replace('*', '', $newvatrate);
168 $localtaxes_type = getLocalTaxesFromRate($newvat, 0, $mysoc, $mysoc);
169 $newnpr = $objectstatic->tva_npr;
170 $newdefaultvatcode = $vat_src_code_new;
171 $newlevel = 0;
172 if (!empty($price_base_type) && !$updatelevel1) {
173 //print "$objectstatic->id $newprice, $price_base_type, $newvat, $newminprice, $newlevel, $newnpr<br>\n";
174 $ret = $objectstatic->updatePrice($newprice, $price_base_type, $user, $newvatrateclean, $newminprice, $newlevel, $newnpr, 0, 0, $localtaxes_type, $newdefaultvatcode);
175 }
176
177 if ($ret < 0 || $retm < 0) {
178 $error++;
179 } else {
180 $nbrecordsmodified++;
181 }
182 }
183 unset($objectstatic);
184
185 $i++;
186 }
187 } else {
188 dol_print_error($db);
189 }
190 }
191
192 $fourn = new Fournisseur($db);
193
194 // Change supplier prices
195 $sql = 'SELECT pfp.rowid, pfp.fk_soc, pfp.price as price, pfp.quantity as qty, pfp.fk_availability, pfp.ref_fourn';
196 $sql .= ' FROM '.MAIN_DB_PREFIX.'product_fournisseur_price as pfp, '.MAIN_DB_PREFIX.'societe as s';
197 $sql .= ' WHERE pfp.fk_soc = s.rowid AND pfp.entity IN ('.getEntity('product').')';
198 $sql .= " AND tva_tx = '".$db->escape($oldvatrate)."'";
199 if ($vat_src_code_old) {
200 $sql .= " AND default_vat_code = '".$db->escape($vat_src_code_old)."'";
201 } else {
202 $sql .= " AND default_vat_code = IS NULL";
203 }
204 $sql .= " AND s.fk_pays = ".((int) $country_id);
205
206 $resql = $db->query($sql);
207 if ($resql) {
208 $num = $db->num_rows($resql);
209
210 $i = 0;
211 while ($i < $num) {
212 $obj = $db->fetch_object($resql);
213
214 $objectstatic2 = new ProductFournisseur($db); // Object init must be into loop to avoid to get value of previous step
215 $ret = $objectstatic2->fetch_product_fournisseur_price($obj->rowid);
216 if ($ret > 0) {
217 $ret = 0;
218 $retm = 0;
219 $updatelevel1 = false;
220
221 $price_base_type = 'HT';
222 //$price_base_type = $objectstatic2->price_base_type; // Get price_base_type of product/service to keep the same for update
223 //if ($price_base_type == 'TTC')
224 //{
225 // $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)
226 // $newminprice=$objectstatic2->price_min_ttc;
227 //}
228 //else
229 //{
230 $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)
231 //$newminprice=$objectstatic2->fourn_price_min;
232 //}
233 //if ($newminprice > $newprice) $newminprice=$newprice;
234 $newvat = str_replace('*', '', $newvatrate);
235 $localtaxes_type = getLocalTaxesFromRate($newvat, 0, $mysoc, $mysoc);
236 //$newnpr=$objectstatic2->tva_npr;
237 $newnpr = 0;
238 $newdefaultvatcode = $vat_src_code_new;
239
240 $newpercent = $objectstatic2->fourn_remise_percent;
241 $newdeliverydelay = $objectstatic2->delivery_time_days;
242 $newsupplierreputation = $objectstatic2->supplier_reputation;
243
244 $newlevel = 0;
245 if (!empty($price_base_type) && !$updatelevel1) {
246 //print "$objectstatic2->id $newprice, $price_base_type, $newvat, $newminprice, $newlevel, $newnpr<br>\n";
247 $fourn->id = $obj->fk_soc;
248 $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);
249 }
250
251 if ($ret < 0 || $retm < 0) {
252 $error++;
253 } else {
254 $nbrecordsmodified++;
255 }
256 }
257 unset($objectstatic2);
258
259 $i++;
260 }
261 } else {
262 dol_print_error($db);
263 }
264
265
266 // add hook for external modules
267 $parameters = array('oldvatrate' => $oldvatrate, 'newvatrate' => $newvatrate);
268 $reshook = $hookmanager->executeHooks('hookAfterVatUpdate', $parameters);
269 if ($reshook < 0) {
270 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
271 $error++;
272 }
273
274 if (!getDolGlobalInt('VATUPDATE_NO_TRANSACTION')) {
275 if (!$error) {
276 $db->commit();
277 } else {
278 $db->rollback();
279 }
280 }
281
282 // Output result
283 if (!$error) {
284 if ($nbrecordsmodified > 0) {
285 setEventMessages($langs->trans("RecordsModified", $nbrecordsmodified), null, 'mesgs');
286 } else {
287 setEventMessages($langs->trans("NoRecordFound"), null, 'warnings');
288 }
289 } else {
290 setEventMessages($langs->trans("Error"), null, 'errors');
291 }
292 }
293}
294
295/*
296 * View
297 */
298
299$form = new Form($db);
300
301$title = $langs->trans('ProductVatMassChange');
302
303llxHeader('', $title, '', '', 0, 0, '', '', '', 'mod-product page-admin_product_tools');
304
305print load_fiche_titre($title, '', 'title_setup');
306
307print $langs->trans("ProductVatMassChangeDesc").'<br><br>';
308
309if (empty($mysoc->country_code)) {
310 $langs->load("errors");
311 $warnpicto = img_error($langs->trans("WarningMandatorySetupNotComplete"));
312 print '<br><a href="'.DOL_URL_ROOT.'/admin/company.php?mainmenu=home">'.$warnpicto.' '.$langs->trans("WarningMandatorySetupNotComplete").'</a>';
313} else {
314 print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'">';
315 print '<input type="hidden" name="token" value="'.newToken().'" />';
316 print '<input type="hidden" name="action" value="convert" />';
317
318 print '<table class="noborder centpercent">';
319 print '<tr class="liste_titre">';
320 print '<td>'.$langs->trans("Parameters").'</td>'."\n";
321 print '<td class="right" width="60">'.$langs->trans("Value").'</td>'."\n";
322 print '</tr>'."\n";
323
324
325 print '<tr class="oddeven">'."\n";
326 print '<td>'.$langs->trans("OldVATRates").'</td>'."\n";
327 print '<td width="60" class="right">'."\n";
328 print $form->load_tva('oldvatrate', $oldvatrate, $mysoc, null, 0, 0, '', false, 1);
329 print '</td>'."\n";
330 print '</tr>'."\n";
331
332
333 print '<tr class="oddeven">'."\n";
334 print '<td>'.$langs->trans("NewVATRates").'</td>'."\n";
335 print '<td width="60" class="right">'."\n";
336 print $form->load_tva('newvatrate', $newvatrate, $mysoc, null, 0, 0, '', false, 1);
337 print '</td>'."\n";
338 print '</tr>'."\n";
339
340 /*
341
342 print '<tr class="oddeven">'."\n";
343 print '<td>'.$langs->trans("PriceBaseTypeToChange").'</td>'."\n";
344 print '<td width="60" class="right">'."\n";
345 print $form->selectPriceBaseType($price_base_type);
346 print '</td>'."\n";
347 print '</tr>'."\n";
348 */
349
350 print '</table>';
351
352 print '<br>';
353
354 // Buttons for actions
355
356 print '<div class="center">';
357 print '<input type="submit" id="convert_vatrate" name="convert_vatrate" value="'.$langs->trans("MassConvert").'" class="button" />';
358 print '</div>';
359
360 print '</form>';
361}
362
363// End of page
364llxFooter();
365$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:70
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.