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