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