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