dolibarr  20.0.0-alpha
card.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2001-2007 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3  * Copyright (C) 2004-2016 Laurent Destailleur <eldy@users.sourceforge.net>
4  * Copyright (C) 2005 Eric Seigne <eric.seigne@ryxeo.com>
5  * Copyright (C) 2005-2015 Regis Houssin <regis.houssin@capnetworks.com>
6  * Copyright (C) 2006 Andre Cianfarani <acianfa@free.fr>
7  * Copyright (C) 2006 Auguria SARL <info@auguria.org>
8  * Copyright (C) 2010-2015 Juanjo Menent <jmenent@2byte.es>
9  * Copyright (C) 2013-2016 Marcos García <marcosgdf@gmail.com>
10  * Copyright (C) 2012-2013 Cédric Salvador <csalvador@gpcsolutions.fr>
11  * Copyright (C) 2011-2023 Alexandre Spangaro <aspangaro@open-dsi.fr>
12  * Copyright (C) 2014 Cédric Gross <c.gross@kreiz-it.fr>
13  * Copyright (C) 2014-2015 Ferran Marcet <fmarcet@2byte.es>
14  * Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
15  * Copyright (C) 2015 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
16  * Copyright (C) 2016-2022 Charlene Benke <charlene@patas-monkey.com>
17  * Copyright (C) 2016 Meziane Sof <virtualsof@yahoo.fr>
18  * Copyright (C) 2017 Josep Lluís Amador <joseplluis@lliuretic.cat>
19  * Copyright (C) 2019-2022 Frédéric France <frederic.france@netlogic.fr>
20  * Copyright (C) 2019-2020 Thibault FOUCART <support@ptibogxiv.net>
21  * Copyright (C) 2020 Pierre Ardoin <mapiolca@me.com>
22  * Copyright (C) 2022 Vincent de Grandpré <vincent@de-grandpre.quebec>
23  * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
24  *
25  * This program is free software; you can redistribute it and/or modify
26  * it under the terms of the GNU General Public License as published by
27  * the Free Software Foundation; either version 3 of the License, or
28  * (at your option) any later version.
29  *
30  * This program is distributed in the hope that it will be useful,
31  * but WITHOUT ANY WARRANTY; without even the implied warranty of
32  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33  * GNU General Public License for more details.
34  *
35  * You should have received a copy of the GNU General Public License
36  * along with this program. If not, see <https://www.gnu.org/licenses/>.
37  */
38 
46 // Load Dolibarr environment
47 require '../main.inc.php';
48 require_once DOL_DOCUMENT_ROOT.'/core/class/canvas.class.php';
49 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
50 require_once DOL_DOCUMENT_ROOT.'/core/class/genericobject.class.php';
51 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php';
52 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php';
53 require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
54 require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
55 require_once DOL_DOCUMENT_ROOT.'/core/modules/product/modules_product.class.php';
56 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
57 require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php';
58 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
59 
60 
61 if (isModEnabled('propal')) {
62  require_once DOL_DOCUMENT_ROOT.'/comm/propal/class/propal.class.php';
63 }
64 if (isModEnabled('invoice')) {
65  require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
66 }
67 if (isModEnabled('order')) {
68  require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
69 }
70 if (isModEnabled('accounting')) {
71  require_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php';
72  require_once DOL_DOCUMENT_ROOT.'/core/class/html.formaccounting.class.php';
73  require_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountingaccount.class.php';
74 }
75 if (isModEnabled('bom')) {
76  require_once DOL_DOCUMENT_ROOT.'/bom/class/bom.class.php';
77 }
78 if (isModEnabled('workstation')) {
79  require_once DOL_DOCUMENT_ROOT.'/workstation/class/workstation.class.php';
80 }
81 
82 // Load translation files required by the page
83 $langs->loadLangs(array('products', 'other'));
84 if (isModEnabled('stock')) {
85  $langs->load("stocks");
86 }
87 if (isModEnabled('invoice')) {
88  $langs->load("bills");
89 }
90 if (isModEnabled('productbatch')) {
91  $langs->load("productbatch");
92 }
93 
94 $mesg = '';
95 $error = 0;
96 $errors = array();
97 
98 $refalreadyexists = 0;
99 
100 // Get parameters
101 $id = GETPOSTINT('id');
102 if (getDolGlobalString('MAIN_SECURITY_ALLOW_UNSECURED_REF_LABELS')) {
103  $ref = (GETPOSTISSET('ref') ? GETPOST('ref', 'nohtml') : null);
104 } else {
105  $ref = (GETPOSTISSET('ref') ? GETPOST('ref', 'alpha') : null);
106 }
107 $type = (GETPOSTISSET('type') ? GETPOSTINT('type') : Product::TYPE_PRODUCT);
108 $action = (GETPOST('action', 'alpha') ? GETPOST('action', 'alpha') : 'view');
109 $cancel = GETPOST('cancel', 'alpha');
110 $backtopage = GETPOST('backtopage', 'alpha');
111 $confirm = GETPOST('confirm', 'alpha');
112 $socid = GETPOSTINT('socid');
113 $duration_value = GETPOSTINT('duration_value');
114 $duration_unit = GETPOST('duration_unit', 'alpha');
115 
116 $accountancy_code_sell = GETPOST('accountancy_code_sell', 'alpha');
117 $accountancy_code_sell_intra = GETPOST('accountancy_code_sell_intra', 'alpha');
118 $accountancy_code_sell_export = GETPOST('accountancy_code_sell_export', 'alpha');
119 $accountancy_code_buy = GETPOST('accountancy_code_buy', 'alpha');
120 $accountancy_code_buy_intra = GETPOST('accountancy_code_buy_intra', 'alpha');
121 $accountancy_code_buy_export = GETPOST('accountancy_code_buy_export', 'alpha');
122 
123 $checkmandatory = GETPOST('accountancy_code_buy_export', 'alpha');
124 
125 // by default 'alphanohtml' (better security); hidden conf MAIN_SECURITY_ALLOW_UNSECURED_LABELS_WITH_HTML allows basic html
126 if (getDolGlobalString('MAIN_SECURITY_ALLOW_UNSECURED_REF_LABELS')) {
127  $label_security_check = 'nohtml';
128 } else {
129  $label_security_check = !getDolGlobalString('MAIN_SECURITY_ALLOW_UNSECURED_LABELS_WITH_HTML') ? 'alphanohtml' : 'restricthtml';
130 }
131 
132 if (!empty($user->socid)) {
133  $socid = $user->socid;
134 }
135 
136 // Load object modCodeProduct
137 $module = (getDolGlobalString('PRODUCT_CODEPRODUCT_ADDON') ? $conf->global->PRODUCT_CODEPRODUCT_ADDON : 'mod_codeproduct_leopard');
138 if (substr($module, 0, 16) == 'mod_codeproduct_' && substr($module, -3) == 'php') {
139  $module = substr($module, 0, dol_strlen($module) - 4);
140 }
141 $result = dol_include_once('/core/modules/product/'.$module.'.php');
142 if ($result > 0) {
143  $modCodeProduct = new $module();
144 }
145 
146 $object = new Product($db);
147 $object->type = $type; // so test later to fill $usercancxxx is correct
148 $extrafields = new ExtraFields($db);
149 
150 // fetch optionals attributes and labels
151 $extrafields->fetch_name_optionals_label($object->table_element);
152 
153 if ($id > 0 || !empty($ref)) {
154  $result = $object->fetch($id, $ref);
155  if ($result < 0) {
156  dol_print_error($db, $object->error, $object->errors);
157  }
158  $entity = (empty($object->entity) ? $conf->entity : $object->entity);
159  if (isModEnabled("product")) {
160  $upload_dir = $conf->product->multidir_output[$entity].'/'.get_exdir(0, 0, 0, 0, $object, 'product').dol_sanitizeFileName($object->ref);
161  } elseif (isModEnabled("service")) {
162  $upload_dir = $conf->service->multidir_output[$entity].'/'.get_exdir(0, 0, 0, 0, $object, 'product').dol_sanitizeFileName($object->ref);
163  }
164 
165  if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) { // For backward compatibility, we scan also old dirs
166  if (isModEnabled("product")) {
167  $upload_dirold = $conf->product->multidir_output[$entity].'/'.substr(substr("000".$object->id, -2), 1, 1).'/'.substr(substr("000".$object->id, -2), 0, 1).'/'.$object->id."/photos";
168  } else {
169  $upload_dirold = $conf->service->multidir_output[$entity].'/'.substr(substr("000".$object->id, -2), 1, 1).'/'.substr(substr("000".$object->id, -2), 0, 1).'/'.$object->id."/photos";
170  }
171  }
172 }
173 
174 $modulepart = 'product';
175 
176 // Get object canvas (By default, this is not defined, so standard usage of dolibarr)
177 $canvas = !empty($object->canvas) ? $object->canvas : GETPOST("canvas");
178 $objcanvas = null;
179 if (!empty($canvas)) {
180  require_once DOL_DOCUMENT_ROOT.'/core/class/canvas.class.php';
181  $objcanvas = new Canvas($db, $action);
182  $objcanvas->getCanvas('product', 'card', $canvas);
183 }
184 
185 // Security check
186 $fieldvalue = (!empty($id) ? $id : (!empty($ref) ? $ref : ''));
187 $fieldtype = (!empty($id) ? 'rowid' : 'ref');
188 
189 if ($object->id > 0) {
190  if ($object->type == $object::TYPE_PRODUCT) {
191  restrictedArea($user, 'produit', $object->id, 'product&product', '', '');
192  }
193  if ($object->type == $object::TYPE_SERVICE) {
194  restrictedArea($user, 'service', $object->id, 'product&product', '', '');
195  }
196 } else {
197  restrictedArea($user, 'produit|service', 0, 'product&product', '', '', $fieldtype);
198 }
199 
200 // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context
201 $hookmanager->initHooks(array('productcard', 'globalcard'));
202 
203 // Permissions
204 $usercanread = (($object->type == Product::TYPE_PRODUCT && $user->hasRight('produit', 'read')) || ($object->type == Product::TYPE_SERVICE && $user->hasRight('service', 'lire')));
205 $usercancreate = (($object->type == Product::TYPE_PRODUCT && $user->hasRight('produit', 'creer')) || ($object->type == Product::TYPE_SERVICE && $user->hasRight('service', 'creer')));
206 $usercandelete = (($object->type == Product::TYPE_PRODUCT && $user->hasRight('produit', 'supprimer')) || ($object->type == Product::TYPE_SERVICE && $user->hasRight('service', 'supprimer')));
207 
208 
209 /*
210  * Actions
211  */
212 
213 if ($cancel) {
214  $action = '';
215 }
216 
217 $createbarcode = isModEnabled('barcode');
218 if (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && !$user->hasRight('barcode', 'creer_advance')) {
219  $createbarcode = 0;
220 }
221 
222 $parameters = array('id' => $id, 'ref' => $ref, 'objcanvas' => $objcanvas);
223 $reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
224 if ($reshook < 0) {
225  setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
226 }
227 
228 if (empty($reshook)) {
229  $backurlforlist = DOL_URL_ROOT.'/product/list.php?type='.$type;
230 
231  if (empty($backtopage) || ($cancel && empty($id))) {
232  if (empty($backtopage) || ($cancel && strpos($backtopage, '__ID__'))) {
233  if (empty($id) && (($action != 'add' && $action != 'create') || $cancel)) {
234  $backtopage = $backurlforlist;
235  } else {
236  $backtopage = DOL_URL_ROOT.'/product/card.php?id='.((!empty($id) && $id > 0) ? $id : '__ID__');
237  }
238  }
239  }
240 
241  if ($cancel) {
242  if (!empty($backtopageforcancel)) {
243  header("Location: ".$backtopageforcancel);
244  exit;
245  } elseif (!empty($backtopage)) {
246  header("Location: ".$backtopage);
247  exit;
248  }
249  $action = '';
250  }
251  // merge products
252  if ($action == 'confirm_merge' && $confirm == 'yes' && $user->hasRight('societe', 'creer')) {
253  $error = 0;
254  $productOriginId = GETPOSTINT('product_origin');
255  $productOrigin = new Product($db);
256 
257  if ($productOriginId <= 0) {
258  $langs->load('errors');
259  setEventMessages($langs->trans('ErrorProductIdIsMandatory', $langs->transnoentitiesnoconv('MergeOriginProduct')), null, 'errors');
260  } else {
261  if (!$error && $productOrigin->fetch($productOriginId) < 1) {
262  setEventMessages($langs->trans('ErrorRecordNotFound'), null, 'errors');
263  $error++;
264  }
265 
266  if (!$error) {
267  // TODO Move the merge function into class of object.
268  $db->begin();
269 
270  // Recopy some data
271  $listofproperties = array(
272  'ref',
273  'ref_ext',
274  'label',
275  'description',
276  'url',
277  'barcode',
278  'fk_barcode_type',
279  'import_key',
280  'mandatory_period',
281  'accountancy_code_buy',
282  'accountancy_code_buy_intra',
283  'accountancy_code_buy_export',
284  'accountancy_code_sell',
285  'accountancy_code_sell_intra',
286  'accountancy_code_sell_export'
287  );
288  foreach ($listofproperties as $property) {
289  if (empty($object->$property)) {
290  $object->$property = $productOrigin->$property;
291  }
292  }
293  // Concat some data
294  $listofproperties = array(
295  'note_public', 'note_private'
296  );
297  foreach ($listofproperties as $property) {
298  $object->$property = dol_concatdesc($object->$property, $productOrigin->$property);
299  }
300 
301  // Merge extrafields
302  if (is_array($productOrigin->array_options)) {
303  foreach ($productOrigin->array_options as $key => $val) {
304  if (empty($object->array_options[$key])) {
305  $object->array_options[$key] = $val;
306  }
307  }
308  }
309 
310  // Merge categories
311  $static_cat = new Categorie($db);
312  $custcats_ori = $static_cat->containing($productOrigin->id, 'product', 'id');
313  $custcats = $static_cat->containing($object->id, 'product', 'id');
314  $custcats = array_merge($custcats, $custcats_ori);
315  $object->setCategories($custcats);
316 
317  // If product has a new code that is same than origin, we clean origin code to avoid duplicate key from database unique keys.
318  if ($productOrigin->barcode == $object->barcode) {
319  dol_syslog("We clean customer and supplier code so we will be able to make the update of target");
320  $productOrigin->barcode = '';
321  //$productOrigin->update($productOrigin->id, $user, 0, 'merge');
322  }
323 
324  // Update
325  $result = $object->update($object->id, $user, 0, 'merge');
326  if ($result <= 0) {
327  setEventMessages($object->error, $object->errors, 'errors');
328  $error++;
329  }
330 
331  // Move links
332  if (!$error) {
333  // TODO add this functionality into the api_products.class.php
334  // TODO Mutualise the list into object product.class.php
335  $objects = array(
336  'ActionComm' => '/comm/action/class/actioncomm.class.php',
337  'Bom' => '/bom/class/bom.class.php',
338  // do not use Categorie, it cause foreign key error, merge is done before
339  //'Categorie' => '/categories/class/categorie.class.php',
340  'Commande' => '/commande/class/commande.class.php',
341  'CommandeFournisseur' => '/fourn/class/fournisseur.commande.class.php',
342  'Contrat' => '/contrat/class/contrat.class.php',
343  'Delivery' => '/delivery/class/delivery.class.php',
344  'Facture' => '/compta/facture/class/facture.class.php',
345  'FactureFournisseur' => '/fourn/class/fournisseur.facture.class.php',
346  'FactureRec' => '/compta/facture/class/facture-rec.class.php',
347  'FichinterRec' => '/fichinter/class/fichinterrec.class.php',
348  'ProductFournisseur' => '/fourn/class/fournisseur.product.class.php',
349  'Propal' => '/comm/propal/class/propal.class.php',
350  'Reception' => '/reception/class/reception.class.php',
351  'SupplierProposal' => '/supplier_proposal/class/supplier_proposal.class.php',
352  );
353 
354  //First, all core objects must update their tables
355  foreach ($objects as $object_name => $object_file) {
356  require_once DOL_DOCUMENT_ROOT.$object_file;
357 
358  if (!$error && !$object_name::replaceProduct($db, $productOrigin->id, $object->id)) {
359  $error++;
360  setEventMessages($db->lasterror(), null, 'errors');
361  break;
362  }
363  }
364  }
365 
366  // External modules should update their ones too
367  if (!$error) {
368  $parameters = array('soc_origin' => $productOrigin->id, 'soc_dest' => $object->id);
369  $reshook = $hookmanager->executeHooks(
370  'replaceProduct',
371  $parameters,
372  $object,
373  $action
374  );
375 
376  if ($reshook < 0) {
377  setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
378  $error++;
379  }
380  }
381 
382 
383  if (!$error) {
384  $object->context = array(
385  'merge' => 1,
386  'mergefromid' => $productOrigin->id,
387  );
388 
389  // Call trigger
390  $result = $object->call_trigger('PRODUCT_MODIFY', $user);
391  if ($result < 0) {
392  setEventMessages($object->error, $object->errors, 'errors');
393  $error++;
394  }
395  // End call triggers
396  }
397 
398  if (!$error) {
399  // We finally remove the old product
400  // TODO merge attached files from old product into new one before delete
401  if ($productOrigin->delete($user) < 1) {
402  $error++;
403  }
404  }
405 
406  if (!$error) {
407  setEventMessages($langs->trans('ProductsMergeSuccess'), null, 'mesgs');
408  $db->commit();
409  } else {
410  $langs->load("errors");
411  setEventMessages($langs->trans('ErrorsProductsMerge'), null, 'errors');
412  $db->rollback();
413  }
414  }
415  }
416  }
417 
418  // Type
419  if ($action == 'setfk_product_type' && $usercancreate) {
420  $result = $object->setValueFrom('fk_product_type', GETPOST('fk_product_type'), '', null, 'text', '', $user, 'PRODUCT_MODIFY');
421  header("Location: ".$_SERVER['PHP_SELF']."?id=".$object->id);
422  exit;
423  }
424 
425  // Actions to build doc
426  $upload_dir = $conf->product->dir_output;
427  $permissiontoadd = $usercancreate;
428  include DOL_DOCUMENT_ROOT.'/core/actions_builddoc.inc.php';
429 
430  include DOL_DOCUMENT_ROOT.'/core/actions_printing.inc.php';
431 
432  // Barcode type
433  if ($action == 'setfk_barcode_type' && $createbarcode) {
434  $result = $object->setValueFrom('fk_barcode_type', GETPOST('fk_barcode_type'), '', null, 'text', '', $user, 'PRODUCT_MODIFY');
435  header("Location: ".$_SERVER['PHP_SELF']."?id=".$object->id);
436  exit;
437  }
438 
439  // Barcode value
440  if ($action == 'setbarcode' && $createbarcode) {
441  $result = $object->check_barcode(GETPOST('barcode'), GETPOST('barcode_type_code'));
442 
443  if ($result >= 0) {
444  $result = $object->setValueFrom('barcode', GETPOST('barcode'), '', null, 'text', '', $user, 'PRODUCT_MODIFY');
445  header("Location: ".$_SERVER['PHP_SELF']."?id=".$object->id);
446  exit;
447  } else {
448  $langs->load("errors");
449  if ($result == -1) {
450  $errors[] = 'ErrorBadBarCodeSyntax';
451  } elseif ($result == -2) {
452  $errors[] = 'ErrorBarCodeRequired';
453  } elseif ($result == -3) {
454  $errors[] = 'ErrorBarCodeAlreadyUsed';
455  } else {
456  $errors[] = 'FailedToValidateBarCode';
457  }
458 
459  $error++;
460  setEventMessages('', $errors, 'errors');
461  }
462  }
463 
464  // Quick edit for extrafields
465  if ($action == 'update_extras') {
466  $object->oldcopy = dol_clone($object, 2);
467 
468  // Fill array 'array_options' with data from update form
469  $ret = $extrafields->setOptionalsFromPost(null, $object, GETPOST('attribute', 'restricthtml'));
470  if ($ret < 0) {
471  $error++;
472  }
473 
474  if (!$error) {
475  // Actions on extra fields
476  $result = $object->insertExtraFields('PRODUCT_MODIFY');
477  if ($result < 0) {
478  setEventMessages($object->error, $object->errors, 'errors');
479  $error++;
480  }
481  }
482 
483  if ($error) {
484  $action = 'edit_extras';
485  }
486  }
487 
488  // Add a product or service
489  if ($action == 'add' && $usercancreate) {
490  $error = 0;
491 
492  if (!GETPOST('label', $label_security_check)) {
493  setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentities('Label')), null, 'errors');
494  $action = "create";
495  $error++;
496  }
497  if (empty($ref)) {
498  if (!getDolGlobalString('PRODUCT_GENERATE_REF_AFTER_FORM')) {
499  setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentities('ProductRef')), null, 'errors');
500  $action = "create";
501  $error++;
502  }
503  }
504  if (!empty($duration_value) && empty($duration_unit)) {
505  setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentities('Unit')), null, 'errors');
506  $action = "create";
507  $error++;
508  }
509 
510  if (!$error) {
511  $units = GETPOSTINT('units');
512 
513  $object->entity = $conf->entity;
514  $object->ref = $ref;
515  $object->label = GETPOST('label', $label_security_check);
516  $object->price_base_type = GETPOST('price_base_type', 'aZ09');
517  $object->mandatory_period = !empty(GETPOST("mandatoryperiod", 'alpha')) ? 1 : 0;
518  if ($object->price_base_type == 'TTC') {
519  $object->price_ttc = GETPOST('price');
520  } else {
521  $object->price = GETPOST('price');
522  }
523  if ($object->price_base_type == 'TTC') {
524  $object->price_min_ttc = GETPOST('price_min');
525  } else {
526  $object->price_min = GETPOST('price_min');
527  }
528 
529  $tva_tx_txt = GETPOST('tva_tx', 'alpha'); // tva_tx can be '8.5' or '8.5*' or '8.5 (XXX)' or '8.5* (XXX)'
530 
531  // We must define tva_tx, npr and local taxes
532  $vatratecode = '';
533  $tva_tx = preg_replace('/[^0-9\.].*$/', '', $tva_tx_txt); // keep remove all after the numbers and dot
534  $npr = preg_match('/\*/', $tva_tx_txt) ? 1 : 0;
535  $localtax1 = 0;
536  $localtax2 = 0;
537  $localtax1_type = '0';
538  $localtax2_type = '0';
539  // If value contains the unique code of vat line (new recommended method), we use it to find npr and local taxes
540  $reg = array();
541  if (preg_match('/\‍((.*)\‍)/', $tva_tx_txt, $reg)) {
542  // We look into database using code (we can't use get_localtax() because it depends on buyer that is not known). Same in update price.
543  $vatratecode = $reg[1];
544  // Get record from code
545  $sql = "SELECT t.rowid, t.code, t.recuperableonly, t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
546  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
547  $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($mysoc->country_code)."'";
548  $sql .= " AND t.taux = ".((float) $tva_tx)." AND t.active = 1";
549  $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
550  $sql .= " AND t.entity IN (".getEntity('c_tva').")";
551  $resql = $db->query($sql);
552  if ($resql) {
553  $obj = $db->fetch_object($resql);
554  $npr = $obj->recuperableonly;
555  $localtax1 = $obj->localtax1;
556  $localtax2 = $obj->localtax2;
557  $localtax1_type = $obj->localtax1_type;
558  $localtax2_type = $obj->localtax2_type;
559  }
560  }
561 
562  $object->default_vat_code = $vatratecode;
563  $object->tva_tx = $tva_tx;
564  $object->tva_npr = $npr;
565  $object->localtax1_tx = $localtax1;
566  $object->localtax2_tx = $localtax2;
567  $object->localtax1_type = $localtax1_type;
568  $object->localtax2_type = $localtax2_type;
569 
570  $object->type = $type;
571  $object->status = GETPOST('statut');
572  $object->status_buy = GETPOST('statut_buy');
573  $object->status_batch = GETPOST('status_batch');
574  $object->sell_or_eat_by_mandatory = GETPOSTINT('sell_or_eat_by_mandatory');
575  $object->batch_mask = GETPOST('batch_mask');
576 
577  $object->barcode_type = GETPOST('fk_barcode_type');
578  $object->barcode = GETPOST('barcode');
579  // Set barcode_type_xxx from barcode_type id
580  $stdobject = new GenericObject($db);
581  $stdobject->element = 'product';
582  $stdobject->barcode_type = GETPOSTINT('fk_barcode_type');
583  $result = $stdobject->fetch_barcode();
584  if ($result < 0) {
585  $error++;
586  $mesg = 'Failed to get bar code type information ';
587  setEventMessages($mesg.$stdobject->error, $stdobject->errors, 'errors');
588  }
589  $object->barcode_type_code = $stdobject->barcode_type_code;
590  $object->barcode_type_coder = $stdobject->barcode_type_coder;
591  $object->barcode_type_label = $stdobject->barcode_type_label;
592 
593  $object->description = dol_htmlcleanlastbr(GETPOST('desc', 'restricthtml'));
594  $object->url = GETPOST('url');
595  $object->note_private = dol_htmlcleanlastbr(GETPOST('note_private', 'restricthtml'));
596  $object->note = $object->note_private; // deprecated
597  $object->customcode = GETPOST('customcode', 'alphanohtml');
598  $object->country_id = GETPOSTINT('country_id');
599  $object->state_id = GETPOSTINT('state_id');
600  $object->lifetime = GETPOSTINT('lifetime');
601  $object->qc_frequency = GETPOSTINT('qc_frequency');
602  $object->duration_value = $duration_value;
603  $object->duration_unit = $duration_unit;
604  $object->fk_default_warehouse = GETPOSTINT('fk_default_warehouse');
605  $object->fk_default_workstation = GETPOSTINT('fk_default_workstation');
606  $object->seuil_stock_alerte = GETPOST('seuil_stock_alerte') ? GETPOST('seuil_stock_alerte') : 0;
607  $object->desiredstock = GETPOST('desiredstock') ? GETPOST('desiredstock') : 0;
608  $object->canvas = GETPOST('canvas');
609  $object->net_measure = GETPOST('net_measure');
610  $object->net_measure_units = GETPOST('net_measure_units'); // This is not the fk_unit but the power of unit
611  $object->weight = GETPOST('weight');
612  $object->weight_units = GETPOST('weight_units'); // This is not the fk_unit but the power of unit
613  $object->length = GETPOST('size');
614  $object->length_units = GETPOST('size_units'); // This is not the fk_unit but the power of unit
615  $object->width = GETPOST('sizewidth');
616  $object->height = GETPOST('sizeheight');
617  $object->surface = GETPOST('surface');
618  $object->surface_units = GETPOST('surface_units'); // This is not the fk_unit but the power of unit
619  $object->volume = GETPOST('volume');
620  $object->volume_units = GETPOST('volume_units'); // This is not the fk_unit but the power of unit
621  $finished = GETPOSTINT('finished');
622  if ($finished >= 0) {
623  $object->finished = $finished;
624  } else {
625  $object->finished = null;
626  }
627 
628  $units = GETPOSTINT('units');
629  if ($units > 0) {
630  $object->fk_unit = $units;
631  } else {
632  $object->fk_unit = null;
633  }
634 
635  $accountancy_code_sell = GETPOST('accountancy_code_sell', 'alpha');
636  $accountancy_code_sell_intra = GETPOST('accountancy_code_sell_intra', 'alpha');
637  $accountancy_code_sell_export = GETPOST('accountancy_code_sell_export', 'alpha');
638  $accountancy_code_buy = GETPOST('accountancy_code_buy', 'alpha');
639  $accountancy_code_buy_intra = GETPOST('accountancy_code_buy_intra', 'alpha');
640  $accountancy_code_buy_export = GETPOST('accountancy_code_buy_export', 'alpha');
641 
642  if (empty($accountancy_code_sell) || $accountancy_code_sell == '-1') {
643  $object->accountancy_code_sell = '';
644  } else {
645  $object->accountancy_code_sell = $accountancy_code_sell;
646  }
647  if (empty($accountancy_code_sell_intra) || $accountancy_code_sell_intra == '-1') {
648  $object->accountancy_code_sell_intra = '';
649  } else {
650  $object->accountancy_code_sell_intra = $accountancy_code_sell_intra;
651  }
652  if (empty($accountancy_code_sell_export) || $accountancy_code_sell_export == '-1') {
653  $object->accountancy_code_sell_export = '';
654  } else {
655  $object->accountancy_code_sell_export = $accountancy_code_sell_export;
656  }
657  if (empty($accountancy_code_buy) || $accountancy_code_buy == '-1') {
658  $object->accountancy_code_buy = '';
659  } else {
660  $object->accountancy_code_buy = $accountancy_code_buy;
661  }
662  if (empty($accountancy_code_buy_intra) || $accountancy_code_buy_intra == '-1') {
663  $object->accountancy_code_buy_intra = '';
664  } else {
665  $object->accountancy_code_buy_intra = $accountancy_code_buy_intra;
666  }
667  if (empty($accountancy_code_buy_export) || $accountancy_code_buy_export == '-1') {
668  $object->accountancy_code_buy_export = '';
669  } else {
670  $object->accountancy_code_buy_export = $accountancy_code_buy_export;
671  }
672 
673  // MultiPrix
674  if (getDolGlobalString('PRODUIT_MULTIPRICES')) {
675  for ($i = 2; $i <= $conf->global->PRODUIT_MULTIPRICES_LIMIT; $i++) {
676  if (GETPOSTISSET("price_".$i)) {
677  $object->multiprices["$i"] = price2num(GETPOST("price_".$i), 'MU');
678  $object->multiprices_base_type["$i"] = GETPOST("multiprices_base_type_".$i);
679  } else {
680  $object->multiprices["$i"] = "";
681  }
682  }
683  }
684 
685  // Fill array 'array_options' with data from add form
686  $ret = $extrafields->setOptionalsFromPost(null, $object);
687  if ($ret < 0) {
688  $error++;
689  }
690 
691  if (!$ref && getDolGlobalString('PRODUCT_GENERATE_REF_AFTER_FORM')) {
692  // Generate ref...
693  $ref = $modCodeProduct->getNextValue($object, $type);
694  }
695 
696  if (!$error) {
697  $id = $object->create($user);
698  }
699 
700  if ($id > 0) {
701  // Category association
702  $categories = GETPOST('categories', 'array');
703  $object->setCategories($categories);
704 
705  if (!empty($backtopage)) {
706  $backtopage = preg_replace('/__ID__/', (string) $object->id, $backtopage); // New method to autoselect parent project after a New on another form object creation
707  $backtopage = preg_replace('/--IDFORBACKTOPAGE--/', (string) $object->id, $backtopage); // New method to autoselect parent after a New on another form object creation
708  if (preg_match('/\?/', $backtopage)) {
709  $backtopage .= '&productid='.$object->id; // Old method
710  }
711 
712  header("Location: ".$backtopage);
713  exit;
714  } else {
715  header("Location: ".$_SERVER['PHP_SELF']."?id=".$id);
716  exit;
717  }
718  } else {
719  if (count($object->errors)) {
720  setEventMessages($object->error, $object->errors, 'errors');
721  } else {
722  if ($object->error == 'ErrorProductAlreadyExists') {
723  // allow to hook on ErrorProductAlreadyExists in any module
724  $reshook = $hookmanager->executeHooks('onProductAlreadyExists', $parameters, $object, $action);
725  if ($reshook < 0) {
726  setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
727  }
728  if ($object->error) {
729  // check again to prevent translation issue,
730  // as error may have been cleared in hook function
731  setEventMessages($langs->trans($object->error), null, 'errors');
732  }
733  } else {
734  setEventMessages($langs->trans($object->error), null, 'errors');
735  }
736  }
737  $action = "create";
738  }
739  }
740  }
741 
742  // Update a product or service
743  if ($action == 'update' && $usercancreate) {
744  if (GETPOST('cancel', 'alpha')) {
745  $action = '';
746  } else {
747  if ($object->id > 0) {
748  //Need dol_clone methode 1 (same object class) because update product use hasbatch() method on oldcopy
749  $object->oldcopy = dol_clone($object, 1);
750 
751  if (!getDolGlobalString('PRODUCT_GENERATE_REF_AFTER_FORM')) {
752  $object->ref = $ref;
753  }
754  $object->label = GETPOST('label', $label_security_check);
755 
756  $desc = dol_htmlcleanlastbr(preg_replace('/&nbsp;$/', '', GETPOST('desc', 'restricthtml')));
757  $object->description = $desc;
758 
759  $object->url = GETPOST('url');
760  if (getDolGlobalString('MAIN_DISABLE_NOTES_TAB')) {
761  $object->note_private = dol_htmlcleanlastbr(GETPOST('note_private', 'restricthtml'));
762  $object->note = $object->note_private;
763  }
764  $object->customcode = GETPOST('customcode', 'alpha');
765  $object->country_id = GETPOSTINT('country_id');
766  $object->state_id = GETPOSTINT('state_id');
767  $object->lifetime = GETPOSTINT('lifetime');
768  $object->qc_frequency = GETPOSTINT('qc_frequency');
769  $object->status = GETPOSTINT('statut');
770  $object->status_buy = GETPOSTINT('statut_buy');
771  $object->status_batch = GETPOST('status_batch', 'aZ09');
772  $object->sell_or_eat_by_mandatory = GETPOSTINT('sell_or_eat_by_mandatory');
773  $object->batch_mask = GETPOST('batch_mask', 'alpha');
774  $object->fk_default_warehouse = GETPOSTINT('fk_default_warehouse');
775  $object->fk_default_workstation = GETPOSTINT('fk_default_workstation');
776  // removed from update view so GETPOST always empty
777  /*
778  $object->seuil_stock_alerte = GETPOST('seuil_stock_alerte');
779  $object->desiredstock = GETPOST('desiredstock');
780  */
781  $object->duration_value = GETPOSTINT('duration_value');
782  $object->duration_unit = GETPOST('duration_unit', 'alpha');
783 
784  $object->canvas = GETPOST('canvas');
785  $object->net_measure = GETPOST('net_measure');
786  $object->net_measure_units = GETPOST('net_measure_units'); // This is not the fk_unit but the power of unit
787  $object->weight = GETPOST('weight');
788  $object->weight_units = GETPOST('weight_units'); // This is not the fk_unit but the power of unit
789  $object->length = GETPOST('size');
790  $object->length_units = GETPOST('size_units'); // This is not the fk_unit but the power of unit
791  $object->width = GETPOST('sizewidth');
792  $object->height = GETPOST('sizeheight');
793 
794  $object->surface = GETPOST('surface');
795  $object->surface_units = GETPOST('surface_units'); // This is not the fk_unit but the power of unit
796  $object->volume = GETPOST('volume');
797  $object->volume_units = GETPOST('volume_units'); // This is not the fk_unit but the power of unit
798 
799  $finished = GETPOSTINT('finished');
800  if ($finished >= 0) {
801  $object->finished = $finished;
802  } else {
803  $object->finished = null;
804  }
805 
806  $fk_default_bom = GETPOSTINT('fk_default_bom');
807  if ($fk_default_bom >= 0) {
808  $object->fk_default_bom = $fk_default_bom;
809  } else {
810  $object->fk_default_bom = 0;
811  }
812 
813  $units = GETPOSTINT('units');
814  if ($units > 0) {
815  $object->fk_unit = $units;
816  } else {
817  $object->fk_unit = null;
818  }
819 
820  $object->barcode_type = GETPOST('fk_barcode_type');
821  $object->barcode = GETPOST('barcode');
822  // Set barcode_type_xxx from barcode_type id
823  $stdobject = new GenericObject($db);
824  $stdobject->element = 'product';
825  $stdobject->barcode_type = GETPOSTINT('fk_barcode_type');
826  $result = $stdobject->fetch_barcode();
827  if ($result < 0) {
828  $error++;
829  $mesg = 'Failed to get bar code type information ';
830  setEventMessages($mesg.$stdobject->error, $stdobject->errors, 'errors');
831  }
832  $object->barcode_type_code = $stdobject->barcode_type_code;
833  $object->barcode_type_coder = $stdobject->barcode_type_coder;
834  $object->barcode_type_label = $stdobject->barcode_type_label;
835 
836  $accountancy_code_sell = GETPOST('accountancy_code_sell', 'alpha');
837  $accountancy_code_sell_intra = GETPOST('accountancy_code_sell_intra', 'alpha');
838  $accountancy_code_sell_export = GETPOST('accountancy_code_sell_export', 'alpha');
839  $accountancy_code_buy = GETPOST('accountancy_code_buy', 'alpha');
840  $accountancy_code_buy_intra = GETPOST('accountancy_code_buy_intra', 'alpha');
841  $accountancy_code_buy_export = GETPOST('accountancy_code_buy_export', 'alpha');
842  $checkmandatory = GETPOST('mandatoryperiod', 'alpha');
843  if (empty($accountancy_code_sell) || $accountancy_code_sell == '-1') {
844  $object->accountancy_code_sell = '';
845  } else {
846  $object->accountancy_code_sell = $accountancy_code_sell;
847  }
848  if (empty($accountancy_code_sell_intra) || $accountancy_code_sell_intra == '-1') {
849  $object->accountancy_code_sell_intra = '';
850  } else {
851  $object->accountancy_code_sell_intra = $accountancy_code_sell_intra;
852  }
853  if (empty($accountancy_code_sell_export) || $accountancy_code_sell_export == '-1') {
854  $object->accountancy_code_sell_export = '';
855  } else {
856  $object->accountancy_code_sell_export = $accountancy_code_sell_export;
857  }
858  if (empty($accountancy_code_buy) || $accountancy_code_buy == '-1') {
859  $object->accountancy_code_buy = '';
860  } else {
861  $object->accountancy_code_buy = $accountancy_code_buy;
862  }
863  if (empty($accountancy_code_buy_intra) || $accountancy_code_buy_intra == '-1') {
864  $object->accountancy_code_buy_intra = '';
865  } else {
866  $object->accountancy_code_buy_intra = $accountancy_code_buy_intra;
867  }
868  if (empty($accountancy_code_buy_export) || $accountancy_code_buy_export == '-1') {
869  $object->accountancy_code_buy_export = '';
870  } else {
871  $object->accountancy_code_buy_export = $accountancy_code_buy_export;
872  }
873  if ($object->isService()) {
874  $object->mandatory_period = (!empty($checkmandatory)) ? 1 : 0 ;
875  }
876 
877 
878 
879  // Fill array 'array_options' with data from add form
880  $ret = $extrafields->setOptionalsFromPost(null, $object, '@GETPOSTISSET');
881  if ($ret < 0) {
882  $error++;
883  }
884 
885  if (!$error && $object->check()) {
886  if ($object->update($object->id, $user) > 0) {
887  // Category association
888  $categories = GETPOST('categories', 'array');
889  $object->setCategories($categories);
890 
891  $action = 'view';
892  } else {
893  if (count($object->errors)) {
894  setEventMessages($object->error, $object->errors, 'errors');
895  } else {
896  setEventMessages($langs->trans($object->error), null, 'errors');
897  }
898  $action = 'edit';
899  }
900  } else {
901  if (count($object->errors)) {
902  setEventMessages($object->error, $object->errors, 'errors');
903  } else {
904  setEventMessages($langs->trans("ErrorProductBadRefOrLabel"), null, 'errors');
905  }
906  $action = 'edit';
907  }
908  }
909  }
910  }
911 
912  // Action clone object
913  if ($action == 'confirm_clone' && $confirm != 'yes') {
914  $action = '';
915  }
916  if ($action == 'confirm_clone' && $confirm == 'yes' && $usercancreate) {
917  if (!GETPOST('clone_content') && !GETPOST('clone_prices')) {
918  setEventMessages($langs->trans("NoCloneOptionsSpecified"), null, 'errors');
919  } else {
920  if ($object->id > 0) {
921  $error = 0;
922  // We clone object to avoid to denaturate loaded object when setting some properties for clone or if createFromClone modifies the object.
923  // We use native clone to keep this->db valid and allow to use later all the methods of object.
924  $clone = dol_clone($object, 1);
925 
926  $clone->id = 0;
927  $clone->ref = GETPOST('clone_ref', 'alphanohtml');
928  $clone->status = 0;
929  $clone->status_buy = 0;
930  $clone->barcode = -1;
931 
932  if ($clone->check()) {
933  $db->begin();
934 
935  $clone->context['createfromclone'] = 'createfromclone';
936  $id = $clone->create($user);
937  if ($id > 0) {
938  if (GETPOST('clone_composition')) {
939  $result = $clone->clone_associations($object->id, $id);
940  if ($result < 1) {
941  setEventMessages($langs->trans('ErrorProductClone'), null, 'errors');
942  setEventMessages($clone->error, $clone->errors, 'errors');
943  $error++;
944  }
945  }
946 
947  if (!$error && GETPOST('clone_categories')) {
948  $result = $clone->cloneCategories($object->id, $id);
949  if ($result < 1) {
950  setEventMessages($langs->trans('ErrorProductClone'), null, 'errors');
951  setEventMessages($clone->error, $clone->errors, 'errors');
952  $error++;
953  }
954  }
955 
956  if (!$error && GETPOST('clone_prices')) {
957  $result = $clone->clone_price($object->id, $id);
958  if ($result < 1) {
959  setEventMessages($langs->trans('ErrorProductClone'), null, 'errors');
960  setEventMessages($clone->error, $clone->errors, 'errors');
961  $error++;
962  }
963  }
964 
965  // $clone->clone_fournisseurs($object->id, $id);
966  } else {
967  if ($clone->error == 'ErrorProductAlreadyExists') {
968  $refalreadyexists++;
969  $action = "";
970 
971  $mesg = $langs->trans("ErrorProductAlreadyExists", $clone->ref);
972  $mesg .= ' <a href="' . $_SERVER["PHP_SELF"] . '?ref=' . $clone->ref . '">' . $langs->trans("ShowCardHere") . '</a>.';
973  setEventMessages($mesg, null, 'errors');
974  } else {
975  setEventMessages(empty($clone->error) ? '' : $langs->trans($clone->error), $clone->errors, 'errors');
976  }
977  $error++;
978  }
979 
980  unset($clone->context['createfromclone']);
981 
982  if ($error) {
983  $db->rollback();
984  } else {
985  $db->commit();
986  $db->close();
987  header("Location: " . $_SERVER["PHP_SELF"] . "?id=" . $id);
988  exit;
989  }
990  } else {
991  setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("NewRefForClone")), null, 'errors');
992  }
993  } else {
994  dol_print_error($db, $object->error, $object->errors);
995  }
996  }
997  $action = 'clone';
998  }
999 
1000  // Delete a product
1001  if ($action == 'confirm_delete' && $confirm != 'yes') {
1002  $action = '';
1003  }
1004  if ($action == 'confirm_delete' && $confirm == 'yes' && $usercandelete) {
1005  $result = $object->delete($user);
1006 
1007  if ($result > 0) {
1008  header('Location: '.DOL_URL_ROOT.'/product/list.php?type='.$object->type.'&delprod='.urlencode($object->ref));
1009  exit;
1010  } else {
1011  setEventMessages($langs->trans($object->error), null, 'errors');
1012  $reload = 0;
1013  $action = '';
1014  }
1015  }
1016 
1017 
1018  // Add product into object
1019  if ($object->id > 0 && $action == 'addin') {
1020  $thirpdartyid = 0;
1021  if (GETPOST('propalid') > 0) {
1022  $propal = new Propal($db);
1023  $result = $propal->fetch(GETPOST('propalid'));
1024  if ($result <= 0) {
1025  dol_print_error($db, $propal->error);
1026  exit;
1027  }
1028  $thirpdartyid = $propal->socid;
1029  } elseif (GETPOST('commandeid') > 0) {
1030  $commande = new Commande($db);
1031  $result = $commande->fetch(GETPOST('commandeid'));
1032  if ($result <= 0) {
1033  dol_print_error($db, $commande->error);
1034  exit;
1035  }
1036  $thirpdartyid = $commande->socid;
1037  } elseif (GETPOST('factureid') > 0) {
1038  $facture = new Facture($db);
1039  $result = $facture->fetch(GETPOST('factureid'));
1040  if ($result <= 0) {
1041  dol_print_error($db, $facture->error);
1042  exit;
1043  }
1044  $thirpdartyid = $facture->socid;
1045  }
1046 
1047  if ($thirpdartyid > 0) {
1048  $soc = new Societe($db);
1049  $result = $soc->fetch($thirpdartyid);
1050  if ($result <= 0) {
1051  dol_print_error($db, $soc->error);
1052  exit;
1053  }
1054 
1055  $desc = $object->description;
1056 
1057  $tva_tx = get_default_tva($mysoc, $soc, $object->id);
1058  $tva_npr = get_default_npr($mysoc, $soc, $object->id);
1059  if (empty($tva_tx)) {
1060  $tva_npr = 0;
1061  }
1062  $localtax1_tx = get_localtax($tva_tx, 1, $soc, $mysoc, $tva_npr);
1063  $localtax2_tx = get_localtax($tva_tx, 2, $soc, $mysoc, $tva_npr);
1064 
1065  $pu_ht = $object->price;
1066  $pu_ttc = $object->price_ttc;
1067  $price_base_type = $object->price_base_type;
1068 
1069  // If multiprice
1070  if ($conf->global->PRODUIT_MULTIPRICES && $soc->price_level) {
1071  $pu_ht = $object->multiprices[$soc->price_level];
1072  $pu_ttc = $object->multiprices_ttc[$soc->price_level];
1073  $price_base_type = $object->multiprices_base_type[$soc->price_level];
1074  } elseif (getDolGlobalString('PRODUIT_CUSTOMER_PRICES')) {
1075  require_once DOL_DOCUMENT_ROOT.'/product/class/productcustomerprice.class.php';
1076 
1077  $prodcustprice = new ProductCustomerPrice($db);
1078 
1079  $filter = array('t.fk_product' => $object->id, 't.fk_soc' => $soc->id);
1080 
1081  $result = $prodcustprice->fetchAll('', '', 0, 0, $filter);
1082  if ($result) {
1083  if (count($prodcustprice->lines) > 0) {
1084  $pu_ht = price($prodcustprice->lines [0]->price);
1085  $pu_ttc = price($prodcustprice->lines [0]->price_ttc);
1086  $price_base_type = $prodcustprice->lines [0]->price_base_type;
1087  $tva_tx = $prodcustprice->lines [0]->tva_tx;
1088  }
1089  }
1090  }
1091 
1092  $tmpvat = price2num(preg_replace('/\s*\‍(.*\‍)/', '', $tva_tx));
1093  $tmpprodvat = price2num(preg_replace('/\s*\‍(.*\‍)/', '', $prod->tva_tx));
1094 
1095  // On reevalue prix selon taux tva car taux tva transaction peut etre different
1096  // de ceux du produit par default (par example si pays different entre vendeur et acheteur).
1097  if ($tmpvat != $tmpprodvat) {
1098  if ($price_base_type != 'HT') {
1099  $pu_ht = price2num($pu_ttc / (1 + ($tmpvat / 100)), 'MU');
1100  } else {
1101  $pu_ttc = price2num($pu_ht * (1 + ($tmpvat / 100)), 'MU');
1102  }
1103  }
1104 
1105  if (GETPOST('propalid') > 0) {
1106  // Define cost price for margin calculation
1107  $buyprice = 0;
1108  if (($result = $propal->defineBuyPrice($pu_ht, price2num(GETPOST('remise_percent'), '', 2), $object->id)) < 0) {
1109  dol_syslog($langs->trans('FailedToGetCostPrice'));
1110  setEventMessages($langs->trans('FailedToGetCostPrice'), null, 'errors');
1111  } else {
1112  $buyprice = $result;
1113  }
1114 
1115  $result = $propal->addline(
1116  $desc,
1117  $pu_ht,
1118  price2num(GETPOST('qty'), 'MS'),
1119  $tva_tx,
1120  $localtax1_tx, // localtax1
1121  $localtax2_tx, // localtax2
1122  $object->id,
1123  price2num(GETPOST('remise_percent'), '', 2),
1124  $price_base_type,
1125  $pu_ttc,
1126  0,
1127  0,
1128  -1,
1129  0,
1130  0,
1131  0,
1132  $buyprice,
1133  '',
1134  '',
1135  '',
1136  0,
1137  $object->fk_unit
1138  );
1139  if ($result > 0) {
1140  header("Location: ".DOL_URL_ROOT."/comm/propal/card.php?id=".$propal->id);
1141  return;
1142  }
1143 
1144  setEventMessages($langs->trans("ErrorUnknown").": $result", null, 'errors');
1145  } elseif (GETPOST('commandeid') > 0) {
1146  // Define cost price for margin calculation
1147  $buyprice = 0;
1148  if (($result = $commande->defineBuyPrice($pu_ht, price2num(GETPOST('remise_percent'), '', 2), $object->id)) < 0) {
1149  dol_syslog($langs->trans('FailedToGetCostPrice'));
1150  setEventMessages($langs->trans('FailedToGetCostPrice'), null, 'errors');
1151  } else {
1152  $buyprice = $result;
1153  }
1154 
1155  $result = $commande->addline(
1156  $desc,
1157  $pu_ht,
1158  price2num(GETPOST('qty'), 'MS'),
1159  $tva_tx,
1160  $localtax1_tx, // localtax1
1161  $localtax2_tx, // localtax2
1162  $object->id,
1163  price2num(GETPOST('remise_percent'), '', 2),
1164  '',
1165  '',
1166  $price_base_type,
1167  $pu_ttc,
1168  '',
1169  '',
1170  0,
1171  -1,
1172  0,
1173  0,
1174  null,
1175  $buyprice,
1176  '',
1177  0,
1178  $object->fk_unit
1179  );
1180 
1181  if ($result > 0) {
1182  header("Location: ".DOL_URL_ROOT."/commande/card.php?id=".urlencode((string) ($commande->id)));
1183  exit;
1184  }
1185  } elseif (GETPOST('factureid') > 0) {
1186  // Define cost price for margin calculation
1187  $buyprice = 0;
1188  if (($result = $facture->defineBuyPrice($pu_ht, price2num(GETPOST('remise_percent'), '', 2), $object->id)) < 0) {
1189  dol_syslog($langs->trans('FailedToGetCostPrice'));
1190  setEventMessages($langs->trans('FailedToGetCostPrice'), null, 'errors');
1191  } else {
1192  $buyprice = $result;
1193  }
1194 
1195  $result = $facture->addline(
1196  $desc,
1197  $pu_ht,
1198  price2num(GETPOST('qty'), 'MS'),
1199  $tva_tx,
1200  $localtax1_tx,
1201  $localtax2_tx,
1202  $object->id,
1203  price2num(GETPOST('remise_percent'), '', 2),
1204  '',
1205  '',
1206  '',
1207  '',
1208  '',
1209  $price_base_type,
1210  $pu_ttc,
1212  -1,
1213  0,
1214  '',
1215  0,
1216  0,
1217  null,
1218  $buyprice,
1219  '',
1220  0,
1221  100,
1222  '',
1223  $object->fk_unit
1224  );
1225 
1226  if ($result > 0) {
1227  header("Location: ".DOL_URL_ROOT."/compta/facture/card.php?facid=".$facture->id);
1228  exit;
1229  }
1230  }
1231  } else {
1232  $action = "";
1233  setEventMessages($langs->trans("WarningSelectOneDocument"), null, 'warnings');
1234  }
1235  }
1236 }
1237 
1238 
1239 
1240 /*
1241  * View
1242  */
1243 
1244 $form = new Form($db);
1245 $formfile = new FormFile($db);
1246 $formproduct = new FormProduct($db);
1247 $formcompany = new FormCompany($db);
1248 if (isModEnabled('accounting')) {
1249  $formaccounting = new FormAccounting($db);
1250 }
1251 $sellOrEatByMandatoryList = null;
1252 if (isModEnabled('productbatch')) {
1253  $sellOrEatByMandatoryList = Product::getSellOrEatByMandatoryList();
1254 }
1255 
1256 $title = $langs->trans('ProductServiceCard');
1257 
1258 $help_url = '';
1259 $shortlabel = dol_trunc($object->label, 16);
1260 if (GETPOST("type") == '0' || ($object->type == Product::TYPE_PRODUCT)) {
1261  if ($action == 'create') {
1262  $title = $langs->trans("NewProduct");
1263  } else {
1264  $title = $langs->trans('Product')." ".$shortlabel." - ".$langs->trans('Card');
1265  $help_url = 'EN:Module_Products|FR:Module_Produits|ES:M&oacute;dulo_Productos|DE:Modul_Produkte';
1266  }
1267 }
1268 if (GETPOST("type") == '1' || ($object->type == Product::TYPE_SERVICE)) {
1269  if ($action == 'create') {
1270  $title = $langs->trans("NewService");
1271  } else {
1272  $title = $langs->trans('Service')." ".$shortlabel." - ".$langs->trans('Card');
1273  $help_url = 'EN:Module_Services_En|FR:Module_Services|ES:M&oacute;dulo_Servicios|DE:Modul_Leistungen';
1274  }
1275 }
1276 
1277 llxHeader('', $title, $help_url);
1278 
1279 // Load object modBarCodeProduct
1280 $res = 0;
1281 if (isModEnabled('barcode') && getDolGlobalString('BARCODE_PRODUCT_ADDON_NUM')) {
1282  $module = strtolower($conf->global->BARCODE_PRODUCT_ADDON_NUM);
1283  $dirbarcode = array_merge(array('/core/modules/barcode/'), $conf->modules_parts['barcode']);
1284  foreach ($dirbarcode as $dirroot) {
1285  $res = dol_include_once($dirroot.$module.'.php');
1286  if ($res) {
1287  break;
1288  }
1289  }
1290  if ($res > 0) {
1291  $modBarCodeProduct = new $module();
1292  }
1293 }
1294 
1295 $canvasdisplayaction = $action;
1296 if (in_array($canvasdisplayaction, array('merge', 'confirm_merge'))) {
1297  $canvasdisplayaction = 'view';
1298 }
1299 
1300 if (is_object($objcanvas) && $objcanvas->displayCanvasExists($canvasdisplayaction)) {
1301  // -----------------------------------------
1302  // When used with CANVAS
1303  // -----------------------------------------
1304  $objcanvas->assign_values($canvasdisplayaction, $object->id, $object->ref); // Set value for templates
1305  $objcanvas->display_canvas($canvasdisplayaction); // Show template
1306 } else {
1307  // -----------------------------------------
1308  // When used in standard mode
1309  // -----------------------------------------
1310  if ($action == 'create' && $usercancreate) {
1311  //WYSIWYG Editor
1312  require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
1313 
1314  if (!empty($conf->use_javascript_ajax)) {
1315  print '<script type="text/javascript">';
1316  print '$(document).ready(function () {
1317  $("#selectcountry_id").change(function() {
1318  document.formprod.action.value="create";
1319  document.formprod.submit();
1320  });
1321  });';
1322  print '</script>'."\n";
1323  }
1324 
1325  // Load object modCodeProduct
1326  $module = (getDolGlobalString('PRODUCT_CODEPRODUCT_ADDON') ? $conf->global->PRODUCT_CODEPRODUCT_ADDON : 'mod_codeproduct_leopard');
1327  if (substr($module, 0, 16) == 'mod_codeproduct_' && substr($module, -3) == 'php') {
1328  $module = substr($module, 0, dol_strlen($module) - 4);
1329  }
1330  $result = dol_include_once('/core/modules/product/'.$module.'.php');
1331  if ($result > 0) {
1332  $modCodeProduct = new $module();
1333  }
1334 
1335  dol_set_focus('input[name="ref"]');
1336 
1337  print '<form action="'.$_SERVER["PHP_SELF"].'" method="POST" name="formprod">';
1338  print '<input type="hidden" name="token" value="'.newToken().'">';
1339  print '<input type="hidden" name="action" value="add">';
1340  print '<input type="hidden" name="type" value="'.$type.'">'."\n";
1341  if (!empty($modCodeProduct->code_auto)) {
1342  print '<input type="hidden" name="code_auto" value="1">';
1343  }
1344  if (!empty($modBarCodeProduct->code_auto)) {
1345  print '<input type="hidden" name="barcode_auto" value="1">';
1346  }
1347  print '<input type="hidden" name="backtopage" value="'.$backtopage.'">';
1348 
1349  if ($type == 1) {
1350  $picto = 'service';
1351  $title = $langs->trans("NewService");
1352  } else {
1353  $picto = 'product';
1354  $title = $langs->trans("NewProduct");
1355  }
1356  $linkback = "";
1357  print load_fiche_titre($title, $linkback, $picto);
1358 
1359  // We set country_id, country_code and country for the selected country
1360  $object->country_id = GETPOSTISSET('country_id') ? GETPOSTINT('country_id') : null;
1361  if ($object->country_id > 0) {
1362  $tmparray = getCountry($object->country_id, 'all');
1363  $object->country_code = $tmparray['code'];
1364  $object->country = $tmparray['label'];
1365  }
1366 
1367  print dol_get_fiche_head();
1368 
1369  // Call Hook tabContentCreateProduct
1370  $parameters = array();
1371  // Note that $action and $object may be modified by hook
1372  $reshook = $hookmanager->executeHooks('tabContentCreateProduct', $parameters, $object, $action);
1373  if (empty($reshook)) {
1374  print '<table class="border centpercent">';
1375 
1376  if (!getDolGlobalString('PRODUCT_GENERATE_REF_AFTER_FORM')) {
1377  print '<tr>';
1378  $tmpcode = '';
1379  if (!empty($modCodeProduct->code_auto)) {
1380  $tmpcode = $modCodeProduct->getNextValue($object, $type);
1381  }
1382  print '<td class="titlefieldcreate fieldrequired">'.$langs->trans("ProductRef").'</td><td><input id="ref" name="ref" class="maxwidth200" maxlength="128" value="'.dol_escape_htmltag(GETPOSTISSET('ref') ? GETPOST('ref', 'alphanohtml') : $tmpcode).'">';
1383  if ($refalreadyexists) {
1384  print $langs->trans("RefAlreadyExists");
1385  }
1386  print '</td></tr>';
1387  }
1388 
1389  // Label
1390  print '<tr><td class="fieldrequired">'.$langs->trans("Label").'</td><td><input name="label" class="minwidth300 maxwidth400onsmartphone" maxlength="255" value="'.dol_escape_htmltag(GETPOST('label', $label_security_check)).'"></td></tr>';
1391 
1392  // On sell
1393  print '<tr><td class="fieldrequired">'.$langs->trans("Status").' ('.$langs->trans("Sell").')</td><td>';
1394  $statutarray = array('1' => $langs->trans("OnSell"), '0' => $langs->trans("NotOnSell"));
1395  print $form->selectarray('statut', $statutarray, GETPOST('statut'));
1396  print '</td></tr>';
1397 
1398  // To buy
1399  print '<tr><td class="fieldrequired">'.$langs->trans("Status").' ('.$langs->trans("Buy").')</td><td>';
1400  $statutarray = array('1' => $langs->trans("ProductStatusOnBuy"), '0' => $langs->trans("ProductStatusNotOnBuy"));
1401  print $form->selectarray('statut_buy', $statutarray, GETPOST('statut_buy'));
1402  print '</td></tr>';
1403 
1404  // Batch number management
1405  if (isModEnabled('productbatch')) {
1406  print '<tr><td>'.$langs->trans("ManageLotSerial").'</td><td>';
1407  $statutarray = array('0' => $langs->trans("ProductStatusNotOnBatch"), '1' => $langs->trans("ProductStatusOnBatch"), '2' => $langs->trans("ProductStatusOnSerial"));
1408  print $form->selectarray('status_batch', $statutarray, GETPOST('status_batch'));
1409  print '</td></tr>';
1410  // Product specific batch number management
1411  $status_batch = GETPOST('status_batch');
1412  if ($status_batch !== '0') {
1413  $langs->load("admin");
1414  $tooltip = $langs->trans("GenericMaskCodes", $langs->transnoentities("Batch"), $langs->transnoentities("Batch"));
1415  $tooltip .= '<br>'.$langs->trans("GenericMaskCodes2");
1416  $tooltip .= '<br>'.$langs->trans("GenericMaskCodes3");
1417  $tooltip .= '<br>'.$langs->trans("GenericMaskCodes4a", $langs->transnoentities("Batch"), $langs->transnoentities("Batch"));
1418  $tooltip .= '<br>'.$langs->trans("GenericMaskCodes5");
1419  if ((getDolGlobalString('PRODUCTBATCH_LOT_ADDON') == 'mod_lot_advanced')
1420  || (getDolGlobalString('PRODUCTBATCH_SN_ADDON') == 'mod_sn_advanced')) {
1421  print '<tr><td id="mask_option">'.$langs->trans("ManageLotMask").'</td>';
1422  $inherited_mask_lot = getDolGlobalString('LOT_ADVANCED_MASK');
1423  $inherited_mask_sn = getDolGlobalString('SN_ADVANCED_MASK');
1424  print '<td id="field_mask">';
1425  print $form->textwithpicto('<input type="text" class="flat minwidth175" name="batch_mask" id="batch_mask_input">', $tooltip, 1, 1);
1426  print '<script type="text/javascript">
1427  $(document).ready(function() {
1428  $("#field_mask, #mask_option").addClass("hideobject");
1429  $("#status_batch").on("change", function () {
1430  console.log("We change batch status");
1431  var optionSelected = $("option:selected", this);
1432  var valueSelected = this.value;
1433  $("#field_mask, #mask_option").addClass("hideobject");
1434  ';
1435  if (getDolGlobalString('PRODUCTBATCH_LOT_USE_PRODUCT_MASKS') && getDolGlobalString('PRODUCTBATCH_LOT_ADDON') == 'mod_lot_advanced') {
1436  print '
1437  if (this.value == 1) {
1438  $("#field_mask, #mask_option").toggleClass("hideobject");
1439  $("#batch_mask_input").val("'.$inherited_mask_lot.'");
1440  }
1441  ';
1442  }
1443  if (isset($conf->global->PRODUCTBATCH_SN_USE_PRODUCT_MASKS) && getDolGlobalString('PRODUCTBATCH_SN_ADDON') == 'mod_sn_advanced') {
1444  print '
1445  if (this.value == 2) {
1446  $("#field_mask, #mask_option").toggleClass("hideobject");
1447  $("#batch_mask_input").val("'.$inherited_mask_sn.'");
1448  }
1449  ';
1450  }
1451  print '
1452  })
1453  })
1454  </script>';
1455  print '</td></tr>';
1456  }
1457  }
1458 
1459  // SellBy / EatBy mandatory list
1460  if (!empty($sellOrEatByMandatoryList)) {
1461  print '<tr><td>'.$langs->trans('BatchSellOrEatByMandatoryList', $langs->trans('SellByDate'), $langs->trans('EatByDate')).'</td><td>';
1462  print $form->selectarray('sell_or_eat_by_mandatory', $sellOrEatByMandatoryList, GETPOSTINT('sell_or_eat_by_mandatory'));
1463  print '</td></tr>';
1464  }
1465  }
1466 
1467  $showbarcode = isModEnabled('barcode');
1468  if (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && !$user->hasRight('barcode', 'lire_advance')) {
1469  $showbarcode = 0;
1470  }
1471 
1472  if ($showbarcode) {
1473  print '<tr><td>'.$langs->trans('BarcodeType').'</td><td>';
1474  if (GETPOSTISSET('fk_barcode_type')) {
1475  $fk_barcode_type = GETPOST('fk_barcode_type') ? GETPOST('fk_barcode_type') : 0;
1476  } else {
1477  if (empty($fk_barcode_type) && getDolGlobalString('PRODUIT_DEFAULT_BARCODE_TYPE')) {
1478  $fk_barcode_type = getDolGlobalInt("PRODUIT_DEFAULT_BARCODE_TYPE");
1479  } else {
1480  $fk_barcode_type = 0;
1481  }
1482  }
1483  require_once DOL_DOCUMENT_ROOT.'/core/class/html.formbarcode.class.php';
1484  $formbarcode = new FormBarCode($db);
1485  print $formbarcode->selectBarcodeType($fk_barcode_type, 'fk_barcode_type', 1);
1486  print '</td>';
1487  print '</tr><tr>';
1488  print '<td>'.$langs->trans("BarcodeValue").'</td><td>';
1489  $tmpcode = GETPOSTISSET('barcode') ? GETPOST('barcode') : $object->barcode;
1490  if (empty($tmpcode) && !empty($modBarCodeProduct->code_auto)) {
1491  $tmpcode = $modBarCodeProduct->getNextValue($object, $fk_barcode_type);
1492  }
1493  print img_picto('', 'barcode', 'class="pictofixedwidth"');
1494  print '<input class="maxwidth100" type="text" name="barcode" value="'.dol_escape_htmltag($tmpcode).'">';
1495  print '</td></tr>';
1496  }
1497 
1498  // Description (used in invoice, propal...)
1499  print '<tr><td class="tdtop">'.$langs->trans("Description").'</td><td>';
1500  $doleditor = new DolEditor('desc', GETPOST('desc', 'restricthtml'), '', 160, 'dolibarr_details', '', false, true, getDolGlobalString('FCKEDITOR_ENABLE_DETAILS'), ROWS_4, '90%');
1501  $doleditor->Create();
1502  print "</td></tr>";
1503 
1504  if (!getDolGlobalString('PRODUCT_DISABLE_PUBLIC_URL')) {
1505  // Public URL
1506  print '<tr><td>'.$langs->trans("PublicUrl").'</td><td>';
1507  print img_picto('', 'globe', 'class="pictofixedwidth"');
1508  print '<input type="text" name="url" class="quatrevingtpercent" value="'.GETPOST('url').'">';
1509  print '</td></tr>';
1510  }
1511 
1512  if (($type != 1 || getDolGlobalInt('STOCK_SUPPORTS_SERVICES')) && isModEnabled('stock')) {
1513  // Default warehouse
1514  print '<tr><td>'.$langs->trans("DefaultWarehouse").'</td><td>';
1515  print img_picto($langs->trans("DefaultWarehouse"), 'stock', 'class="pictofixedwidth"');
1516  print $formproduct->selectWarehouses(GETPOSTINT('fk_default_warehouse'), 'fk_default_warehouse', 'warehouseopen', 1, 0, 0, '', 0, 0, array(), 'minwidth300 widthcentpercentminusxx maxwidth500');
1517  print ' <a href="'.DOL_URL_ROOT.'/product/stock/card.php?action=create&token='.newToken().'&backtopage='.urlencode($_SERVER['PHP_SELF'].'?&action=create&type='.GETPOSTINT('type')).'">';
1518  print '<span class="fa fa-plus-circle valignmiddle paddingleft" title="'.$langs->trans("AddWarehouse").'"></span>';
1519  print '</a>';
1520 
1521  print '</td>';
1522  print '</tr>';
1523 
1524  if (!getDolGlobalString('PRODUCT_DISABLE_STOCK_LEVELS')) {
1525  // Stock min level
1526  print '<tr><td>'.$form->textwithpicto($langs->trans("StockLimit"), $langs->trans("StockLimitDesc"), 1).'</td><td>';
1527  print '<input name="seuil_stock_alerte" class="maxwidth50" value="'.GETPOST('seuil_stock_alerte').'">';
1528  print '</td>';
1529  print '</tr>';
1530 
1531  // Stock desired level
1532  print '<tr><td>'.$form->textwithpicto($langs->trans("DesiredStock"), $langs->trans("DesiredStockDesc"), 1).'</td><td>';
1533  print '<input name="desiredstock" class="maxwidth50" value="'.GETPOST('desiredstock').'">';
1534  print '</td></tr>';
1535  }
1536  } else {
1537  if (!getDolGlobalString('PRODUCT_DISABLE_STOCK_LEVELS')) {
1538  print '<input name="seuil_stock_alerte" type="hidden" value="0">';
1539  print '<input name="desiredstock" type="hidden" value="0">';
1540  }
1541  }
1542 
1543  if ($type == $object::TYPE_SERVICE && isModEnabled("workstation")) {
1544  // Default workstation
1545  print '<tr><td>'.$langs->trans("DefaultWorkstation").'</td><td>';
1546  print img_picto($langs->trans("DefaultWorkstation"), 'workstation', 'class="pictofixedwidth"');
1547  print $formproduct->selectWorkstations($object->fk_default_workstation, 'fk_default_workstation', 1);
1548  print '</td></tr>';
1549  }
1550 
1551  // Duration
1552  if ($type == 1) {
1553  print '<tr><td>'.$langs->trans("Duration").'</td><td>';
1554  print img_picto('', 'clock', 'class="pictofixedwidth"');
1555  print '<input name="duration_value" size="4" value="'.GETPOSTINT('duration_value').'">';
1556  print $formproduct->selectMeasuringUnits("duration_unit", "time", (GETPOSTISSET('duration_value') ? GETPOST('duration_value', 'alpha') : 'h'), 0, 1);
1557 
1558  // Mandatory period
1559  print ' &nbsp; &nbsp; &nbsp; ';
1560  print '<input type="checkbox" id="mandatoryperiod" name="mandatoryperiod"'.($object->mandatory_period == 1 ? ' checked="checked"' : '').'>';
1561  print '<label for="mandatoryperiod">';
1562  $htmltooltip = $langs->trans("mandatoryHelper");
1563  print $form->textwithpicto($langs->trans("mandatoryperiod"), $htmltooltip, 1, 0);
1564  print '</label>';
1565 
1566  print '</td></tr>';
1567  }
1568 
1569  if ($type != 1) { // Nature, Weight and volume only applies to products and not to services
1570  if (!getDolGlobalString('PRODUCT_DISABLE_NATURE')) {
1571  // Nature
1572  print '<tr><td>'.$form->textwithpicto($langs->trans("NatureOfProductShort"), $langs->trans("NatureOfProductDesc")).'</td><td>';
1573  print $formproduct->selectProductNature('finished', $object->finished);
1574  print '</td></tr>';
1575  }
1576  }
1577 
1578  if ($type != 1) {
1579  if (!getDolGlobalString('PRODUCT_DISABLE_WEIGHT')) {
1580  // Brut Weight
1581  print '<tr><td>'.$langs->trans("Weight").'</td><td>';
1582  print img_picto('', 'fa-balance-scale', 'class="pictofixedwidth"');
1583  print '<input name="weight" size="4" value="'.GETPOST('weight').'">';
1584  print $formproduct->selectMeasuringUnits("weight_units", "weight", GETPOSTISSET('weight_units') ? GETPOST('weight_units', 'alpha') : (!getDolGlobalString('MAIN_WEIGHT_DEFAULT_UNIT') ? 0 : $conf->global->MAIN_WEIGHT_DEFAULT_UNIT), 0, 2);
1585  print '</td></tr>';
1586  }
1587 
1588  // Brut Length
1589  if (!getDolGlobalString('PRODUCT_DISABLE_SIZE')) {
1590  print '<tr><td>'.$langs->trans("Length").' x '.$langs->trans("Width").' x '.$langs->trans("Height").'</td><td>';
1591  print img_picto('', 'fa-ruler', 'class="pictofixedwidth"');
1592  print '<input name="size" class="width50" value="'.GETPOST('size').'"> x ';
1593  print '<input name="sizewidth" class="width50" value="'.GETPOST('sizewidth').'"> x ';
1594  print '<input name="sizeheight" class="width50" value="'.GETPOST('sizeheight').'">';
1595  print $formproduct->selectMeasuringUnits("size_units", "size", GETPOSTISSET('size_units') ? GETPOST('size_units', 'alpha') : '0', 0, 2);
1596  print '</td></tr>';
1597  }
1598  if (!getDolGlobalString('PRODUCT_DISABLE_SURFACE')) {
1599  // Brut Surface
1600  print '<tr><td>'.$langs->trans("Surface").'</td><td>';
1601  print '<input name="surface" size="4" value="'.GETPOST('surface').'">';
1602  print $formproduct->selectMeasuringUnits("surface_units", "surface", GETPOSTISSET('surface_units') ? GETPOST('surface_units', 'alpha') : '0', 0, 2);
1603  print '</td></tr>';
1604  }
1605  if (!getDolGlobalString('PRODUCT_DISABLE_VOLUME')) {
1606  // Brut Volume
1607  print '<tr><td>'.$langs->trans("Volume").'</td><td>';
1608  print '<input name="volume" size="4" value="'.GETPOST('volume').'">';
1609  print $formproduct->selectMeasuringUnits("volume_units", "volume", GETPOSTISSET('volume_units') ? GETPOST('volume_units', 'alpha') : '0', 0, 2);
1610  print '</td></tr>';
1611  }
1612 
1613  if (getDolGlobalString('PRODUCT_ADD_NET_MEASURE')) {
1614  // Net Measure
1615  print '<tr><td>'.$langs->trans("NetMeasure").'</td><td>';
1616  print '<input name="net_measure" size="4" value="'.GETPOST('net_measure').'">';
1617  print $formproduct->selectMeasuringUnits("net_measure_units", '', GETPOSTISSET('net_measure_units') ? GETPOST('net_measure_units', 'alpha') : (!getDolGlobalString('MAIN_WEIGHT_DEFAULT_UNIT') ? 0 : $conf->global->MAIN_WEIGHT_DEFAULT_UNIT), 0, 0);
1618  print '</td></tr>';
1619  }
1620  }
1621 
1622  // Units
1623  if (getDolGlobalString('PRODUCT_USE_UNITS')) {
1624  print '<tr><td>'.$langs->trans('DefaultUnitToShow').'</td>';
1625  print '<td>';
1626  print $form->selectUnits(empty($line->fk_unit) ? $conf->global->PRODUCT_USE_UNITS : $line->fk_unit, 'units');
1627  print '</td></tr>';
1628  }
1629 
1630  // Custom code
1631  if (!getDolGlobalString('PRODUCT_DISABLE_CUSTOM_INFO') && empty($type)) {
1632  print '<tr><td class="wordbreak">'.$langs->trans("CustomCode").'</td><td><input name="customcode" class="maxwidth100onsmartphone" value="'.GETPOST('customcode').'"></td></tr>';
1633 
1634  // Origin country
1635  print '<tr><td>'.$langs->trans("CountryOrigin").'</td>';
1636  print '<td>';
1637  print img_picto('', 'globe-americas', 'class="pictofixedwidth"');
1638  print $form->select_country((GETPOSTISSET('country_id') ? GETPOST('country_id') : $object->country_id), 'country_id', '', 0, 'minwidth300 widthcentpercentminusx maxwidth500');
1639  if ($user->admin) {
1640  print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
1641  }
1642  print '</td></tr>';
1643 
1644  // State
1645  if (!getDolGlobalString('PRODUCT_DISABLE_STATE')) {
1646  print '<tr>';
1647  if (getDolGlobalString('MAIN_SHOW_REGION_IN_STATE_SELECT') && (getDolGlobalInt('MAIN_SHOW_REGION_IN_STATE_SELECT') == 1 || getDolGlobalInt('MAIN_SHOW_REGION_IN_STATE_SELECT') == 2)) {
1648  print '<td>'.$form->editfieldkey('RegionStateOrigin', 'state_id', '', $object, 0).'</td><td>';
1649  } else {
1650  print '<td>'.$form->editfieldkey('StateOrigin', 'state_id', '', $object, 0).'</td><td>';
1651  }
1652 
1653  print img_picto('', 'state', 'class="pictofixedwidth"');
1654  print $formcompany->select_state($object->state_id, $object->country_code);
1655  print '</tr>';
1656  }
1657  }
1658 
1659  // Quality control
1660  if (getDolGlobalString('PRODUCT_LOT_ENABLE_QUALITY_CONTROL')) {
1661  print '<tr><td>'.$langs->trans("LifeTime").'</td><td><input name="lifetime" class="maxwidth50" value="'.GETPOST('lifetime').'"></td></tr>';
1662  print '<tr><td>'.$langs->trans("QCFrequency").'</td><td><input name="qc_frequency" class="maxwidth50" value="'.GETPOST('qc_frequency').'"></td></tr>';
1663  }
1664 
1665  // Other attributes
1666  $parameters = array('colspan' => ' colspan="2"', 'cols' => 2);
1667  $reshook = $hookmanager->executeHooks('formObjectOptions', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
1668  print $hookmanager->resPrint;
1669  if (empty($reshook)) {
1670  print $object->showOptionals($extrafields, 'create', $parameters);
1671  }
1672 
1673  // Note (private, no output on invoices, propales...)
1674  //if (!empty($conf->global->MAIN_DISABLE_NOTES_TAB)) available in create mode
1675  //{
1676  print '<tr><td class="tdtop">'.$langs->trans("NoteNotVisibleOnBill").'</td><td>';
1677 
1678  // We use dolibarr_details as type of DolEditor here, because we must not accept images as description is included into PDF and not accepted by TCPDF.
1679  $doleditor = new DolEditor('note_private', GETPOST('note_private', 'restricthtml'), '', 140, 'dolibarr_details', '', false, true, getDolGlobalString('FCKEDITOR_ENABLE_NOTE_PRIVATE'), ROWS_8, '90%');
1680  $doleditor->Create();
1681 
1682  print "</td></tr>";
1683  //}
1684 
1685  if (isModEnabled('category')) {
1686  // Categories
1687  print '<tr><td>'.$langs->trans("Categories").'</td><td>';
1688  $cate_arbo = $form->select_all_categories(Categorie::TYPE_PRODUCT, '', 'parent', 64, 0, 3);
1689  print img_picto('', 'category', 'class="pictofixedwidth"').$form->multiselectarray('categories', $cate_arbo, GETPOST('categories', 'array'), '', 0, 'quatrevingtpercent widthcentpercentminusx', 0, 0);
1690  print "</td></tr>";
1691  }
1692 
1693  print '</table>';
1694 
1695  print '<hr>';
1696 
1697  if (!getDolGlobalString('PRODUCT_DISABLE_PRICES')) {
1698  if (getDolGlobalString('PRODUIT_MULTIPRICES')) {
1699  // We do no show price array on create when multiprices enabled.
1700  // We must set them on prices tab.
1701  print '<table class="border centpercent">';
1702  // VAT
1703  print '<tr><td class="titlefieldcreate">'.$langs->trans("VATRate").'</td><td>';
1704  $defaultva = get_default_tva($mysoc, $mysoc);
1705  print $form->load_tva("tva_tx", $defaultva, $mysoc, $mysoc, 0, 0, '', false, 1);
1706  print '</td></tr>';
1707 
1708  print '</table>';
1709 
1710  print '<br>';
1711  } else {
1712  print '<table class="border centpercent">';
1713 
1714  // Price
1715  print '<tr><td class="titlefieldcreate">'.$langs->trans("SellingPrice").'</td>';
1716  print '<td><input name="price" class="maxwidth50" value="'.$object->price.'">';
1717  print $form->selectPriceBaseType($conf->global->PRODUCT_PRICE_BASE_TYPE, "price_base_type");
1718  print '</td></tr>';
1719 
1720  // Min price
1721  print '<tr><td>'.$langs->trans("MinPrice").'</td>';
1722  print '<td><input name="price_min" class="maxwidth50" value="'.$object->price_min.'">';
1723  print '</td></tr>';
1724 
1725  // VAT
1726  print '<tr><td>'.$langs->trans("VATRate").'</td><td>';
1727  $defaultva = get_default_tva($mysoc, $mysoc);
1728  print $form->load_tva("tva_tx", $defaultva, $mysoc, $mysoc, 0, 0, '', false, 1);
1729  print '</td></tr>';
1730 
1731  print '</table>';
1732 
1733  print '<br>';
1734  }
1735  }
1736 
1737  // Accountancy codes
1738  print '<!-- accountancy codes -->'."\n";
1739  print '<table class="border centpercent">';
1740 
1741  if (!getDolGlobalString('PRODUCT_DISABLE_ACCOUNTING')) {
1742  if (isModEnabled('accounting')) {
1743  // Accountancy_code_sell
1744  print '<tr><td class="titlefieldcreate">'.$langs->trans("ProductAccountancySellCode").'</td>';
1745  print '<td>';
1746  if ($type == 0) {
1747  $accountancy_code_sell = (GETPOSTISSET('accountancy_code_sell') ? GETPOST('accountancy_code_sell', 'alpha') : getDolGlobalString("ACCOUNTING_PRODUCT_SOLD_ACCOUNT"));
1748  } else {
1749  $accountancy_code_sell = (GETPOSTISSET('accountancy_code_sell') ? GETPOST('accountancy_code_sell', 'alpha') : getDolGlobalString("ACCOUNTING_SERVICE_SOLD_ACCOUNT"));
1750  }
1751  print $formaccounting->select_account($accountancy_code_sell, 'accountancy_code_sell', 1, null, 1, 1, 'minwidth150 maxwidth300', 1);
1752  print '</td></tr>';
1753 
1754  // Accountancy_code_sell_intra
1755  if ($mysoc->isInEEC()) {
1756  print '<tr><td class="titlefieldcreate">'.$langs->trans("ProductAccountancySellIntraCode").'</td>';
1757  print '<td>';
1758  if ($type == 0) {
1759  $accountancy_code_sell_intra = (GETPOSTISSET('accountancy_code_sell_intra') ? GETPOST('accountancy_code_sell_intra', 'alpha') : getDolGlobalString("ACCOUNTING_PRODUCT_SOLD_INTRA_ACCOUNT"));
1760  } else {
1761  $accountancy_code_sell_intra = (GETPOSTISSET('accountancy_code_sell_intra') ? GETPOST('accountancy_code_sell_intra', 'alpha') : getDolGlobalString("ACCOUNTING_SERVICE_SOLD_INTRA_ACCOUNT"));
1762  }
1763  print $formaccounting->select_account($accountancy_code_sell_intra, 'accountancy_code_sell_intra', 1, null, 1, 1, 'minwidth150 maxwidth300', 1);
1764  print '</td></tr>';
1765  }
1766 
1767  // Accountancy_code_sell_export
1768  print '<tr><td class="titlefieldcreate">'.$langs->trans("ProductAccountancySellExportCode").'</td>';
1769  print '<td>';
1770  if ($type == 0) {
1771  $accountancy_code_sell_export = (GETPOST('accountancy_code_sell_export') ? GETPOST('accountancy_code_sell_export', 'alpha') : getDolGlobalString("ACCOUNTING_PRODUCT_SOLD_EXPORT_ACCOUNT"));
1772  } else {
1773  $accountancy_code_sell_export = (GETPOST('accountancy_code_sell_export') ? GETPOST('accountancy_code_sell_export', 'alpha') : getDolGlobalString("ACCOUNTING_SERVICE_SOLD_EXPORT_ACCOUNT"));
1774  }
1775  print $formaccounting->select_account($accountancy_code_sell_export, 'accountancy_code_sell_export', 1, null, 1, 1, 'minwidth150 maxwidth300', 1);
1776  print '</td></tr>';
1777 
1778  // Accountancy_code_buy
1779  print '<tr><td>'.$langs->trans("ProductAccountancyBuyCode").'</td>';
1780  print '<td>';
1781  if ($type == 0) {
1782  $accountancy_code_buy = (GETPOST('accountancy_code_buy', 'alpha') ? (GETPOST('accountancy_code_buy', 'alpha')) : getDolGlobalString("ACCOUNTING_PRODUCT_BUY_ACCOUNT"));
1783  } else {
1784  $accountancy_code_buy = (GETPOST('accountancy_code_buy', 'alpha') ? (GETPOST('accountancy_code_buy', 'alpha')) : getDolGlobalString("ACCOUNTING_SERVICE_BUY_ACCOUNT"));
1785  }
1786  print $formaccounting->select_account($accountancy_code_buy, 'accountancy_code_buy', 1, null, 1, 1, 'minwidth150 maxwidth300', 1);
1787  print '</td></tr>';
1788 
1789  // Accountancy_code_buy_intra
1790  if ($mysoc->isInEEC()) {
1791  print '<tr><td class="titlefieldcreate">'.$langs->trans("ProductAccountancyBuyIntraCode").'</td>';
1792  print '<td>';
1793  if ($type == 0) {
1794  $accountancy_code_buy_intra = (GETPOSTISSET('accountancy_code_buy_intra') ? GETPOST('accountancy_code_buy_intra', 'alpha') : getDolGlobalString("ACCOUNTING_PRODUCT_BUY_INTRA_ACCOUNT"));
1795  } else {
1796  $accountancy_code_buy_intra = (GETPOSTISSET('accountancy_code_buy_intra') ? GETPOST('accountancy_code_buy_intra', 'alpha') : getDolGlobalString("ACCOUNTING_SERVICE_BUY_INTRA_ACCOUNT"));
1797  }
1798  print $formaccounting->select_account($accountancy_code_buy_intra, 'accountancy_code_buy_intra', 1, null, 1, 1, 'minwidth150 maxwidth300', 1);
1799  print '</td></tr>';
1800  }
1801 
1802  // Accountancy_code_buy_export
1803  print '<tr><td class="titlefieldcreate">'.$langs->trans("ProductAccountancyBuyExportCode").'</td>';
1804  print '<td>';
1805  if ($type == 0) {
1806  $accountancy_code_buy_export = (GETPOST('accountancy_code_buy_export') ? GETPOST('accountancy_code_buy_export', 'alpha') : getDolGlobalString("ACCOUNTING_PRODUCT_BUY_EXPORT_ACCOUNT"));
1807  } else {
1808  $accountancy_code_buy_export = (GETPOST('accountancy_code_buy_export') ? GETPOST('accountancy_code_buy_export', 'alpha') : getDolGlobalString("ACCOUNTING_SERVICE_BUY_EXPORT_ACCOUNT"));
1809  }
1810  print $formaccounting->select_account($accountancy_code_buy_export, 'accountancy_code_buy_export', 1, null, 1, 1, 'minwidth150 maxwidth300', 1);
1811  print '</td></tr>';
1812  } else {// For external software
1813  if (!empty($accountancy_code_sell)) {
1814  $object->accountancy_code_sell = $accountancy_code_sell;
1815  }
1816  if (!empty($accountancy_code_sell_intra)) {
1817  $object->accountancy_code_sell_intra = $accountancy_code_sell_intra;
1818  }
1819  if (!empty($accountancy_code_sell_export)) {
1820  $object->accountancy_code_sell_export = $accountancy_code_sell_export;
1821  }
1822  if (!empty($accountancy_code_buy)) {
1823  $object->accountancy_code_buy = $accountancy_code_buy;
1824  }
1825  if (!empty($accountancy_code_buy_intra)) {
1826  $object->accountancy_code_buy_intra = $accountancy_code_buy_intra;
1827  }
1828  if (!empty($accountancy_code_buy_export)) {
1829  $object->accountancy_code_buy_export = $accountancy_code_buy_export;
1830  }
1831 
1832  // Accountancy_code_sell
1833  print '<tr><td class="titlefieldcreate">'.$langs->trans("ProductAccountancySellCode").'</td>';
1834  print '<td class="maxwidthonsmartphone"><input class="minwidth150" name="accountancy_code_sell" value="'.$object->accountancy_code_sell.'">';
1835  print '</td></tr>';
1836 
1837  // Accountancy_code_sell_intra
1838  if ($mysoc->isInEEC()) {
1839  print '<tr><td class="titlefieldcreate">'.$langs->trans("ProductAccountancySellIntraCode").'</td>';
1840  print '<td class="maxwidthonsmartphone"><input class="minwidth150" name="accountancy_code_sell_intra" value="'.$object->accountancy_code_sell_intra.'">';
1841  print '</td></tr>';
1842  }
1843 
1844  // Accountancy_code_sell_export
1845  print '<tr><td class="titlefieldcreate">'.$langs->trans("ProductAccountancySellExportCode").'</td>';
1846  print '<td class="maxwidthonsmartphone"><input class="minwidth150" name="accountancy_code_sell_export" value="'.$object->accountancy_code_sell_export.'">';
1847  print '</td></tr>';
1848 
1849  // Accountancy_code_buy
1850  print '<tr><td>'.$langs->trans("ProductAccountancyBuyCode").'</td>';
1851  print '<td class="maxwidthonsmartphone"><input class="minwidth150" name="accountancy_code_buy" value="'.$object->accountancy_code_buy.'">';
1852  print '</td></tr>';
1853 
1854  // Accountancy_code_buy_intra
1855  if ($mysoc->isInEEC()) {
1856  print '<tr><td class="titlefieldcreate">'.$langs->trans("ProductAccountancyBuyIntraCode").'</td>';
1857  print '<td class="maxwidthonsmartphone"><input class="minwidth150" name="accountancy_code_buy_intra" value="'.$object->accountancy_code_buy_intra.'">';
1858  print '</td></tr>';
1859  }
1860 
1861  // Accountancy_code_buy_export
1862  print '<tr><td class="titlefieldcreate">'.$langs->trans("ProductAccountancyBuyExportCode").'</td>';
1863  print '<td class="maxwidthonsmartphone"><input class="minwidth150" name="accountancy_code_buy_export" value="'.$object->accountancy_code_buy_export.'">';
1864  print '</td></tr>';
1865  }
1866  }
1867  print '</table>';
1868  }
1869 
1870  print dol_get_fiche_end();
1871 
1872  print $form->buttonsSaveCancel("Create");
1873 
1874  print '</form>';
1875  } elseif ($object->id > 0) {
1876  /*
1877  * Product card
1878  */
1879 
1880  // Card in edit mode
1881  if ($action == 'edit' && $usercancreate) {
1882  //WYSIWYG Editor
1883  require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
1884 
1885  if (!empty($conf->use_javascript_ajax)) {
1886  print '<script type="text/javascript">';
1887  print '$(document).ready(function () {
1888  $("#selectcountry_id").change(function () {
1889  document.formprod.action.value="edit";
1890  document.formprod.submit();
1891  });
1892  });';
1893  print '</script>'."\n";
1894  }
1895 
1896  // We set country_id, country_code and country for the selected country
1897  $object->country_id = GETPOST('country_id') ? GETPOST('country_id') : $object->country_id;
1898  if ($object->country_id) {
1899  $tmparray = getCountry($object->country_id, 'all');
1900  $object->country_code = $tmparray['code'];
1901  $object->country = $tmparray['label'];
1902  }
1903 
1904  $type = $langs->trans('Product');
1905  if ($object->isService()) {
1906  $type = $langs->trans('Service');
1907  }
1908  // print load_fiche_titre($langs->trans('Modify').' '.$type.' : '.(is_object($object->oldcopy)?$object->oldcopy->ref:$object->ref), "");
1909 
1910  // Main official, simple, and not duplicated code
1911  print '<form action="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'" method="POST" name="formprod">'."\n";
1912  print '<input type="hidden" name="token" value="'.newToken().'">';
1913  print '<input type="hidden" name="action" value="update">';
1914  print '<input type="hidden" name="id" value="'.$object->id.'">';
1915  print '<input type="hidden" name="canvas" value="'.$object->canvas.'">';
1916 
1917  $head = product_prepare_head($object);
1918  $titre = $langs->trans("CardProduct".$object->type);
1919  $picto = ($object->type == Product::TYPE_SERVICE ? 'service' : 'product');
1920  print dol_get_fiche_head($head, 'card', $titre, 0, $picto);
1921 
1922  // Call Hook tabContentEditProduct
1923  $parameters = array();
1924  // Note that $action and $object may be modified by hook
1925  $reshook = $hookmanager->executeHooks('tabContentEditProduct', $parameters, $object, $action);
1926 
1927  if (empty($reshook)) {
1928  print '<table class="border allwidth">';
1929 
1930  // Ref
1931  if (!getDolGlobalString('MAIN_PRODUCT_REF_NOT_EDITABLE')) {
1932  print '<tr><td class="titlefieldcreate fieldrequired">'.$langs->trans("Ref").'</td><td colspan="3"><input name="ref" class="maxwidth200" maxlength="128" value="'.dol_escape_htmltag(GETPOSTISSET('ref') ? GETPOST('ref') : $object->ref).'"></td></tr>';
1933  } else {
1934  print '<tr><td class="titlefieldcreate fieldrequired">'.$langs->trans("Ref").'</td><td colspan="3"><input name="ref" class="maxwidth200" maxlength="128" value="'.dol_escape_htmltag($object->ref).'" readonly="true"></td></tr>';
1935  }
1936 
1937  // Label
1938  print '<tr><td class="fieldrequired">'.$langs->trans("Label").'</td><td colspan="3"><input name="label" class="minwidth300 maxwidth400onsmartphone" maxlength="255" value="'.dol_escape_htmltag(GETPOSTISSET('label') ? GETPOST('label') : $object->label).'"></td></tr>';
1939 
1940  // Status To sell
1941  print '<tr><td class="fieldrequired">'.$langs->trans("Status").' ('.$langs->trans("Sell").')</td><td colspan="3">';
1942  print '<select class="flat" name="statut">';
1943  if ((GETPOSTISSET('statut') && GETPOST('statut')) || (!GETPOSTISSET('statut') && $object->status)) {
1944  print '<option value="1" selected>'.$langs->trans("OnSell").'</option>';
1945  print '<option value="0">'.$langs->trans("NotOnSell").'</option>';
1946  } else {
1947  print '<option value="1">'.$langs->trans("OnSell").'</option>';
1948  print '<option value="0" selected>'.$langs->trans("NotOnSell").'</option>';
1949  }
1950  print '</select>';
1951  print '</td></tr>';
1952 
1953  // Status To Buy
1954  print '<tr><td class="fieldrequired">'.$langs->trans("Status").' ('.$langs->trans("Buy").')</td><td colspan="3">';
1955  print '<select class="flat" name="statut_buy">';
1956  if ((GETPOSTISSET('statut_buy') && GETPOST('statut_buy')) || (!GETPOSTISSET('statut_buy') && $object->status_buy)) {
1957  print '<option value="1" selected>'.$langs->trans("ProductStatusOnBuy").'</option>';
1958  print '<option value="0">'.$langs->trans("ProductStatusNotOnBuy").'</option>';
1959  } else {
1960  print '<option value="1">'.$langs->trans("ProductStatusOnBuy").'</option>';
1961  print '<option value="0" selected>'.$langs->trans("ProductStatusNotOnBuy").'</option>';
1962  }
1963  print '</select>';
1964  print '</td></tr>';
1965 
1966  // Batch number management
1967  if (isModEnabled('productbatch')) {
1968  if ($object->isProduct() || getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
1969  print '<tr><td>'.$langs->trans("ManageLotSerial").'</td><td>';
1970  $statutarray = array('0' => $langs->trans("ProductStatusNotOnBatch"), '1' => $langs->trans("ProductStatusOnBatch"), '2' => $langs->trans("ProductStatusOnSerial"));
1971 
1972  print $form->selectarray('status_batch', $statutarray, GETPOSTISSET('status_batch') ? GETPOST('status_batch') : $object->status_batch);
1973 
1974  print '<span id="statusBatchWarning" class="warning" style="display: none;">';
1975  print img_warning().'&nbsp;'.$langs->trans("WarningConvertFromBatchToSerial").'</span>';
1976 
1977  print '<span id="statusBatchMouvToGlobal" class="warning" style="display: none;">';
1978  print img_warning().'&nbsp;'.$langs->trans("WarningTransferBatchStockMouvToGlobal").'</span>';
1979 
1980  if ($object->status_batch) {
1981  // Display message to make user know that all batch will be move into global stock
1982  print '<script type="text/javascript">
1983  $(document).ready(function() {
1984  console.log($("#statusBatchWarning"))
1985  $("#status_batch").on("change", function() {
1986  if ($("#status_batch")[0].value == 0){
1987  $("#statusBatchMouvToGlobal").show()
1988  } else {
1989  $("#statusBatchMouvToGlobal").hide()
1990  }
1991  })
1992  })</script>';
1993 
1994  // Display message to explain that if the product currently have a quantity higher or equal to 2, switching to this choice means we will still have a product with different objects of the same batch (while we want a unique serial number)
1995  if ($object->status_batch == 1) {
1996  print '<script type="text/javascript">
1997  $(document).ready(function() {
1998  console.log($("#statusBatchWarning"))
1999  $("#status_batch").on("change", function() {
2000  if ($("#status_batch")[0].value == 2){
2001  $("#statusBatchWarning").show()
2002  } else {
2003  $("#statusBatchWarning").hide()
2004  }
2005  })
2006  })</script>';
2007  }
2008  }
2009 
2010  print '</td></tr>';
2011  if (!empty($object->status_batch) || !empty($conf->use_javascript_ajax)) {
2012  $langs->load("admin");
2013  $tooltip = $langs->trans("GenericMaskCodes", $langs->transnoentities("Batch"), $langs->transnoentities("Batch"));
2014  $tooltip .= '<br>'.$langs->trans("GenericMaskCodes2");
2015  $tooltip .= '<br>'.$langs->trans("GenericMaskCodes3");
2016  $tooltip .= '<br>'.$langs->trans("GenericMaskCodes4a", $langs->transnoentities("Batch"), $langs->transnoentities("Batch"));
2017  $tooltip .= '<br>'.$langs->trans("GenericMaskCodes5");
2018  print '<tr><td id="mask_option">'.$langs->trans("ManageLotMask").'</td>';
2019  $mask = '';
2020  if ($object->status_batch == '1' && getDolGlobalString('PRODUCTBATCH_LOT_USE_PRODUCT_MASKS') && getDolGlobalString('PRODUCTBATCH_LOT_ADDON') == 'mod_lot_advanced') {
2021  $mask = !empty($object->batch_mask) ? $object->batch_mask : getDolGlobalString('LOT_ADVANCED_MASK');
2022  }
2023  if ($object->status_batch == '2' && getDolGlobalString('PRODUCTBATCH_SN_USE_PRODUCT_MASKS') && getDolGlobalString('PRODUCTBATCH_SN_ADDON') == 'mod_sn_advanced') {
2024  $mask = !empty($object->batch_mask) ? $object->batch_mask : getDolGlobalString('SN_ADVANCED_MASK');
2025  }
2026  $inherited_mask_lot = getDolGlobalString('LOT_ADVANCED_MASK');
2027  $inherited_mask_sn = getDolGlobalString('SN_ADVANCED_MASK');
2028  print '<td id="field_mask">';
2029  print $form->textwithpicto('<input type="text" class="flat minwidth175" name="batch_mask" id="batch_mask_input" value="'.$mask.'">', $tooltip, 1, 1);
2030  // Add javascript to sho/hide field for custom mask
2031  if (!empty($conf->use_javascript_ajax)) {
2032  print '<script type="text/javascript">
2033  $(document).ready(function() {
2034  $("#field_mask").parent().addClass("hideobject");
2035  var preselect = document.getElementById("status_batch");';
2036  if (getDolGlobalString('PRODUCTBATCH_SN_USE_PRODUCT_MASKS')) {
2037  print 'if (preselect.value == "2") {
2038  $("#field_mask").parent().removeClass("hideobject");
2039  }';
2040  }
2041  if (getDolGlobalString('PRODUCTBATCH_LOT_USE_PRODUCT_MASKS')) {
2042  print 'if (preselect.value == "1") {
2043  $("#field_mask").parent().removeClass("hideobject");
2044  }';
2045  }
2046  print '$("#status_batch").on("change", function () {
2047  var optionSelected = $("option:selected", this);
2048  var valueSelected = this.value;
2049  $("#field_mask").parent().addClass("hideobject");
2050  ';
2051  if (getDolGlobalString('PRODUCTBATCH_LOT_USE_PRODUCT_MASKS') && getDolGlobalString('PRODUCTBATCH_LOT_ADDON') == 'mod_lot_advanced') {
2052  print '
2053  if (this.value == 1) {
2054  $("#field_mask").parent().removeClass("hideobject");
2055  $("#batch_mask_input").val("'.$inherited_mask_lot.'");
2056  }
2057  ';
2058  }
2059  if (getDolGlobalString('PRODUCTBATCH_SN_USE_PRODUCT_MASKS') && getDolGlobalString('PRODUCTBATCH_SN_ADDON') == 'mod_sn_advanced') {
2060  print '
2061  if (this.value == 2) {
2062  $("#field_mask").parent().removeClass("hideobject");
2063  $("#batch_mask_input").val("'.$inherited_mask_sn.'");
2064  }
2065  ';
2066  }
2067  print '
2068  })
2069  })
2070  </script>';
2071  }
2072  print '</td></tr>';
2073  }
2074  }
2075 
2076  // SellBy / EatBy mandatory list
2077  if (!empty($sellOrEatByMandatoryList)) {
2078  if (GETPOSTISSET('sell_or_eat_by_mandatory')) {
2079  $sellOrEatByMandatorySelectedId = GETPOSTINT('sell_or_eat_by_mandatory');
2080  } else {
2081  $sellOrEatByMandatorySelectedId = $object->sell_or_eat_by_mandatory;
2082  }
2083  print '<tr><td>'.$langs->trans('BatchSellOrEatByMandatoryList', $langs->trans('SellByDate'), $langs->trans('EatByDate')).'</td><td>';
2084  print $form->selectarray('sell_or_eat_by_mandatory', $sellOrEatByMandatoryList, $sellOrEatByMandatorySelectedId);
2085  print '</td></tr>';
2086  }
2087  }
2088 
2089  // Barcode
2090  $showbarcode = isModEnabled('barcode');
2091  if (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && !$user->hasRight('barcode', 'lire_advance')) {
2092  $showbarcode = 0;
2093  }
2094 
2095  if ($showbarcode) {
2096  print '<tr><td>'.$langs->trans('BarcodeType').'</td><td>';
2097  if (GETPOSTISSET('fk_barcode_type')) {
2098  $fk_barcode_type = GETPOST('fk_barcode_type');
2099  } else {
2100  $fk_barcode_type = $object->barcode_type;
2101  if (empty($fk_barcode_type) && getDolGlobalString('PRODUIT_DEFAULT_BARCODE_TYPE')) {
2102  $fk_barcode_type = getDolGlobalString('PRODUIT_DEFAULT_BARCODE_TYPE');
2103  }
2104  }
2105  require_once DOL_DOCUMENT_ROOT.'/core/class/html.formbarcode.class.php';
2106  $formbarcode = new FormBarCode($db);
2107  print $formbarcode->selectBarcodeType($fk_barcode_type, 'fk_barcode_type', 1);
2108  print '</td></tr>';
2109  print '<tr><td>'.$langs->trans("BarcodeValue").'</td><td>';
2110  $tmpcode = GETPOSTISSET('barcode') ? GETPOST('barcode') : $object->barcode;
2111  if (empty($tmpcode) && !empty($modBarCodeProduct->code_auto)) {
2112  $tmpcode = $modBarCodeProduct->getNextValue($object, $fk_barcode_type);
2113  }
2114  print '<input class="maxwidth150 maxwidthonsmartphone" type="text" name="barcode" value="'.dol_escape_htmltag($tmpcode).'">';
2115  print '</td></tr>';
2116  }
2117 
2118  // Description (used in invoice, propal...)
2119  print '<tr><td class="tdtop">'.$langs->trans("Description").'</td><td>';
2120 
2121  // We use dolibarr_details as type of DolEditor here, because we must not accept images as description is included into PDF and not accepted by TCPDF.
2122  $doleditor = new DolEditor('desc', GETPOSTISSET('desc') ? GETPOST('desc', 'restricthtml') : $object->description, '', 160, 'dolibarr_details', '', false, true, getDolGlobalInt('FCKEDITOR_ENABLE_DETAILS'), ROWS_4, '90%');
2123  $doleditor->Create();
2124 
2125  print "</td></tr>";
2126  print "\n";
2127 
2128  // Public Url
2129  if (!getDolGlobalString('PRODUCT_DISABLE_PUBLIC_URL')) {
2130  print '<tr><td>'.$langs->trans("PublicUrl").'</td><td>';
2131  print img_picto('', 'globe', 'class="pictofixedwidth"');
2132  print '<input type="text" name="url" class="maxwidth500 widthcentpercentminusx" value="'.(GETPOSTISSET('url') ? GETPOST('url') : $object->url).'">';
2133  print '</td></tr>';
2134  }
2135 
2136  // Stock
2137  if (($object->isProduct() || getDolGlobalInt('STOCK_SUPPORTS_SERVICES')) && isModEnabled('stock')) {
2138  // Default warehouse
2139  print '<tr><td>'.$langs->trans("DefaultWarehouse").'</td><td>';
2140  print img_picto($langs->trans("DefaultWarehouse"), 'stock', 'class="pictofixedwidth"');
2141  print $formproduct->selectWarehouses((GETPOSTISSET('fk_default_warehouse') ? GETPOST('fk_default_warehouse') : $object->fk_default_warehouse), 'fk_default_warehouse', 'warehouseopen', 1, 0, 0, '', 0, 0, array(), 'maxwidth500 widthcentpercentminusxx');
2142  print ' <a href="'.DOL_URL_ROOT.'/product/stock/card.php?action=create&amp;backtopage='.urlencode($_SERVER['PHP_SELF'].'?action=edit&id='.((int) $object->id)).'">';
2143  print '<span class="fa fa-plus-circle valignmiddle paddingleft" title="'.$langs->trans("AddWarehouse").'"></span></a>';
2144  print '</td></tr>';
2145  /*
2146  print "<tr>".'<td>'.$langs->trans("StockLimit").'</td><td>';
2147  print '<input name="seuil_stock_alerte" size="4" value="'.$object->seuil_stock_alerte.'">';
2148  print '</td>';
2149 
2150  print '<td>'.$langs->trans("DesiredStock").'</td><td>';
2151  print '<input name="desiredstock" size="4" value="'.$object->desiredstock.'">';
2152  print '</td></tr>';
2153  */
2154  }
2155 
2156  if ($object->isService() && isModEnabled('workstation')) {
2157  // Default workstation
2158  print '<tr><td>'.$langs->trans("DefaultWorkstation").'</td><td>';
2159  print img_picto($langs->trans("DefaultWorkstation"), 'workstation', 'class="pictofixedwidth"');
2160  print $formproduct->selectWorkstations($object->fk_default_workstation, 'fk_default_workstation', 1);
2161  print '</td></tr>';
2162  }
2163 
2164  /*
2165  else
2166  {
2167  print '<input name="seuil_stock_alerte" type="hidden" value="'.$object->seuil_stock_alerte.'">';
2168  print '<input name="desiredstock" type="hidden" value="'.$object->desiredstock.'">';
2169  }*/
2170 
2171  if ($object->isService()) {
2172  // Duration
2173  print '<tr><td>'.$langs->trans("Duration").'</td><td>';
2174  print '<input name="duration_value" size="5" value="'.$object->duration_value.'"> ';
2175  print $formproduct->selectMeasuringUnits("duration_unit", "time", $object->duration_unit, 0, 1);
2176 
2177  // Mandatory period
2178  print ' &nbsp; &nbsp; &nbsp; ';
2179  print '<input type="checkbox" id="mandatoryperiod" name="mandatoryperiod"'.($object->mandatory_period == 1 ? ' checked="checked"' : '').'>';
2180  print '<label for="mandatoryperiod">';
2181  $htmltooltip = $langs->trans("mandatoryHelper");
2182  print $form->textwithpicto($langs->trans("mandatoryperiod"), $htmltooltip, 1, 0);
2183  print '</label>';
2184 
2185  print '</td></tr>';
2186  } else {
2187  if (!getDolGlobalString('PRODUCT_DISABLE_NATURE')) {
2188  // Nature
2189  print '<tr><td>'.$form->textwithpicto($langs->trans("NatureOfProductShort"), $langs->trans("NatureOfProductDesc")).'</td><td>';
2190  print $formproduct->selectProductNature('finished', (GETPOSTISSET('finished') ? GETPOST('finished') : $object->finished));
2191  print '</td></tr>';
2192  }
2193  }
2194 
2195  if (!$object->isService() && isModEnabled('bom')) {
2196  print '<tr><td>'.$form->textwithpicto($langs->trans("DefaultBOM"), $langs->trans("DefaultBOMDesc", $langs->transnoentitiesnoconv("Finished"))).'</td><td>';
2197  $bomkey = "Bom:bom/class/bom.class.php:0:(t.status:=:1) AND (t.fk_product:=:".((int) $object->id).')';
2198  print $form->selectForForms($bomkey, 'fk_default_bom', (GETPOSTISSET('fk_default_bom') ? GETPOST('fk_default_bom') : $object->fk_default_bom), 1);
2199  print '</td></tr>';
2200  }
2201 
2202  if (!$object->isService()) {
2203  if (!getDolGlobalString('PRODUCT_DISABLE_WEIGHT')) {
2204  // Brut Weight
2205  print '<tr><td>'.$langs->trans("Weight").'</td><td>';
2206  print '<input name="weight" size="5" value="'.(GETPOSTISSET('weight') ? GETPOST('weight') : $object->weight).'"> ';
2207  print $formproduct->selectMeasuringUnits("weight_units", "weight", GETPOSTISSET('weight_units') ? GETPOST('weight_units') : $object->weight_units, 0, 2);
2208  print '</td></tr>';
2209  }
2210 
2211  if (!getDolGlobalString('PRODUCT_DISABLE_SIZE')) {
2212  // Brut Length
2213  print '<tr><td>'.$langs->trans("Length").' x '.$langs->trans("Width").' x '.$langs->trans("Height").'</td><td>';
2214  print '<input name="size" size="5" value="'.(GETPOSTISSET('size') ? GETPOST('size') : $object->length).'">x';
2215  print '<input name="sizewidth" size="5" value="'.(GETPOSTISSET('sizewidth') ? GETPOST('sizewidth') : $object->width).'">x';
2216  print '<input name="sizeheight" size="5" value="'.(GETPOSTISSET('sizeheight') ? GETPOST('sizeheight') : $object->height).'"> ';
2217  print $formproduct->selectMeasuringUnits("size_units", "size", GETPOSTISSET('size_units') ? GETPOST('size_units') : $object->length_units, 0, 2);
2218  print '</td></tr>';
2219  }
2220  if (!getDolGlobalString('PRODUCT_DISABLE_SURFACE')) {
2221  // Brut Surface
2222  print '<tr><td>'.$langs->trans("Surface").'</td><td>';
2223  print '<input name="surface" size="5" value="'.(GETPOSTISSET('surface') ? GETPOST('surface') : $object->surface).'"> ';
2224  print $formproduct->selectMeasuringUnits("surface_units", "surface", GETPOSTISSET('surface_units') ? GETPOST('surface_units') : $object->surface_units, 0, 2);
2225  print '</td></tr>';
2226  }
2227  if (!getDolGlobalString('PRODUCT_DISABLE_VOLUME')) {
2228  // Brut Volume
2229  print '<tr><td>'.$langs->trans("Volume").'</td><td>';
2230  print '<input name="volume" size="5" value="'.(GETPOSTISSET('volume') ? GETPOST('volume') : $object->volume).'"> ';
2231  print $formproduct->selectMeasuringUnits("volume_units", "volume", GETPOSTISSET('volume_units') ? GETPOST('volume_units') : $object->volume_units, 0, 2);
2232  print '</td></tr>';
2233  }
2234 
2235  if (getDolGlobalString('PRODUCT_ADD_NET_MEASURE')) {
2236  // Net Measure
2237  print '<tr><td>'.$langs->trans("NetMeasure").'</td><td>';
2238  print '<input name="net_measure" size="5" value="'.(GETPOSTISSET('net_measure') ? GETPOST('net_measure') : $object->net_measure).'"> ';
2239  print $formproduct->selectMeasuringUnits("net_measure_units", "", GETPOSTISSET('net_measure_units') ? GETPOST('net_measure_units') : $object->net_measure_units, 0, 0);
2240  print '</td></tr>';
2241  }
2242  }
2243  // Units
2244  if (getDolGlobalString('PRODUCT_USE_UNITS')) {
2245  print '<tr><td>'.$langs->trans('DefaultUnitToShow').'</td>';
2246  print '<td>';
2247  print $form->selectUnits($object->fk_unit, 'units');
2248  print '</td></tr>';
2249  }
2250 
2251  // Custom code
2252  if (!$object->isService() && !getDolGlobalString('PRODUCT_DISABLE_CUSTOM_INFO')) {
2253  print '<tr><td class="wordbreak">'.$langs->trans("CustomCode").'</td><td><input name="customcode" class="maxwidth100onsmartphone" value="'.(GETPOSTISSET('customcode') ? GETPOST('customcode') : $object->customcode).'"></td></tr>';
2254  // Origin country
2255  print '<tr><td>'.$langs->trans("CountryOrigin").'</td>';
2256  print '<td>';
2257  print img_picto('', 'globe-americas', 'class="paddingrightonly"');
2258  print $form->select_country(GETPOSTISSET('country_id') ? GETPOSTINT('country_id') : $object->country_id, 'country_id', '', 0, 'minwidth100 maxwidthonsmartphone');
2259  if ($user->admin) {
2260  print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
2261  }
2262  print '</td></tr>';
2263 
2264  // State
2265  if (!getDolGlobalString('PRODUCT_DISABLE_STATE')) {
2266  print '<tr>';
2267  if (getDolGlobalString('MAIN_SHOW_REGION_IN_STATE_SELECT') && (getDolGlobalInt('MAIN_SHOW_REGION_IN_STATE_SELECT') == 1 || getDolGlobalInt('MAIN_SHOW_REGION_IN_STATE_SELECT') == 2)) {
2268  print '<td>'.$form->editfieldkey('RegionStateOrigin', 'state_id', '', $object, 0).'</td><td>';
2269  } else {
2270  print '<td>'.$form->editfieldkey('StateOrigin', 'state_id', '', $object, 0).'</td><td>';
2271  }
2272 
2273  print img_picto('', 'state', 'class="pictofixedwidth"');
2274  print $formcompany->select_state(GETPOSTISSET('state_id') ? GETPOSTINT('state_id') : $object->state_id, $object->country_code);
2275  print '</td>';
2276  print '</tr>';
2277  }
2278  }
2279 
2280  // Quality control
2281  if (getDolGlobalString('PRODUCT_LOT_ENABLE_QUALITY_CONTROL')) {
2282  print '<tr><td>'.$langs->trans("LifeTime").'</td><td><input name="lifetime" class="maxwidth100onsmartphone" value="'.$object->lifetime.'"></td></tr>';
2283  print '<tr><td>'.$langs->trans("QCFrequency").'</td><td><input name="qc_frequency" class="maxwidth100onsmartphone" value="'.$object->qc_frequency.'"></td></tr>';
2284  }
2285 
2286  // Other attributes
2287  $parameters = array('colspan' => ' colspan="2"', 'cols' => 2);
2288  $reshook = $hookmanager->executeHooks('formObjectOptions', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
2289  print $hookmanager->resPrint;
2290  if (empty($reshook)) {
2291  print $object->showOptionals($extrafields, 'edit', $parameters);
2292  }
2293 
2294  // Tags-Categories
2295  if (isModEnabled('category')) {
2296  print '<tr><td>'.$langs->trans("Categories").'</td><td>';
2297  $cate_arbo = $form->select_all_categories(Categorie::TYPE_PRODUCT, '', 'parent', 64, 0, 3);
2298  $c = new Categorie($db);
2299  $cats = $c->containing($object->id, Categorie::TYPE_PRODUCT);
2300  $arrayselected = array();
2301  if (is_array($cats)) {
2302  foreach ($cats as $cat) {
2303  $arrayselected[] = $cat->id;
2304  }
2305  }
2306  if (GETPOSTISARRAY('categories')) {
2307  foreach (GETPOST('categories', 'array') as $cat) {
2308  $arrayselected[] = $cat;
2309  }
2310  }
2311  print img_picto('', 'category', 'class="pictofixedwidth"').$form->multiselectarray('categories', $cate_arbo, $arrayselected, '', 0, 'quatrevingtpercent widthcentpercentminusx', 0, 0);
2312  print "</td></tr>";
2313  }
2314 
2315  // Note private
2316  if (getDolGlobalString('MAIN_DISABLE_NOTES_TAB')) {
2317  print '<tr><td class="tdtop">'.$langs->trans("NoteNotVisibleOnBill").'</td><td>';
2318 
2319  $doleditor = new DolEditor('note_private', $object->note_private, '', 140, 'dolibarr_notes', '', false, true, getDolGlobalInt('FCKEDITOR_ENABLE_NOTE_PRIVATE'), ROWS_4, '90%');
2320  $doleditor->Create();
2321 
2322  print "</td></tr>";
2323  }
2324 
2325  print '</table>';
2326 
2327  print '<br>';
2328 
2329  print '<table class="border centpercent">';
2330 
2331  if (!getDolGlobalString('PRODUCT_DISABLE_ACCOUNTING')) {
2332  if (isModEnabled('accounting')) {
2333  // Accountancy_code_sell
2334  print '<tr><td class="titlefieldcreate">'.$langs->trans("ProductAccountancySellCode").'</td>';
2335  print '<td>';
2336  print $formaccounting->select_account((GETPOSTISSET('accountancy_code_sell') ? GETPOST('accountancy_code_sell') : $object->accountancy_code_sell), 'accountancy_code_sell', 1, '', 1, 1, 'minwidth150 maxwidth300');
2337  print '</td></tr>';
2338 
2339  // Accountancy_code_sell_intra
2340  if ($mysoc->isInEEC()) {
2341  print '<tr><td class="titlefieldcreate">'.$langs->trans("ProductAccountancySellIntraCode").'</td>';
2342  print '<td>';
2343  print $formaccounting->select_account((GETPOSTISSET('accountancy_code_sell_intra') ? GETPOST('accountancy_code_sell_intra') : $object->accountancy_code_sell_intra), 'accountancy_code_sell_intra', 1, '', 1, 1, 'minwidth150 maxwidth300');
2344  print '</td></tr>';
2345  }
2346 
2347  // Accountancy_code_sell_export
2348  print '<tr><td class="titlefieldcreate">'.$langs->trans("ProductAccountancySellExportCode").'</td>';
2349  print '<td>';
2350  print $formaccounting->select_account((GETPOSTISSET('accountancy_code_sell_export') ? GETPOST('accountancy_code_sell_export') : $object->accountancy_code_sell_export), 'accountancy_code_sell_export', 1, '', 1, 1, 'minwidth150 maxwidth300');
2351  print '</td></tr>';
2352 
2353  // Accountancy_code_buy
2354  print '<tr><td>'.$langs->trans("ProductAccountancyBuyCode").'</td>';
2355  print '<td>';
2356  print $formaccounting->select_account((GETPOSTISSET('accountancy_code_buy') ? GETPOST('accountancy_code_buy') : $object->accountancy_code_buy), 'accountancy_code_buy', 1, '', 1, 1, 'minwidth150 maxwidth300');
2357  print '</td></tr>';
2358 
2359  // Accountancy_code_buy_intra
2360  if ($mysoc->isInEEC()) {
2361  print '<tr><td class="titlefieldcreate">'.$langs->trans("ProductAccountancyBuyIntraCode").'</td>';
2362  print '<td>';
2363  print $formaccounting->select_account((GETPOSTISSET('accountancy_code_buy_intra') ? GETPOST('accountancy_code_buy_intra') : $object->accountancy_code_buy_intra), 'accountancy_code_buy_intra', 1, '', 1, 1, 'minwidth150 maxwidth300');
2364  print '</td></tr>';
2365  }
2366 
2367  // Accountancy_code_buy_export
2368  print '<tr><td class="titlefieldcreate">'.$langs->trans("ProductAccountancyBuyExportCode").'</td>';
2369  print '<td>';
2370  print $formaccounting->select_account((GETPOSTISSET('accountancy_code_buy_export') ? GETPOST('accountancy_code_buy_export') : $object->accountancy_code_buy_export), 'accountancy_code_buy_export', 1, '', 1, 1, 'minwidth150 maxwidth300');
2371  print '</td></tr>';
2372  } else {
2373  // For external software
2374  // Accountancy_code_sell
2375  print '<tr><td class="titlefieldcreate">'.$langs->trans("ProductAccountancySellCode").'</td>';
2376  print '<td><input name="accountancy_code_sell" class="maxwidth200" value="'.(GETPOSTISSET('accountancy_code_sell') ? GETPOST('accountancy_code_sell') : $object->accountancy_code_sell).'">';
2377  print '</td></tr>';
2378 
2379  // Accountancy_code_sell_intra
2380  if ($mysoc->isInEEC()) {
2381  print '<tr><td class="titlefieldcreate">'.$langs->trans("ProductAccountancySellIntraCode").'</td>';
2382  print '<td><input name="accountancy_code_sell_intra" class="maxwidth200" value="'.(GETPOSTISSET('accountancy_code_sell_intra') ? GETPOST('accountancy_code_sell_intra') : $object->accountancy_code_sell_intra).'">';
2383  print '</td></tr>';
2384  }
2385 
2386  // Accountancy_code_sell_export
2387  print '<tr><td class="titlefieldcreate">'.$langs->trans("ProductAccountancySellExportCode").'</td>';
2388  print '<td><input name="accountancy_code_sell_export" class="maxwidth200" value="'.(GETPOSTISSET('accountancy_code_sell_export') ? GETPOST('accountancy_code_sell_export') : $object->accountancy_code_sell_export).'">';
2389  print '</td></tr>';
2390 
2391  // Accountancy_code_buy
2392  print '<tr><td>'.$langs->trans("ProductAccountancyBuyCode").'</td>';
2393  print '<td><input name="accountancy_code_buy" class="maxwidth200" value="'.(GETPOSTISSET('accountancy_code_buy') ? GETPOST('accountancy_code_buy') : $object->accountancy_code_buy).'">';
2394  print '</td></tr>';
2395 
2396  // Accountancy_code_buy_intra
2397  if ($mysoc->isInEEC()) {
2398  print '<tr><td class="titlefieldcreate">'.$langs->trans("ProductAccountancyBuyIntraCode").'</td>';
2399  print '<td><input name="accountancy_code_buy_intra" class="maxwidth200" value="'.(GETPOSTISSET('accountancy_code_buy_intra') ? GETPOST('accountancy_code_buy_intra') : $object->accountancy_code_buy_intra).'">';
2400  print '</td></tr>';
2401  }
2402 
2403  // Accountancy_code_buy_export
2404  print '<tr><td class="titlefieldcreate">'.$langs->trans("ProductAccountancyBuyExportCode").'</td>';
2405  print '<td><input name="accountancy_code_buy_export" class="maxwidth200" value="'.(GETPOSTISSET('accountancy_code_buy_export') ? GETPOST('accountancy_code_buy_export') : $object->accountancy_code_buy_export).'">';
2406  print '</td></tr>';
2407  }
2408  }
2409  print '</table>';
2410  }
2411 
2412  print dol_get_fiche_end();
2413 
2414  print $form->buttonsSaveCancel();
2415 
2416  print '</form>';
2417  } else {
2418  // Card in view mode
2419 
2420  $showbarcode = isModEnabled('barcode');
2421  if (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && !$user->hasRight('barcode', 'lire_advance')) {
2422  $showbarcode = 0;
2423  }
2424 
2425  $head = product_prepare_head($object);
2426  $titre = $langs->trans("CardProduct".$object->type);
2427  $picto = ($object->type == Product::TYPE_SERVICE ? 'service' : 'product');
2428 
2429  print dol_get_fiche_head($head, 'card', $titre, -1, $picto);
2430 
2431  $linkback = '<a href="'.DOL_URL_ROOT.'/product/list.php?restore_lastsearch_values=1&type='.$object->type.'">'.$langs->trans("BackToList").'</a>';
2432  $object->next_prev_filter = "fk_product_type:=:".((int) $object->type);
2433 
2434  $shownav = 1;
2435  if ($user->socid && !in_array('product', explode(',', getDolGlobalString('MAIN_MODULES_FOR_EXTERNAL')))) {
2436  $shownav = 0;
2437  }
2438 
2439  dol_banner_tab($object, 'ref', $linkback, $shownav, 'ref');
2440 
2441  // Call Hook tabContentViewProduct
2442  $parameters = array();
2443  // Note that $action and $object may be modified by hook
2444  $reshook = $hookmanager->executeHooks('tabContentViewProduct', $parameters, $object, $action);
2445  if (empty($reshook)) {
2446  print '<div class="fichecenter">';
2447  print '<div class="fichehalfleft">';
2448 
2449  print '<div class="underbanner clearboth"></div>';
2450  print '<table class="border tableforfield centpercent">';
2451 
2452  // Type
2453  if (isModEnabled("product") && isModEnabled("service")) {
2454  $typeformat = 'select;0:'.$langs->trans("Product").',1:'.$langs->trans("Service");
2455  print '<tr><td class="titlefield">';
2456  print (!getDolGlobalString('PRODUCT_DENY_CHANGE_PRODUCT_TYPE')) ? $form->editfieldkey("Type", 'fk_product_type', $object->type, $object, $usercancreate, $typeformat) : $langs->trans('Type');
2457  print '</td><td>';
2458  print $form->editfieldval("Type", 'fk_product_type', $object->type, $object, $usercancreate, $typeformat);
2459  print '</td></tr>';
2460  }
2461 
2462  if ($showbarcode) {
2463  // Barcode type
2464  print '<tr><td class="nowrap">';
2465  print '<table width="100%" class="nobordernopadding"><tr><td class="nowrap">';
2466  print $langs->trans("BarcodeType");
2467  print '</td>';
2468  if (($action != 'editbarcodetype') && $usercancreate && $createbarcode) {
2469  print '<td class="right"><a class="editfielda" href="'.$_SERVER["PHP_SELF"].'?action=editbarcodetype&id='.$object->id.'&token='.newToken().'">'.img_edit($langs->trans('Edit'), 1).'</a></td>';
2470  }
2471  print '</tr></table>';
2472  print '</td><td>';
2473  if ($action == 'editbarcodetype' || $action == 'editbarcode') {
2474  require_once DOL_DOCUMENT_ROOT.'/core/class/html.formbarcode.class.php';
2475  $formbarcode = new FormBarCode($db);
2476  }
2477 
2478  $fk_barcode_type = '';
2479  if ($action == 'editbarcodetype') {
2480  print $formbarcode->formBarcodeType($_SERVER['PHP_SELF'].'?id='.$object->id, $object->barcode_type, 'fk_barcode_type');
2481  $fk_barcode_type = $object->barcode_type;
2482  } else {
2483  $object->fetch_barcode();
2484  $fk_barcode_type = $object->barcode_type;
2485  print $object->barcode_type_label ? $object->barcode_type_label : ($object->barcode ? '<div class="warning">'.$langs->trans("SetDefaultBarcodeType").'<div>' : '');
2486  }
2487  print '</td></tr>'."\n";
2488 
2489  // Barcode value
2490  print '<tr><td class="nowrap">';
2491  print '<table width="100%" class="nobordernopadding"><tr><td class="nowrap">';
2492  print $langs->trans("BarcodeValue");
2493  print '</td>';
2494  if (($action != 'editbarcode') && $usercancreate && $createbarcode) {
2495  print '<td class="right"><a class="editfielda" href="'.$_SERVER["PHP_SELF"].'?action=editbarcode&id='.$object->id.'&token='.newToken().'">'.img_edit($langs->trans('Edit'), 1).'</a></td>';
2496  }
2497  print '</tr></table>';
2498  print '</td><td class="wordbreak">';
2499  if ($action == 'editbarcode') {
2500  $tmpcode = GETPOSTISSET('barcode') ? GETPOST('barcode') : $object->barcode;
2501  if (empty($tmpcode) && !empty($modBarCodeProduct->code_auto)) {
2502  $tmpcode = $modBarCodeProduct->getNextValue($object, $fk_barcode_type);
2503  }
2504 
2505  print '<form method="post" action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'">';
2506  print '<input type="hidden" name="token" value="'.newToken().'">';
2507  print '<input type="hidden" name="action" value="setbarcode">';
2508  print '<input type="hidden" name="barcode_type_code" value="'.$object->barcode_type_code.'">';
2509  print '<input class="width300" class="maxwidthonsmartphone" type="text" name="barcode" value="'.$tmpcode.'">';
2510  print '&nbsp;<input type="submit" class="button smallpaddingimp" value="'.$langs->trans("Modify").'">';
2511  print '</form>';
2512  } else {
2513  print showValueWithClipboardCPButton($object->barcode);
2514  }
2515  print '</td></tr>'."\n";
2516  }
2517 
2518  // Batch number management (to batch)
2519  if (isModEnabled('productbatch')) {
2520  if ($object->isProduct() || getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
2521  print '<tr><td>'.$langs->trans("ManageLotSerial").'</td><td>';
2522  print $object->getLibStatut(0, 2);
2523  print '</td></tr>';
2524  if ((($object->status_batch == '1' && getDolGlobalString('PRODUCTBATCH_LOT_USE_PRODUCT_MASKS') && getDolGlobalString('PRODUCTBATCH_LOT_ADDON') == 'mod_lot_advanced')
2525  || ($object->status_batch == '2' && getDolGlobalString('PRODUCTBATCH_SN_ADDON') == 'mod_sn_advanced' && getDolGlobalString('PRODUCTBATCH_SN_USE_PRODUCT_MASKS')))) {
2526  print '<tr><td>'.$langs->trans("ManageLotMask").'</td><td>';
2527  print $object->batch_mask;
2528  print '</td></tr>';
2529  }
2530  }
2531 
2532  print '<tr><td>'.$langs->trans('BatchSellOrEatByMandatoryList', $langs->trans('SellByDate'), $langs->trans('EatByDate')).'</td><td>';
2533  print $object->getSellOrEatByMandatoryLabel();
2534  print '</td></tr>';
2535  }
2536 
2537  if (!getDolGlobalString('PRODUCT_DISABLE_ACCOUNTING')) {
2538  // Accountancy sell code
2539  print '<tr><td class="nowrap">';
2540  print $langs->trans("ProductAccountancySellCode");
2541  print '</td><td>';
2542  if (isModEnabled('accounting')) {
2543  if (!empty($object->accountancy_code_sell)) {
2544  $accountingaccount = new AccountingAccount($db);
2545  $accountingaccount->fetch('', $object->accountancy_code_sell, 1);
2546 
2547  print $accountingaccount->getNomUrl(0, 1, 1, '', 1);
2548  }
2549  } else {
2550  print $object->accountancy_code_sell;
2551  }
2552  print '</td></tr>';
2553 
2554  // Accountancy sell code intra-community
2555  if ($mysoc->isInEEC()) {
2556  print '<tr><td class="nowrap">';
2557  print $langs->trans("ProductAccountancySellIntraCode");
2558  print '</td><td>';
2559  if (isModEnabled('accounting')) {
2560  if (!empty($object->accountancy_code_sell_intra)) {
2561  $accountingaccount2 = new AccountingAccount($db);
2562  $accountingaccount2->fetch('', $object->accountancy_code_sell_intra, 1);
2563 
2564  print $accountingaccount2->getNomUrl(0, 1, 1, '', 1);
2565  }
2566  } else {
2567  print $object->accountancy_code_sell_intra;
2568  }
2569  print '</td></tr>';
2570  }
2571 
2572  // Accountancy sell code export
2573  print '<tr><td class="nowrap">';
2574  print $langs->trans("ProductAccountancySellExportCode");
2575  print '</td><td>';
2576  if (isModEnabled('accounting')) {
2577  if (!empty($object->accountancy_code_sell_export)) {
2578  $accountingaccount3 = new AccountingAccount($db);
2579  $accountingaccount3->fetch('', $object->accountancy_code_sell_export, 1);
2580 
2581  print $accountingaccount3->getNomUrl(0, 1, 1, '', 1);
2582  }
2583  } else {
2584  print $object->accountancy_code_sell_export;
2585  }
2586  print '</td></tr>';
2587 
2588  // Accountancy buy code
2589  print '<tr><td class="nowrap">';
2590  print $langs->trans("ProductAccountancyBuyCode");
2591  print '</td><td>';
2592  if (isModEnabled('accounting')) {
2593  if (!empty($object->accountancy_code_buy)) {
2594  $accountingaccount4 = new AccountingAccount($db);
2595  $accountingaccount4->fetch('', $object->accountancy_code_buy, 1);
2596 
2597  print $accountingaccount4->getNomUrl(0, 1, 1, '', 1);
2598  }
2599  } else {
2600  print $object->accountancy_code_buy;
2601  }
2602  print '</td></tr>';
2603 
2604  // Accountancy buy code intra-community
2605  if ($mysoc->isInEEC()) {
2606  print '<tr><td class="nowrap">';
2607  print $langs->trans("ProductAccountancyBuyIntraCode");
2608  print '</td><td>';
2609  if (isModEnabled('accounting')) {
2610  if (!empty($object->accountancy_code_buy_intra)) {
2611  $accountingaccount5 = new AccountingAccount($db);
2612  $accountingaccount5->fetch('', $object->accountancy_code_buy_intra, 1);
2613 
2614  print $accountingaccount5->getNomUrl(0, 1, 1, '', 1);
2615  }
2616  } else {
2617  print $object->accountancy_code_buy_intra;
2618  }
2619  print '</td></tr>';
2620  }
2621 
2622  // Accountancy buy code export
2623  print '<tr><td class="nowrap">';
2624  print $langs->trans("ProductAccountancyBuyExportCode");
2625  print '</td><td>';
2626  if (isModEnabled('accounting')) {
2627  if (!empty($object->accountancy_code_buy_export)) {
2628  $accountingaccount6 = new AccountingAccount($db);
2629  $accountingaccount6->fetch('', $object->accountancy_code_buy_export, 1);
2630 
2631  print $accountingaccount6->getNomUrl(0, 1, 1, '', 1);
2632  }
2633  } else {
2634  print $object->accountancy_code_buy_export;
2635  }
2636  print '</td></tr>';
2637  }
2638 
2639  // Description
2640  print '<tr><td class="tdtop">'.$langs->trans("Description").'</td><td class="wordbreakimp wrapimp">'.(dol_textishtml($object->description) ? $object->description : dol_nl2br($object->description, 1, true)).'</td></tr>';
2641 
2642  // Public URL
2643  if (!getDolGlobalString('PRODUCT_DISABLE_PUBLIC_URL')) {
2644  print '<tr><td>'.$langs->trans("PublicUrl").'</td><td>';
2645  print dol_print_url($object->url, '_blank', 128);
2646  print '</td></tr>';
2647  }
2648 
2649  // Default warehouse
2650  if (($object->isProduct() || getDolGlobalInt('STOCK_SUPPORTS_SERVICES')) && isModEnabled('stock')) {
2651  $warehouse = new Entrepot($db);
2652  $warehouse->fetch($object->fk_default_warehouse);
2653 
2654  print '<tr><td>'.$langs->trans("DefaultWarehouse").'</td><td>';
2655  print(!empty($warehouse->id) ? $warehouse->getNomUrl(1) : '');
2656  print '</td>';
2657  }
2658 
2659  if ($object->isService() && isModEnabled('workstation')) {
2660  $workstation = new Workstation($db);
2661  $res = $workstation->fetch($object->fk_default_workstation);
2662 
2663  print '<tr><td>'.$langs->trans("DefaultWorkstation").'</td><td>';
2664  print(!empty($workstation->id) ? $workstation->getNomUrl(1) : '');
2665  print '</td>';
2666  }
2667 
2668  // Parent product.
2669  if (isModEnabled('variants') && ($object->isProduct() || $object->isService())) {
2670  $combination = new ProductCombination($db);
2671 
2672  if ($combination->fetchByFkProductChild($object->id) > 0) {
2673  $prodstatic = new Product($db);
2674  $prodstatic->fetch($combination->fk_product_parent);
2675 
2676  // Parent product
2677  print '<tr><td>'.$langs->trans("ParentProduct").'</td><td>';
2678  print $prodstatic->getNomUrl(1);
2679  print '</td></tr>';
2680  }
2681  }
2682 
2683  print '</table>';
2684  print '</div>';
2685  print '<div class="fichehalfright">';
2686 
2687  print '<div class="underbanner clearboth"></div>';
2688  print '<table class="border tableforfield centpercent">';
2689 
2690  if ($object->isService()) {
2691  // Duration
2692  print '<tr><td class="titlefield">'.$langs->trans("Duration").'</td><td>';
2693  print $object->duration_value;
2694  if ($object->duration_value > 1) {
2695  $dur = array("i" => $langs->trans("Minute"), "h" => $langs->trans("Hours"), "d" => $langs->trans("Days"), "w" => $langs->trans("Weeks"), "m" => $langs->trans("Months"), "y" => $langs->trans("Years"));
2696  } elseif ($object->duration_value > 0) {
2697  $dur = array("i" => $langs->trans("Minute"), "h" => $langs->trans("Hour"), "d" => $langs->trans("Day"), "w" => $langs->trans("Week"), "m" => $langs->trans("Month"), "y" => $langs->trans("Year"));
2698  }
2699  print(!empty($object->duration_unit) && isset($dur[$object->duration_unit]) ? "&nbsp;".$langs->trans($dur[$object->duration_unit])."&nbsp;" : '');
2700 
2701  // Mandatory period
2702  if ($object->duration_value > 0) {
2703  print ' &nbsp; &nbsp; &nbsp; ';
2704  }
2705  $htmltooltip = $langs->trans("mandatoryHelper");
2706  print '<input type="checkbox" class="" name="mandatoryperiod"'.($object->mandatory_period == 1 ? ' checked="checked"' : '').' disabled>';
2707  print $form->textwithpicto($langs->trans("mandatoryperiod"), $htmltooltip, 1, 0);
2708 
2709  print '</td></tr>';
2710  } else {
2711  if (!getDolGlobalString('PRODUCT_DISABLE_NATURE')) {
2712  // Nature
2713  print '<tr><td class="titlefield">'.$form->textwithpicto($langs->trans("NatureOfProductShort"), $langs->trans("NatureOfProductDesc")).'</td><td>';
2714  print $object->getLibFinished();
2715  print '</td></tr>';
2716  }
2717  }
2718 
2719  if (!$object->isService() && isModEnabled('bom') && $object->finished) {
2720  print '<tr><td class="titlefield">'.$form->textwithpicto($langs->trans("DefaultBOM"), $langs->trans("DefaultBOMDesc", $langs->transnoentitiesnoconv("Finished"))).'</td><td>';
2721  if ($object->fk_default_bom) {
2722  $bom_static = new BOM($db);
2723  $bom_static->fetch($object->fk_default_bom);
2724  print $bom_static->getNomUrl(1);
2725  }
2726  print '</td></tr>';
2727  }
2728 
2729  if (!$object->isService()) {
2730  // Brut Weight
2731  if (!getDolGlobalString('PRODUCT_DISABLE_WEIGHT')) {
2732  print '<tr><td class="titlefield">'.$langs->trans("Weight").'</td><td>';
2733  if ($object->weight != '') {
2734  print $object->weight." ".measuringUnitString(0, "weight", $object->weight_units);
2735  } else {
2736  print '&nbsp;';
2737  }
2738  print "</td></tr>\n";
2739  }
2740 
2741  if (!getDolGlobalString('PRODUCT_DISABLE_SIZE')) {
2742  // Brut Length
2743  print '<tr><td>'.$langs->trans("Length").' x '.$langs->trans("Width").' x '.$langs->trans("Height").'</td><td>';
2744  if ($object->length != '' || $object->width != '' || $object->height != '') {
2745  print $object->length;
2746  if ($object->width) {
2747  print " x ".$object->width;
2748  }
2749  if ($object->height) {
2750  print " x ".$object->height;
2751  }
2752  print ' '.measuringUnitString(0, "size", $object->length_units);
2753  } else {
2754  print '&nbsp;';
2755  }
2756  print "</td></tr>\n";
2757  }
2758  if (!getDolGlobalString('PRODUCT_DISABLE_SURFACE')) {
2759  // Brut Surface
2760  print '<tr><td>'.$langs->trans("Surface").'</td><td>';
2761  if ($object->surface != '') {
2762  print $object->surface." ".measuringUnitString(0, "surface", $object->surface_units);
2763  } else {
2764  print '&nbsp;';
2765  }
2766  print "</td></tr>\n";
2767  }
2768  if (!getDolGlobalString('PRODUCT_DISABLE_VOLUME')) {
2769  // Brut Volume
2770  print '<tr><td>'.$langs->trans("Volume").'</td><td>';
2771  if ($object->volume != '') {
2772  print $object->volume." ".measuringUnitString(0, "volume", $object->volume_units);
2773  } else {
2774  print '&nbsp;';
2775  }
2776  print "</td></tr>\n";
2777  }
2778 
2779  if (getDolGlobalString('PRODUCT_ADD_NET_MEASURE')) {
2780  // Net Measure
2781  print '<tr><td class="titlefield">'.$langs->trans("NetMeasure").'</td><td>';
2782  if ($object->net_measure != '') {
2783  print $object->net_measure." ".measuringUnitString($object->net_measure_units);
2784  } else {
2785  print '&nbsp;';
2786  }
2787  print '</td></tr>';
2788  }
2789  }
2790 
2791  // Unit
2792  if (getDolGlobalString('PRODUCT_USE_UNITS')) {
2793  $unit = $object->getLabelOfUnit();
2794 
2795  print '<tr><td>'.$langs->trans('DefaultUnitToShow').'</td><td>';
2796  if ($unit !== '') {
2797  print $langs->trans($unit);
2798  }
2799  print '</td></tr>';
2800  }
2801 
2802  // Custom code
2803  if (!$object->isService() && !getDolGlobalString('PRODUCT_DISABLE_CUSTOM_INFO')) {
2804  print '<tr><td>'.$langs->trans("CustomCode").'</td><td>'.$object->customcode.'</td></tr>';
2805 
2806  // Origin country code
2807  print '<tr><td>'.$langs->trans("Origin").'</td><td>'.getCountry($object->country_id, 0, $db);
2808  if (!empty($object->state_id)) {
2809  print ' - '.getState($object->state_id, 0, $db);
2810  }
2811  print '</td></tr>';
2812  }
2813 
2814  // Quality Control
2815  if (getDolGlobalString('PRODUCT_LOT_ENABLE_QUALITY_CONTROL')) {
2816  print '<tr><td>'.$langs->trans("LifeTime").'</td><td>'.$object->lifetime.'</td></tr>';
2817  print '<tr><td>'.$langs->trans("QCFrequency").'</td><td>'.$object->qc_frequency.'</td></tr>';
2818  }
2819 
2820  // Other attributes
2821  $parameters = array();
2822  include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_view.tpl.php';
2823 
2824  // Categories
2825  if (isModEnabled('category')) {
2826  print '<tr><td class="valignmiddle">'.$langs->trans("Categories").'</td><td>';
2827  print $form->showCategories($object->id, Categorie::TYPE_PRODUCT, 1);
2828  print "</td></tr>";
2829  }
2830 
2831  // Note private
2832  if (getDolGlobalString('MAIN_DISABLE_NOTES_TAB')) {
2833  print '<!-- show Note --> '."\n";
2834  print '<tr><td class="tdtop">'.$langs->trans("NotePrivate").'</td><td>'.(dol_textishtml($object->note_private) ? $object->note_private : dol_nl2br($object->note_private, 1, true)).'</td></tr>'."\n";
2835  print '<!-- End show Note --> '."\n";
2836  }
2837 
2838  print "</table>\n";
2839  print '</div>';
2840 
2841  print '</div>';
2842  print '<div class="clearboth"></div>';
2843  }
2844 
2845  print dol_get_fiche_end();
2846  }
2847  } elseif ($action != 'create') {
2848  exit;
2849  }
2850 }
2851 
2852 $tmpcode = '';
2853 if (!empty($modCodeProduct->code_auto)) {
2854  $tmpcode = $modCodeProduct->getNextValue($object, $object->type);
2855 }
2856 
2857 $formconfirm = '';
2858 
2859 // Confirm delete product
2860 if (($action == 'delete' && (empty($conf->use_javascript_ajax) || !empty($conf->dol_use_jmobile))) // Output when action = clone if jmobile or no js
2861  || (!empty($conf->use_javascript_ajax) && empty($conf->dol_use_jmobile))) { // Always output when not jmobile nor js
2862  $formconfirm = $form->formconfirm("card.php?id=".$object->id, $langs->trans("DeleteProduct"), $langs->trans("ConfirmDeleteProduct"), "confirm_delete", '', 0, "action-delete");
2863 }
2864 if ($action == 'merge') {
2865  $formquestion = array(
2866  array(
2867  'name' => 'product_origin',
2868  'label' => $langs->trans('MergeOriginProduct'),
2869  'type' => 'other',
2870  'value' => $form->select_produits('', 'product_origin', '', 0, 0, 1, 2, '', 1, array(), 0, 1, 0, 'minwidth200', 0, '', null, 1),
2871  )
2872  );
2873  $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"]."?id=".$object->id, $langs->trans("MergeProducts"), $langs->trans("ConfirmMergeProducts"), "confirm_merge", $formquestion, 'no', 1, 250);
2874 }
2875 
2876 // Clone confirmation
2877 if (($action == 'clone' && (empty($conf->use_javascript_ajax) || !empty($conf->dol_use_jmobile))) // Output when action = clone if jmobile or no js
2878  || (!empty($conf->use_javascript_ajax) && empty($conf->dol_use_jmobile))) { // Always output when not jmobile nor js
2879  // Define confirmation messages
2880  $formquestionclone = array(
2881  'text' => $langs->trans("ConfirmClone"),
2882  0 => array('type' => 'text', 'name' => 'clone_ref', 'label' => $langs->trans("NewRefForClone"), 'value' => empty($tmpcode) ? $langs->trans("CopyOf").' '.$object->ref : $tmpcode, 'morecss' => 'width150'),
2883  1 => array('type' => 'checkbox', 'name' => 'clone_content', 'label' => $langs->trans("CloneContentProduct"), 'value' => 1),
2884  2 => array('type' => 'checkbox', 'name' => 'clone_categories', 'label' => $langs->trans("CloneCategoriesProduct"), 'value' => 1),
2885  );
2886  if (getDolGlobalString('PRODUIT_MULTIPRICES')) {
2887  $formquestionclone[] = array('type' => 'checkbox', 'name' => 'clone_prices', 'label' => $langs->trans("ClonePricesProduct").' ('.$langs->trans("CustomerPrices").')', 'value' => 0);
2888  }
2889  if (getDolGlobalString('PRODUIT_SOUSPRODUITS')) {
2890  $formquestionclone[] = array('type' => 'checkbox', 'name' => 'clone_composition', 'label' => $langs->trans('CloneCompositionProduct'), 'value' => 1);
2891  }
2892 
2893  $formconfirm .= $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id, $langs->trans('ToClone'), $langs->trans('ConfirmCloneProduct', $object->ref), 'confirm_clone', $formquestionclone, 'yes', 'action-clone', 350, 600);
2894 }
2895 
2896 // Call Hook formConfirm
2897 $parameters = array('formConfirm' => $formconfirm, 'object' => $object);
2898 $reshook = $hookmanager->executeHooks('formConfirm', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
2899 if (empty($reshook)) {
2900  $formconfirm .= $hookmanager->resPrint;
2901 } elseif ($reshook > 0) {
2902  $formconfirm = $hookmanager->resPrint;
2903 }
2904 
2905 // Print form confirm
2906 print $formconfirm;
2907 
2908 /*
2909  * Action bar
2910  */
2911 if ($action != 'create' && $action != 'edit') {
2912  $cloneProductUrl = $_SERVER["PHP_SELF"].'?action=clone&token='.newToken();
2913  $cloneButtonId = 'action-clone-no-ajax';
2914 
2915  print "\n".'<div class="tabsAction">'."\n";
2916 
2917  $parameters = array();
2918  $reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
2919  if (empty($reshook)) {
2920  if ($usercancreate) {
2921  if (!isset($object->no_button_edit) || $object->no_button_edit != 1) {
2922  print dolGetButtonAction('', $langs->trans('Modify'), 'default', $_SERVER["PHP_SELF"].'?action=edit&token='.newToken().'&id='.$object->id, '', $usercancreate);
2923  }
2924 
2925  if (!isset($object->no_button_copy) || $object->no_button_copy != 1) {
2926  if (!empty($conf->use_javascript_ajax) && empty($conf->dol_use_jmobile)) {
2927  $cloneProductUrl = '';
2928  $cloneButtonId = 'action-clone';
2929  }
2930  print dolGetButtonAction($langs->trans('ToClone'), '', 'default', $cloneProductUrl, $cloneButtonId, $usercancreate);
2931  }
2932  }
2933  $object_is_used = $object->isObjectUsed($object->id);
2934 
2935  if ($usercandelete) {
2936  if (empty($object_is_used) && (!isset($object->no_button_delete) || $object->no_button_delete != 1)) {
2937  if (!empty($conf->use_javascript_ajax) && empty($conf->dol_use_jmobile)) {
2938  print dolGetButtonAction($langs->trans('Delete'), '', 'delete', '#', 'action-delete', true);
2939  } else {
2940  print dolGetButtonAction('', $langs->trans('Delete'), 'delete', $_SERVER["PHP_SELF"].'?action=delete&token='.newToken().'&id='.$object->id, '');
2941  }
2942  } else {
2943  print dolGetButtonAction($langs->trans("ProductIsUsed"), $langs->trans('Delete'), 'delete', '#', '', false);
2944  }
2945  if (getDolGlobalInt('MAIN_FEATURES_LEVEL') > 1) {
2946  print '<a class="butActionDelete" href="card.php?action=merge&id='.$object->id.'" title="'.dol_escape_htmltag($langs->trans("MergeProducts")).'">'.$langs->trans('Merge').'</a>'."\n";
2947  }
2948  } else {
2949  print dolGetButtonAction($langs->trans("NotEnoughPermissions"), $langs->trans('Delete'), 'delete', '#', '', false);
2950  }
2951  }
2952 
2953  print "\n</div>\n";
2954 }
2955 
2956 
2957 /*
2958  * All the "Add to" areas if PRODUCT_ADD_FORM_ADD_TO is set
2959  */
2960 
2961 if (getDolGlobalString('PRODUCT_ADD_FORM_ADD_TO') && $object->id && ($action == '' || $action == 'view') && $object->status) {
2962  //Variable used to check if any text is going to be printed
2963  $html = '';
2964  //print '<div class="fichecenter"><div class="fichehalfleft">';
2965 
2966  // Propals
2967  if (isModEnabled("propal") && $user->hasRight('propal', 'creer')) {
2968  $propal = new Propal($db);
2969 
2970  $langs->load("propal");
2971 
2972  $otherprop = $propal->liste_array(2, 1, 0);
2973 
2974  if (is_array($otherprop) && count($otherprop)) {
2975  $html .= '<tr><td style="width: 200px;">';
2976  $html .= $langs->trans("AddToDraftProposals").'</td><td>';
2977  $html .= $form->selectarray("propalid", $otherprop, 0, 1);
2978  $html .= '</td></tr>';
2979  } else {
2980  $html .= '<tr><td style="width: 200px;">';
2981  $html .= $langs->trans("AddToDraftProposals").'</td><td>';
2982  $html .= $langs->trans("NoDraftProposals");
2983  $html .= '</td></tr>';
2984  }
2985  }
2986 
2987  // Commande
2988  if (isModEnabled('order') && $user->hasRight('commande', 'creer')) {
2989  $commande = new Commande($db);
2990 
2991  $langs->load("orders");
2992 
2993  $othercom = $commande->liste_array(2, 1, null);
2994  if (is_array($othercom) && count($othercom)) {
2995  $html .= '<tr><td style="width: 200px;">';
2996  $html .= $langs->trans("AddToDraftOrders").'</td><td>';
2997  $html .= $form->selectarray("commandeid", $othercom, 0, 1);
2998  $html .= '</td></tr>';
2999  } else {
3000  $html .= '<tr><td style="width: 200px;">';
3001  $html .= $langs->trans("AddToDraftOrders").'</td><td>';
3002  $html .= $langs->trans("NoDraftOrders");
3003  $html .= '</td></tr>';
3004  }
3005  }
3006 
3007  // Factures
3008  if (isModEnabled('invoice') && $user->hasRight('facture', 'creer')) {
3009  $invoice = new Facture($db);
3010 
3011  $langs->load("bills");
3012 
3013  $otherinvoice = $invoice->liste_array(2, 1, null);
3014  if (is_array($otherinvoice) && count($otherinvoice)) {
3015  $html .= '<tr><td style="width: 200px;">';
3016  $html .= $langs->trans("AddToDraftInvoices").'</td><td>';
3017  $html .= $form->selectarray("factureid", $otherinvoice, 0, 1);
3018  $html .= '</td></tr>';
3019  } else {
3020  $html .= '<tr><td style="width: 200px;">';
3021  $html .= $langs->trans("AddToDraftInvoices").'</td><td>';
3022  $html .= $langs->trans("NoDraftInvoices");
3023  $html .= '</td></tr>';
3024  }
3025  }
3026 
3027  //If any text is going to be printed, then we show the table
3028  if (!empty($html)) {
3029  print '<form method="POST" action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'">';
3030  print '<input type="hidden" name="token" value="'.newToken().'">';
3031  print '<input type="hidden" name="action" value="addin">';
3032 
3033  print load_fiche_titre($langs->trans("AddToDraft"), '', '');
3034 
3035  print dol_get_fiche_head();
3036 
3037  $html .= '<tr><td class="nowrap">'.$langs->trans("Quantity").' ';
3038  $html .= '<input type="text" class="flat" name="qty" size="1" value="1"></td>';
3039  $html .= '<td class="nowrap">'.$langs->trans("ReductionShort").'(%) ';
3040  $html .= '<input type="text" class="flat" name="remise_percent" size="1" value="0">';
3041  $html .= '</td></tr>';
3042 
3043  print '<table width="100%" class="border">';
3044  print $html;
3045  print '</table>';
3046 
3047  print '<div class="center">';
3048  print '<input type="submit" class="button button-add" value="'.$langs->trans("Add").'">';
3049  print '</div>';
3050 
3051  print dol_get_fiche_end();
3052 
3053  print '</form>';
3054  }
3055 }
3056 
3057 
3058 /*
3059  * Generated documents
3060  */
3061 
3062 if ($action != 'create' && $action != 'edit' && $action != 'delete') {
3063  print '<div class="fichecenter"><div class="fichehalfleft">';
3064  print '<a name="builddoc"></a>'; // ancre
3065 
3066  // Documents
3067  $objectref = dol_sanitizeFileName($object->ref);
3068  if (!empty($conf->product->multidir_output[$object->entity])) {
3069  $filedir = $conf->product->multidir_output[$object->entity].'/'.$objectref; //Check repertories of current entities
3070  } else {
3071  $filedir = $conf->product->dir_output.'/'.$objectref;
3072  }
3073  $urlsource = $_SERVER["PHP_SELF"]."?id=".$object->id;
3074  $genallowed = $usercanread;
3075  $delallowed = $usercancreate;
3076 
3077  print $formfile->showdocuments($modulepart, $object->ref, $filedir, $urlsource, $genallowed, $delallowed, '', 0, 0, 0, 28, 0, '', 0, '', $langs->getDefaultLang(), '', $object);
3078  $somethingshown = $formfile->numoffiles;
3079 
3080  print '</div><div class="fichehalfright">';
3081 
3082  $MAXEVENT = 10;
3083  $morehtmlcenter = '<div class="nowraponall">';
3084  $morehtmlcenter .= dolGetButtonTitle($langs->trans('FullConversation'), '', 'fa fa-comments imgforviewmode', DOL_URL_ROOT.'/product/messaging.php?id='.$object->id);
3085  $morehtmlcenter .= dolGetButtonTitle($langs->trans('SeeAll'), '', 'fa fa-bars imgforviewmode', DOL_URL_ROOT.'/product/agenda.php?id='.$object->id);
3086  $morehtmlcenter .= '</div>';
3087 
3088  // List of actions on element
3089  include_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php';
3090  $formactions = new FormActions($db);
3091  $somethingshown = $formactions->showactions($object, 'product', 0, 1, '', $MAXEVENT, '', $morehtmlcenter); // Show all action for product
3092 
3093  print '</div></div>';
3094 }
3095 
3096 // End of page
3097 llxFooter();
3098 $db->close();
if($user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition: card.php:58
if(GETPOST('button_removefilter_x', 'alpha')||GETPOST('button_removefilter.x', 'alpha')||GETPOST('button_removefilter', 'alpha')) if(GETPOST('button_search_x', 'alpha')||GETPOST('button_search.x', 'alpha')||GETPOST('button_search', 'alpha')) if($action=="save" &&empty($cancel)) $help_url
View.
Definition: agenda.php:118
if(preg_match('/set_([a-z0-9_\-]+)/i', $action, $reg)) if(preg_match('/del_([a-z0-9_\-]+)/i', $action, $reg)) if($action=='set') elseif($action=='specimen') elseif($action=='setmodel') elseif($action=='del') elseif($action=='setdoc') $formactions
View.
if(!defined('NOREQUIRESOC')) if(!defined('NOREQUIRETRAN')) if(!defined('NOTOKENRENEWAL')) if(!defined('NOREQUIREMENU')) if(!defined('NOREQUIREHTML')) if(!defined('NOREQUIREAJAX')) llxHeader()
Empty header.
Definition: wrapper.php:56
llxFooter()
Empty footer.
Definition: wrapper.php:70
Class to manage accounting accounts.
Class for BOM.
Definition: bom.class.php:45
Class to manage canvas.
Class to manage categories.
Class to manage customers orders.
Class to manage a WYSIWYG editor.
Class to manage warehouses.
Class to manage standard extra fields.
Class to manage invoices.
const TYPE_STANDARD
Standard invoice.
Class to manage generation of HTML components for accounting management.
Class to manage building of HTML components.
Class to manage barcode HTML.
Class to build HTML component for third parties management Only common components are here.
Class to offer components to list and upload files.
Class to manage generation of HTML components Only common components must be here.
Class with static methods for building HTML components related to products Only components common to ...
Class of a generic business object.
Class ProductCombination Used to represent the relation between a product and one of its variants.
File of class to manage predefined price products or services by customer.
Class to manage products or services.
const TYPE_PRODUCT
Regular product.
static getSellOrEatByMandatoryList()
Get sell or eat by mandatory list.
const TYPE_SERVICE
Service.
Class to manage proposals.
Class to manage third parties objects (customers, suppliers, prospects...)
Class for Workstation.
getCountry($searchkey, $withcode='', $dbtouse=null, $outputlangs=null, $entconv=1, $searchlabel='')
Return country label, code or id from an id, code or label.
$parameters
Actions.
Definition: card.php:84
if(isModEnabled('invoice') && $user->hasRight('facture', 'lire')) if((isModEnabled('fournisseur') &&!getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD') && $user->hasRight("fournisseur", "facture", "lire"))||(isModEnabled('supplier_invoice') && $user->hasRight("supplier_invoice", "lire"))) if(isModEnabled('don') && $user->hasRight('don', 'lire')) if(isModEnabled('tax') && $user->hasRight('tax', 'charges', 'lire')) if(isModEnabled('invoice') &&isModEnabled('order') && $user->hasRight("commande", "lire") &&!getDolGlobalString('WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER')) $sql
Social contributions to pay.
Definition: index.php:744
if($cancel &&! $id) if($action=='add' &&! $cancel) if($action=='delete') if($id) $form
Actions.
Definition: card.php:143
load_fiche_titre($titre, $morehtmlright='', $picto='generic', $pictoisfullpath=0, $id='', $morecssontable='', $morehtmlcenter='')
Load a title with picto.
showValueWithClipboardCPButton($valuetocopy, $showonlyonhover=1, $texttoshow='')
Create a button to copy $valuetocopy in the clipboard (for copy and paste feature).
img_warning($titlealt='default', $moreatt='', $morecss='pictowarning')
Show warning logo.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limittoshow=0, $moretabssuffix='', $dragdropfile=0)
Show tabs of a record.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dolGetButtonTitle($label, $helpText='', $iconClass='fa fa-file', $url='', $id='', $status=1, $params=array())
Function dolGetButtonTitle : this kind of buttons are used in title in list.
dol_get_fiche_end($notab=0)
Return tab footer of a card.
dol_nl2br($stringtoencode, $nl2brmode=0, $forxml=false)
Replace CRLF in string with a HTML BR tag.
dol_print_url($url, $target='_blank', $max=32, $withpicto=0, $morecss='')
Show Url link.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
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.
GETPOSTISARRAY($paramname, $method=0)
Return true if the parameter $paramname is submit from a POST OR GET as an array.
if(!function_exists('dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
dol_set_focus($selector)
Set focus onto field with selector (similar behaviour of 'autofocus' HTML5 tag)
newToken()
Return the value of token currently saved into session with name 'newtoken'.
dol_clone($object, $native=0)
Create a clone of instance of object (new instance with same value for each properties) With native =...
dolGetButtonAction($label, $text='', $actionType='default', $url='', $id='', $userRight=1, $params=array())
Function dolGetButtonAction.
dol_htmlcleanlastbr($stringtodecode)
This function remove all ending and br at end.
get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Function that returns whether VAT must be recoverable collected VAT (e.g.
dol_concatdesc($text1, $text2, $forxml=false, $invert=false)
Concat 2 descriptions with a new line between them (second operand after first one with appropriate n...
dol_textishtml($msg, $option=0)
Return if a text is a html content.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
info_admin($text, $infoonimgalt=0, $nodiv=0, $admin='1', $morecss='hideonsmartphone', $textfordropdown='')
Show information in HTML for admin users or standard users.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0)
Set event messages in dol_events session object.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
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 dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
img_edit($titlealt='default', $float=0, $other='')
Show logo edit/modify fiche.
get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Function that return vat rate of a product line (according to seller, buyer and product vat rate) VAT...
get_localtax($vatrate, $local, $thirdparty_buyer=null, $thirdparty_seller=null, $vatnpr=0)
Return localtax rate for a particular vat, when selling a product with vat $vatrate,...
get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart='')
Return a path to have a the directory according to object where files are stored.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
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...
$formconfirm
if ($action == 'delbookkeepingyear') {
product_prepare_head($object)
Prepare array with list of tabs.
Definition: product.lib.php:37
measuringUnitString($unit, $measuring_style='', $scale='', $use_short_label=0, $outputlangs=null)
Return translation label of a unit key.
restrictedArea(User $user, $features, $object=0, $tableandshare='', $feature2='', $dbt_keyfield='fk_soc', $dbt_select='rowid', $isdraft=0, $mode=0)
Check permissions of a user to show a page and an object.