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