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