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