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