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