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