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