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" class="width50" value="'.(GETPOSTISSET('duration_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 if ($object->duration_value > 0) {
1627 print ' &nbsp; &nbsp; ';
1628 }
1629 print '<input type="checkbox" class="marginleftonly valignmiddle" id="mandatoryperiod" name="mandatoryperiod"'.($object->mandatory_period == 1 ? ' checked="checked"' : '').'>';
1630 print '<label for="mandatoryperiod">';
1631 $htmltooltip = $langs->trans("mandatoryHelper");
1632 if (!getDolGlobalString('SERVICE_STRICT_MANDATORY_PERIOD')) {
1633 $htmltooltip .= '<br>'.$langs->trans("mandatoryHelper2");
1634 }
1635 print $form->textwithpicto($langs->trans("mandatoryperiod"), $htmltooltip, 1, 0);
1636 print '</label>';
1637
1638 print '</td></tr>';
1639 }
1640
1641 if ($type != 1) { // Nature, Weight and volume only applies to products and not to services
1642 if (!getDolGlobalString('PRODUCT_DISABLE_NATURE')) {
1643 // Nature
1644 print '<tr><td>'.$form->textwithpicto($langs->trans("NatureOfProductShort"), $langs->trans("NatureOfProductDesc")).'</td><td>';
1645 print $formproduct->selectProductNature('finished', $object->finished);
1646 print '</td></tr>';
1647 }
1648 }
1649
1650 if ($type != 1) {
1651 if (!getDolGlobalString('PRODUCT_DISABLE_WEIGHT')) {
1652 // Brut Weight
1653 print '<tr><td>'.$langs->trans("Weight").'</td><td>';
1654 print img_picto('', 'fa-balance-scale', 'class="pictofixedwidth"');
1655 print '<input name="weight" size="4" value="'.GETPOST('weight').'">';
1656 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);
1657 print '</td></tr>';
1658 }
1659
1660 // Brut Length
1661 if (!getDolGlobalString('PRODUCT_DISABLE_SIZE')) {
1662 print '<tr><td>'.$langs->trans("Length").' x '.$langs->trans("Width").' x '.$langs->trans("Height").'</td><td>';
1663 print img_picto('', 'fa-ruler', 'class="pictofixedwidth"');
1664 print '<input name="size" class="width50" value="'.GETPOST('size').'"> x ';
1665 print '<input name="sizewidth" class="width50" value="'.GETPOST('sizewidth').'"> x ';
1666 print '<input name="sizeheight" class="width50" value="'.GETPOST('sizeheight').'">';
1667 print $formproduct->selectMeasuringUnits("size_units", "size", GETPOSTISSET('size_units') ? GETPOST('size_units', 'alpha') : (GETPOST('size_units', 'alpha') ?: '0'), 0, 2);
1668 print '</td></tr>';
1669 }
1670 if (!getDolGlobalString('PRODUCT_DISABLE_SURFACE')) {
1671 // Brut Surface
1672 print '<tr><td>'.$langs->trans("Surface").'</td><td>';
1673 print '<input name="surface" size="4" value="'.GETPOST('surface').'">';
1674 print $formproduct->selectMeasuringUnits("surface_units", "surface", GETPOSTISSET('surface_units') ? GETPOST('surface_units', 'alpha') : (GETPOST('surface_units', 'alpha') ?: '0'), 0, 2);
1675 print '</td></tr>';
1676 }
1677 if (!getDolGlobalString('PRODUCT_DISABLE_VOLUME')) {
1678 // Brut Volume
1679 print '<tr><td>'.$langs->trans("Volume").'</td><td>';
1680 print '<input name="volume" size="4" value="'.GETPOST('volume').'">';
1681 print $formproduct->selectMeasuringUnits("volume_units", "volume", GETPOSTISSET('volume_units') ? GETPOST('volume_units', 'alpha') : (GETPOST('volume_units', 'alpha') ?: '0'), 0, 2);
1682 print '</td></tr>';
1683 }
1684
1685 if (getDolGlobalString('PRODUCT_ADD_NET_MEASURE')) {
1686 // Net Measure
1687 print '<tr><td>'.$langs->trans("NetMeasure").'</td><td>';
1688 print '<input name="net_measure" size="4" value="'.GETPOST('net_measure').'">';
1689 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);
1690 print '</td></tr>';
1691 }
1692 }
1693
1694 // Units
1695 if (getDolGlobalString('PRODUCT_USE_UNITS')) {
1696 print '<tr><td>'.$langs->trans('DefaultUnitToShow').'</td>';
1697 print '<td>';
1698 print $form->selectUnits(empty($line->fk_unit) ? getDolGlobalString('PRODUCT_USE_UNITS') : $line->fk_unit, 'units');
1699 print '</td></tr>';
1700 }
1701
1702 // Custom code
1703 if (!getDolGlobalString('PRODUCT_DISABLE_CUSTOM_INFO') && empty($type)) {
1704 print '<tr><td class="wordbreak">'.$langs->trans("CustomCode").'</td><td><input name="customcode" class="maxwidth100onsmartphone" value="'.GETPOST('customcode').'"></td></tr>';
1705
1706 // Origin country
1707 print '<tr><td>'.$langs->trans("CountryOrigin").'</td>';
1708 print '<td>';
1709 print img_picto('', 'globe-americas', 'class="pictofixedwidth"');
1710 print $form->select_country((GETPOSTISSET('country_id') ? GETPOST('country_id') : $object->country_id), 'country_id', '', 0, 'minwidth300 widthcentpercentminusx maxwidth500');
1711 if ($user->admin) {
1712 print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
1713 }
1714 print '</td></tr>';
1715
1716 // State
1717 if (!getDolGlobalString('PRODUCT_DISABLE_STATE')) {
1718 print '<tr>';
1719 if (getDolGlobalString('MAIN_SHOW_REGION_IN_STATE_SELECT') && (getDolGlobalInt('MAIN_SHOW_REGION_IN_STATE_SELECT') == 1 || getDolGlobalInt('MAIN_SHOW_REGION_IN_STATE_SELECT') == 2)) {
1720 print '<td>'.$form->editfieldkey('RegionStateOrigin', 'state_id', '', $object, 0).'</td><td>';
1721 } else {
1722 print '<td>'.$form->editfieldkey('StateOrigin', 'state_id', '', $object, 0).'</td><td>';
1723 }
1724
1725 print img_picto('', 'state', 'class="pictofixedwidth"');
1726 print $formcompany->select_state($object->state_id, $object->country_code);
1727 print '</tr>';
1728 }
1729 }
1730
1731 // Quality control
1732 if (getDolGlobalString('PRODUCT_LOT_ENABLE_QUALITY_CONTROL')) {
1733 print '<tr><td>'.$langs->trans("LifeTime").'</td><td><input name="lifetime" class="maxwidth50" value="'.GETPOST('lifetime').'"></td></tr>';
1734 print '<tr><td>'.$langs->trans("QCFrequency").'</td><td><input name="qc_frequency" class="maxwidth50" value="'.GETPOST('qc_frequency').'"></td></tr>';
1735 }
1736
1737 // Other attributes
1738 $parameters = array('colspan' => ' colspan="2"', 'cols' => 2);
1739 $reshook = $hookmanager->executeHooks('formObjectOptions', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
1740 print $hookmanager->resPrint;
1741 if (empty($reshook)) {
1742 print $object->showOptionals($extrafields, 'create', $parameters);
1743 }
1744
1745 // Note (private, no output on invoices, propales...)
1746 print '<tr><td class="tdtop">'.$langs->trans("NoteNotVisibleOnBill").'</td><td>';
1747
1748 // 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.
1749 $doleditor = new DolEditor('note_private', GETPOST('note_private', 'restricthtml'), '', 140, 'dolibarr_details', '', false, true, getDolGlobalString('FCKEDITOR_ENABLE_NOTE_PRIVATE'), ROWS_8, '90%');
1750 $doleditor->Create();
1751
1752 print "</td></tr>";
1753
1754 if (isModEnabled('category')) {
1755 // Categories
1756 print '<tr><td>'.$langs->trans("Categories").'</td><td>';
1757 $cate_arbo = $form->select_all_categories(Categorie::TYPE_PRODUCT, '', 'parent', 64, 0, 3);
1758 print img_picto('', 'category', 'class="pictofixedwidth"').$form->multiselectarray('categories', $cate_arbo, GETPOST('categories', 'array'), 0, 0, 'quatrevingtpercent widthcentpercentminusx', 0, 0);
1759 print "</td></tr>";
1760 }
1761
1762 print '</table>';
1763
1764 print '<hr>';
1765
1766 if (!getDolGlobalString('PRODUCT_DISABLE_PRICES')) {
1767 if (getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
1768 // We do no show price array on create when multiprices enabled.
1769 // We must set them on prices tab.
1770 print '<table class="border centpercent">';
1771 // VAT
1772 print '<tr><td class="titlefieldcreate">'.$langs->trans("VATRate").'</td><td>';
1773 $defaultva = get_default_tva($mysoc, $mysoc);
1774 print $form->load_tva("tva_tx", $defaultva, $mysoc, $mysoc, 0, 0, '', false, 1);
1775 print ajax_combobox("tva_tx");
1776 print '</td></tr>';
1777
1778 print '</table>';
1779
1780 print '<br>';
1781 } else {
1782 print '<table class="border centpercent">';
1783
1784 // Price
1785 print '<tr><td class="titlefieldcreate">'.$langs->trans("SellingPrice").'</td>';
1786 print '<td><input name="price" class="maxwidth50" value="'.$object->price.'">';
1787 print $form->selectPriceBaseType(getDolGlobalString('PRODUCT_PRICE_BASE_TYPE'), "price_base_type");
1788 print ajax_combobox("select_price_base_type");
1789 print '</td></tr>';
1790
1791 // Min price
1792 print '<tr><td>'.$langs->trans("MinPrice").'</td>';
1793 print '<td><input name="price_min" class="maxwidth50" value="'.$object->price_min.'">';
1794 print '</td></tr>';
1795
1796 // VAT
1797 print '<tr><td>'.$langs->trans("VATRate").'</td><td>';
1798 $defaultva = get_default_tva($mysoc, $mysoc);
1799 print $form->load_tva("tva_tx", $defaultva, $mysoc, $mysoc, 0, 0, '', false, 1);
1800 print ajax_combobox("tva_tx");
1801 print '</td></tr>';
1802
1803 print '</table>';
1804
1805 print '<br>';
1806 }
1807 }
1808
1809 // Accountancy codes
1810 print '<!-- accountancy codes -->'."\n";
1811 print '<table class="border centpercent">';
1812
1813 if (!getDolGlobalString('PRODUCT_DISABLE_ACCOUNTING')) {
1814 if (isModEnabled('accounting')) {
1816 // Accountancy_code_sell
1817 print '<tr><td class="titlefieldcreate">'.$langs->trans("ProductAccountancySellCode").'</td>';
1818 print '<td>';
1819 if ($type == 0) {
1820 $accountancy_code_sell = (GETPOSTISSET('accountancy_code_sell') ? GETPOST('accountancy_code_sell', 'alpha') : getDolGlobalString("ACCOUNTING_PRODUCT_SOLD_ACCOUNT"));
1821 } else {
1822 $accountancy_code_sell = (GETPOSTISSET('accountancy_code_sell') ? GETPOST('accountancy_code_sell', 'alpha') : getDolGlobalString("ACCOUNTING_SERVICE_SOLD_ACCOUNT"));
1823 }
1824 print $formaccounting->select_account($accountancy_code_sell, 'accountancy_code_sell', 1, array(), 1, 1, 'minwidth150 maxwidth300', 1);
1825 print '</td></tr>';
1826
1827 // Accountancy_code_sell_intra
1828 if ($mysoc->isInEEC()) {
1829 print '<tr><td class="titlefieldcreate">'.$langs->trans("ProductAccountancySellIntraCode").'</td>';
1830 print '<td>';
1831 if ($type == 0) {
1832 $accountancy_code_sell_intra = (GETPOSTISSET('accountancy_code_sell_intra') ? GETPOST('accountancy_code_sell_intra', 'alpha') : getDolGlobalString("ACCOUNTING_PRODUCT_SOLD_INTRA_ACCOUNT"));
1833 } else {
1834 $accountancy_code_sell_intra = (GETPOSTISSET('accountancy_code_sell_intra') ? GETPOST('accountancy_code_sell_intra', 'alpha') : getDolGlobalString("ACCOUNTING_SERVICE_SOLD_INTRA_ACCOUNT"));
1835 }
1836 print $formaccounting->select_account($accountancy_code_sell_intra, 'accountancy_code_sell_intra', 1, array(), 1, 1, 'minwidth150 maxwidth300', 1);
1837 print '</td></tr>';
1838 }
1839
1840 // Accountancy_code_sell_export
1841 print '<tr><td class="titlefieldcreate">'.$langs->trans("ProductAccountancySellExportCode").'</td>';
1842 print '<td>';
1843 if ($type == 0) {
1844 $accountancy_code_sell_export = (GETPOST('accountancy_code_sell_export') ? GETPOST('accountancy_code_sell_export', 'alpha') : getDolGlobalString("ACCOUNTING_PRODUCT_SOLD_EXPORT_ACCOUNT"));
1845 } else {
1846 $accountancy_code_sell_export = (GETPOST('accountancy_code_sell_export') ? GETPOST('accountancy_code_sell_export', 'alpha') : getDolGlobalString("ACCOUNTING_SERVICE_SOLD_EXPORT_ACCOUNT"));
1847 }
1848 print $formaccounting->select_account($accountancy_code_sell_export, 'accountancy_code_sell_export', 1, array(), 1, 1, 'minwidth150 maxwidth300', 1);
1849 print '</td></tr>';
1850
1851 // Accountancy_code_buy
1852 print '<tr><td>'.$langs->trans("ProductAccountancyBuyCode").'</td>';
1853 print '<td>';
1854 if ($type == 0) {
1855 $accountancy_code_buy = (GETPOST('accountancy_code_buy', 'alpha') ? (GETPOST('accountancy_code_buy', 'alpha')) : getDolGlobalString("ACCOUNTING_PRODUCT_BUY_ACCOUNT"));
1856 } else {
1857 $accountancy_code_buy = (GETPOST('accountancy_code_buy', 'alpha') ? (GETPOST('accountancy_code_buy', 'alpha')) : getDolGlobalString("ACCOUNTING_SERVICE_BUY_ACCOUNT"));
1858 }
1859 print $formaccounting->select_account($accountancy_code_buy, 'accountancy_code_buy', 1, array(), 1, 1, 'minwidth150 maxwidth300', 1);
1860 print '</td></tr>';
1861
1862 // Accountancy_code_buy_intra
1863 if ($mysoc->isInEEC()) {
1864 print '<tr><td class="titlefieldcreate">'.$langs->trans("ProductAccountancyBuyIntraCode").'</td>';
1865 print '<td>';
1866 if ($type == 0) {
1867 $accountancy_code_buy_intra = (GETPOSTISSET('accountancy_code_buy_intra') ? GETPOST('accountancy_code_buy_intra', 'alpha') : getDolGlobalString("ACCOUNTING_PRODUCT_BUY_INTRA_ACCOUNT"));
1868 } else {
1869 $accountancy_code_buy_intra = (GETPOSTISSET('accountancy_code_buy_intra') ? GETPOST('accountancy_code_buy_intra', 'alpha') : getDolGlobalString("ACCOUNTING_SERVICE_BUY_INTRA_ACCOUNT"));
1870 }
1871 print $formaccounting->select_account($accountancy_code_buy_intra, 'accountancy_code_buy_intra', 1, array(), 1, 1, 'minwidth150 maxwidth300', 1);
1872 print '</td></tr>';
1873 }
1874
1875 // Accountancy_code_buy_export
1876 print '<tr><td class="titlefieldcreate">'.$langs->trans("ProductAccountancyBuyExportCode").'</td>';
1877 print '<td>';
1878 if ($type == 0) {
1879 $accountancy_code_buy_export = (GETPOST('accountancy_code_buy_export') ? GETPOST('accountancy_code_buy_export', 'alpha') : getDolGlobalString("ACCOUNTING_PRODUCT_BUY_EXPORT_ACCOUNT"));
1880 } else {
1881 $accountancy_code_buy_export = (GETPOST('accountancy_code_buy_export') ? GETPOST('accountancy_code_buy_export', 'alpha') : getDolGlobalString("ACCOUNTING_SERVICE_BUY_EXPORT_ACCOUNT"));
1882 }
1883 print $formaccounting->select_account($accountancy_code_buy_export, 'accountancy_code_buy_export', 1, array(), 1, 1, 'minwidth150 maxwidth300', 1);
1884 print '</td></tr>';
1885 } else {// For external software
1886 if (!empty($accountancy_code_sell)) {
1887 $object->accountancy_code_sell = $accountancy_code_sell;
1888 }
1889 if (!empty($accountancy_code_sell_intra)) {
1890 $object->accountancy_code_sell_intra = $accountancy_code_sell_intra;
1891 }
1892 if (!empty($accountancy_code_sell_export)) {
1893 $object->accountancy_code_sell_export = $accountancy_code_sell_export;
1894 }
1895 if (!empty($accountancy_code_buy)) {
1896 $object->accountancy_code_buy = $accountancy_code_buy;
1897 }
1898 if (!empty($accountancy_code_buy_intra)) {
1899 $object->accountancy_code_buy_intra = $accountancy_code_buy_intra;
1900 }
1901 if (!empty($accountancy_code_buy_export)) {
1902 $object->accountancy_code_buy_export = $accountancy_code_buy_export;
1903 }
1904
1905 // Accountancy_code_sell
1906 print '<tr><td class="titlefieldcreate">'.$langs->trans("ProductAccountancySellCode").'</td>';
1907 print '<td class="maxwidthonsmartphone"><input class="minwidth150" name="accountancy_code_sell" value="'.$object->accountancy_code_sell.'">';
1908 print '</td></tr>';
1909
1910 // Accountancy_code_sell_intra
1911 if ($mysoc->isInEEC()) {
1912 print '<tr><td class="titlefieldcreate">'.$langs->trans("ProductAccountancySellIntraCode").'</td>';
1913 print '<td class="maxwidthonsmartphone"><input class="minwidth150" name="accountancy_code_sell_intra" value="'.$object->accountancy_code_sell_intra.'">';
1914 print '</td></tr>';
1915 }
1916
1917 // Accountancy_code_sell_export
1918 print '<tr><td class="titlefieldcreate">'.$langs->trans("ProductAccountancySellExportCode").'</td>';
1919 print '<td class="maxwidthonsmartphone"><input class="minwidth150" name="accountancy_code_sell_export" value="'.$object->accountancy_code_sell_export.'">';
1920 print '</td></tr>';
1921
1922 // Accountancy_code_buy
1923 print '<tr><td>'.$langs->trans("ProductAccountancyBuyCode").'</td>';
1924 print '<td class="maxwidthonsmartphone"><input class="minwidth150" name="accountancy_code_buy" value="'.$object->accountancy_code_buy.'">';
1925 print '</td></tr>';
1926
1927 // Accountancy_code_buy_intra
1928 if ($mysoc->isInEEC()) {
1929 print '<tr><td class="titlefieldcreate">'.$langs->trans("ProductAccountancyBuyIntraCode").'</td>';
1930 print '<td class="maxwidthonsmartphone"><input class="minwidth150" name="accountancy_code_buy_intra" value="'.$object->accountancy_code_buy_intra.'">';
1931 print '</td></tr>';
1932 }
1933
1934 // Accountancy_code_buy_export
1935 print '<tr><td class="titlefieldcreate">'.$langs->trans("ProductAccountancyBuyExportCode").'</td>';
1936 print '<td class="maxwidthonsmartphone"><input class="minwidth150" name="accountancy_code_buy_export" value="'.$object->accountancy_code_buy_export.'">';
1937 print '</td></tr>';
1938 }
1939 }
1940 print '</table>';
1941 }
1942
1943 print dol_get_fiche_end();
1944
1945 print $form->buttonsSaveCancel("Create");
1946
1947 print '</form>';
1948 } elseif ($object->id > 0) {
1949 /*
1950 * Product card
1951 */
1952
1953 // Card in edit mode
1954 if ($action == 'edit' && $usercancreate) {
1955 //WYSIWYG Editor
1956 require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
1957
1958 if (!empty($conf->use_javascript_ajax)) {
1959 print '<script type="text/javascript">';
1960 print '$(document).ready(function () {
1961 $("#selectcountry_id").change(function () {
1962 document.formprod.action.value="edit";
1963 document.formprod.submit();
1964 });
1965 });';
1966 print '</script>'."\n";
1967 }
1968
1969 // We set country_id, country_code and country for the selected country
1970 $object->country_id = GETPOST('country_id') ? GETPOST('country_id') : $object->country_id;
1971 if ($object->country_id) {
1972 $tmparray = getCountry($object->country_id, 'all');
1973 $object->country_code = $tmparray['code'];
1974 $object->country = $tmparray['label'];
1975 }
1976
1977 $type = $langs->trans('Product');
1978 if ($object->isService()) {
1979 $type = $langs->trans('Service');
1980 }
1981 // print load_fiche_titre($langs->trans('Modify').' '.$type.' : '.(is_object($object->oldcopy)?$object->oldcopy->ref:$object->ref), "");
1982
1983 // Main official, simple, and not duplicated code
1984 print '<form action="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'" method="POST" name="formprod">'."\n";
1985 print '<input type="hidden" name="token" value="'.newToken().'">';
1986 print '<input type="hidden" name="action" value="update">';
1987 print '<input type="hidden" name="id" value="'.$object->id.'">';
1988 print '<input type="hidden" name="canvas" value="'.$object->canvas.'">';
1989
1991 $titre = $langs->trans("CardProduct".$object->type);
1992 $picto = ($object->type == Product::TYPE_SERVICE ? 'service' : 'product');
1993 print dol_get_fiche_head($head, 'card', $titre, 0, $picto, 0, '', '', 0, '', 1);
1994
1995 // Call Hook tabContentEditProduct
1996 $parameters = array();
1997 // Note that $action and $object may be modified by hook
1998 $reshook = $hookmanager->executeHooks('tabContentEditProduct', $parameters, $object, $action);
1999
2000 if (empty($reshook)) {
2001 print '<table class="border allwidth">';
2002
2003 // Ref
2004 if (!getDolGlobalString('MAIN_PRODUCT_REF_NOT_EDITABLE')) {
2005 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>';
2006 } else {
2007 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>';
2008 }
2009
2010 // Label
2011 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>';
2012
2013 // Status To sell
2014 print '<tr><td class="fieldrequired">'.$langs->trans("Status").' ('.$langs->trans("Sell").')</td><td colspan="3">';
2015 print '<select class="flat" name="statut">';
2016 if ((GETPOSTISSET('statut') && GETPOST('statut')) || (!GETPOSTISSET('statut') && $object->status)) {
2017 print '<option value="1" selected>'.$langs->trans("OnSell").'</option>';
2018 print '<option value="0">'.$langs->trans("NotOnSell").'</option>';
2019 } else {
2020 print '<option value="1">'.$langs->trans("OnSell").'</option>';
2021 print '<option value="0" selected>'.$langs->trans("NotOnSell").'</option>';
2022 }
2023 print '</select>';
2024 print '</td></tr>';
2025
2026 // Status To Buy
2027 print '<tr><td class="fieldrequired">'.$langs->trans("Status").' ('.$langs->trans("Buy").')</td><td colspan="3">';
2028 print '<select class="flat" name="statut_buy">';
2029 if ((GETPOSTISSET('statut_buy') && GETPOST('statut_buy')) || (!GETPOSTISSET('statut_buy') && $object->status_buy)) {
2030 print '<option value="1" selected>'.$langs->trans("ProductStatusOnBuy").'</option>';
2031 print '<option value="0">'.$langs->trans("ProductStatusNotOnBuy").'</option>';
2032 } else {
2033 print '<option value="1">'.$langs->trans("ProductStatusOnBuy").'</option>';
2034 print '<option value="0" selected>'.$langs->trans("ProductStatusNotOnBuy").'</option>';
2035 }
2036 print '</select>';
2037 print '</td></tr>';
2038
2039 // Batch number management
2040 if (isModEnabled('productbatch')) {
2041 if ($object->isProduct() || getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
2042 print '<tr><td>'.$langs->trans("ManageLotSerial").'</td><td>';
2043 $statutarray = array('0' => $langs->trans("ProductStatusNotOnBatch"), '1' => $langs->trans("ProductStatusOnBatch"), '2' => $langs->trans("ProductStatusOnSerial"));
2044
2045 print $form->selectarray('status_batch', $statutarray, GETPOSTISSET('status_batch') ? GETPOSTINT('status_batch') : $object->status_batch);
2046
2047 print '<span id="statusBatchWarning" class="warning" style="display: none;">';
2048 print img_warning().'&nbsp;'.$langs->trans("WarningConvertFromBatchToSerial").'</span>';
2049
2050 print '<span id="statusBatchMouvToGlobal" class="warning" style="display: none;">';
2051 print img_warning().'&nbsp;'.$langs->trans("WarningTransferBatchStockMouvToGlobal").'</span>';
2052
2053 if ($object->status_batch) {
2054 // Display message to make user know that all batch will be move into global stock
2055 print '<script type="text/javascript">
2056 $(document).ready(function() {
2057 console.log($("#statusBatchWarning"))
2058 $("#status_batch").on("change", function() {
2059 if ($("#status_batch")[0].value == 0){
2060 $("#statusBatchMouvToGlobal").show()
2061 } else {
2062 $("#statusBatchMouvToGlobal").hide()
2063 }
2064 })
2065 })</script>';
2066
2067 // 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)
2068 if ($object->status_batch == 1) {
2069 print '<script type="text/javascript">
2070 $(document).ready(function() {
2071 console.log($("#statusBatchWarning"))
2072 $("#status_batch").on("change", function() {
2073 if ($("#status_batch")[0].value == 2){
2074 $("#statusBatchWarning").show()
2075 } else {
2076 $("#statusBatchWarning").hide()
2077 }
2078 })
2079 })</script>';
2080 }
2081 }
2082
2083 print '</td></tr>';
2084 if (!empty($object->status_batch) || !empty($conf->use_javascript_ajax)) {
2085 $langs->load("admin");
2086 $tooltip = $langs->trans("GenericMaskCodes", $langs->transnoentities("Batch"), $langs->transnoentities("Batch"));
2087 $tooltip .= '<br>'.$langs->trans("GenericMaskCodes2");
2088 $tooltip .= '<br>'.$langs->trans("GenericMaskCodes3");
2089 $tooltip .= '<br>'.$langs->trans("GenericMaskCodes4a", $langs->transnoentities("Batch"), $langs->transnoentities("Batch"));
2090 $tooltip .= '<br>'.$langs->trans("GenericMaskCodes5");
2091 //$tooltip .= '<br>'.$langs->trans("GenericMaskCodes5b");
2092
2093 print '<tr><td id="mask_option">'.$langs->trans("ManageLotMask").'</td>';
2094 $mask = '';
2095 if ($object->status_batch == '1' && getDolGlobalString('PRODUCTBATCH_LOT_USE_PRODUCT_MASKS') && getDolGlobalString('PRODUCTBATCH_LOT_ADDON') == 'mod_lot_advanced') {
2096 $mask = !empty($object->batch_mask) ? $object->batch_mask : getDolGlobalString('LOT_ADVANCED_MASK');
2097 }
2098 if ($object->status_batch == '2' && getDolGlobalString('PRODUCTBATCH_SN_USE_PRODUCT_MASKS') && getDolGlobalString('PRODUCTBATCH_SN_ADDON') == 'mod_sn_advanced') {
2099 $mask = !empty($object->batch_mask) ? $object->batch_mask : getDolGlobalString('SN_ADVANCED_MASK');
2100 }
2101 $inherited_mask_lot = getDolGlobalString('LOT_ADVANCED_MASK');
2102 $inherited_mask_sn = getDolGlobalString('SN_ADVANCED_MASK');
2103 print '<td id="field_mask">';
2104 print $form->textwithpicto('<input type="text" class="flat minwidth175" name="batch_mask" id="batch_mask_input" value="'.$mask.'">', $tooltip, 1, 1);
2105 // Add javascript to sho/hide field for custom mask
2106 if (!empty($conf->use_javascript_ajax)) {
2107 print '<script type="text/javascript">
2108 $(document).ready(function() {
2109 $("#field_mask").parent().addClass("hideobject");
2110 var preselect = document.getElementById("status_batch");';
2111 if (getDolGlobalString('PRODUCTBATCH_SN_USE_PRODUCT_MASKS')) {
2112 print 'if (preselect.value == "2") {
2113 $("#field_mask").parent().removeClass("hideobject");
2114 }';
2115 }
2116 if (getDolGlobalString('PRODUCTBATCH_LOT_USE_PRODUCT_MASKS')) {
2117 print 'if (preselect.value == "1") {
2118 $("#field_mask").parent().removeClass("hideobject");
2119 }';
2120 }
2121 print '$("#status_batch").on("change", function () {
2122 var optionSelected = $("option:selected", this);
2123 var valueSelected = this.value;
2124 $("#field_mask").parent().addClass("hideobject");
2125 ';
2126 if (getDolGlobalString('PRODUCTBATCH_LOT_USE_PRODUCT_MASKS') && getDolGlobalString('PRODUCTBATCH_LOT_ADDON') == 'mod_lot_advanced') {
2127 print '
2128 if (this.value == 1) {
2129 $("#field_mask").parent().removeClass("hideobject");
2130 $("#batch_mask_input").val("'.$inherited_mask_lot.'");
2131 }
2132 ';
2133 }
2134 if (getDolGlobalString('PRODUCTBATCH_SN_USE_PRODUCT_MASKS') && getDolGlobalString('PRODUCTBATCH_SN_ADDON') == 'mod_sn_advanced') {
2135 print '
2136 if (this.value == 2) {
2137 $("#field_mask").parent().removeClass("hideobject");
2138 $("#batch_mask_input").val("'.$inherited_mask_sn.'");
2139 }
2140 ';
2141 }
2142 print '
2143 })
2144 })
2145 </script>';
2146 }
2147 print '</td></tr>';
2148 }
2149 }
2150
2151 // SellBy / EatBy mandatory list
2152 if (!empty($sellOrEatByMandatoryList)) {
2153 if (GETPOSTISSET('sell_or_eat_by_mandatory')) {
2154 $sellOrEatByMandatorySelectedId = GETPOSTINT('sell_or_eat_by_mandatory');
2155 } else {
2156 $sellOrEatByMandatorySelectedId = $object->sell_or_eat_by_mandatory;
2157 }
2158 print '<tr><td>'.$langs->trans('BatchSellOrEatByMandatoryList', $langs->transnoentities('SellByDate'), $langs->transnoentities('EatByDate')).'</td><td>';
2159 print $form->selectarray('sell_or_eat_by_mandatory', $sellOrEatByMandatoryList, $sellOrEatByMandatorySelectedId);
2160 print '</td></tr>';
2161 }
2162 }
2163
2164 // Barcode
2165 $showbarcode = isModEnabled('barcode');
2166 if (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && !$user->hasRight('barcode', 'lire_advance')) {
2167 $showbarcode = 0;
2168 }
2169
2170 if ($showbarcode) {
2171 print '<tr><td>'.$langs->trans('BarcodeType').'</td><td>';
2172 if (GETPOSTISSET('fk_barcode_type')) {
2173 $fk_barcode_type = GETPOST('fk_barcode_type');
2174 } else {
2175 $fk_barcode_type = $object->barcode_type;
2176 if (empty($fk_barcode_type) && getDolGlobalString('PRODUIT_DEFAULT_BARCODE_TYPE')) {
2177 $fk_barcode_type = getDolGlobalString('PRODUIT_DEFAULT_BARCODE_TYPE');
2178 }
2179 }
2180 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formbarcode.class.php';
2181 $formbarcode = new FormBarCode($db);
2182 print $formbarcode->selectBarcodeType($fk_barcode_type, 'fk_barcode_type', 1);
2183 print '</td></tr>';
2184 print '<tr><td>'.$langs->trans("BarcodeValue").'</td><td>';
2185 $tmpcode = GETPOSTISSET('barcode') ? GETPOST('barcode') : $object->barcode;
2186 if (empty($tmpcode) && !empty($modBarCodeProduct->code_auto)) {
2187 $tmpcode = $modBarCodeProduct->getNextValue($object, $fk_barcode_type);
2188 }
2189 print '<input class="maxwidth150 maxwidthonsmartphone" type="text" name="barcode" value="'.dol_escape_htmltag($tmpcode).'">';
2190 print '</td></tr>';
2191 }
2192
2193 // Description (used in invoice, propal...)
2194 print '<tr><td class="tdtop">'.$langs->trans("Description").'</td><td>';
2195
2196 // 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.
2197 $doleditor = new DolEditor('desc', GETPOSTISSET('desc') ? GETPOST('desc', 'restricthtml') : $object->description, '', 160, 'dolibarr_details', '', false, true, getDolGlobalInt('FCKEDITOR_ENABLE_DETAILS'), ROWS_4, '90%');
2198 $doleditor->Create();
2199
2200 print "</td></tr>";
2201 print "\n";
2202
2203 // Public Url
2204 if (!getDolGlobalString('PRODUCT_DISABLE_PUBLIC_URL')) {
2205 print '<tr><td>'.$langs->trans("PublicUrl").'</td><td>';
2206 print img_picto('', 'globe', 'class="pictofixedwidth"');
2207 print '<input type="text" name="url" class="maxwidth500 widthcentpercentminusx" value="'.(GETPOSTISSET('url') ? GETPOST('url') : $object->url).'">';
2208 print '</td></tr>';
2209 }
2210
2211 // Stock
2212 if (($object->isProduct() || getDolGlobalInt('STOCK_SUPPORTS_SERVICES')) && isModEnabled('stock')) {
2213 // Default warehouse
2214 print '<tr><td>'.$langs->trans("DefaultWarehouse").'</td><td>';
2215 print img_picto($langs->trans("DefaultWarehouse"), 'stock', 'class="pictofixedwidth"');
2216 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');
2217 print ' <a href="'.DOL_URL_ROOT.'/product/stock/card.php?action=create&amp;backtopage='.urlencode($_SERVER['PHP_SELF'].'?action=edit&id='.((int) $object->id)).'">';
2218 print '<span class="fa fa-plus-circle valignmiddle paddingleft" title="'.$langs->trans("AddWarehouse").'"></span></a>';
2219 print '</td></tr>';
2220 /*
2221 print "<tr>".'<td>'.$langs->trans("StockLimit").'</td><td>';
2222 print '<input name="seuil_stock_alerte" size="4" value="'.$object->seuil_stock_alerte.'">';
2223 print '</td>';
2224
2225 print '<td>'.$langs->trans("DesiredStock").'</td><td>';
2226 print '<input name="desiredstock" size="4" value="'.$object->desiredstock.'">';
2227 print '</td></tr>';
2228 */
2229 }
2230
2231 if ($object->isService() && isModEnabled('workstation')) {
2232 // Default workstation
2233 print '<tr><td>'.$langs->trans("DefaultWorkstation").'</td><td>';
2234 print img_picto($langs->trans("DefaultWorkstation"), 'workstation', 'class="pictofixedwidth"');
2235 print $formproduct->selectWorkstations($object->fk_default_workstation, 'fk_default_workstation', 1);
2236 print '</td></tr>';
2237 }
2238
2239 /*
2240 else
2241 {
2242 print '<input name="seuil_stock_alerte" type="hidden" value="'.$object->seuil_stock_alerte.'">';
2243 print '<input name="desiredstock" type="hidden" value="'.$object->desiredstock.'">';
2244 }*/
2245
2246 if ($object->isService()) {
2247 // Duration
2248 print '<tr><td>'.$langs->trans("Duration").'</td><td>';
2249 print '<input name="duration_value" class="width50" value="'.($object->duration_value ? $object->duration_value : '').'"> ';
2250 print $formproduct->selectMeasuringUnits("duration_unit", "time", $object->duration_unit, 0, 1);
2251
2252 // Mandatory period
2253 if ($object->duration_value > 0) {
2254 print ' &nbsp; &nbsp; ';
2255 }
2256 print '<input type="checkbox" class="valignmiddle" id="mandatoryperiod" name="mandatoryperiod"'.($object->mandatory_period == 1 ? ' checked="checked"' : '').'>';
2257 print '<label for="mandatoryperiod">';
2258 $htmltooltip = $langs->trans("mandatoryHelper");
2259 if (!getDolGlobalString('SERVICE_STRICT_MANDATORY_PERIOD')) {
2260 $htmltooltip .= '<br>'.$langs->trans("mandatoryHelper2");
2261 }
2262 print $form->textwithpicto($langs->trans("mandatoryperiod"), $htmltooltip, 1, 0);
2263 print '</label>';
2264
2265 print '</td></tr>';
2266 } else {
2267 if (!getDolGlobalString('PRODUCT_DISABLE_NATURE')) {
2268 // Nature
2269 print '<tr><td>'.$form->textwithpicto($langs->trans("NatureOfProductShort"), $langs->trans("NatureOfProductDesc")).'</td><td>';
2270 print $formproduct->selectProductNature('finished', (GETPOSTISSET('finished') ? GETPOST('finished') : $object->finished));
2271 print '</td></tr>';
2272 }
2273 }
2274
2275 if (!$object->isService() && isModEnabled('bom')) {
2276 print '<tr><td>'.$form->textwithpicto($langs->trans("DefaultBOM"), $langs->trans("DefaultBOMDesc", $langs->transnoentitiesnoconv("Finished"))).'</td><td>';
2277 $bomkey = "Bom:bom/class/bom.class.php:0:(t.status:=:1) AND (t.fk_product:=:".((int) $object->id).')';
2278 print $form->selectForForms($bomkey, 'fk_default_bom', (GETPOSTISSET('fk_default_bom') ? GETPOST('fk_default_bom') : $object->fk_default_bom), 1);
2279 print '</td></tr>';
2280 }
2281
2282 if (!$object->isService()) {
2283 if (!getDolGlobalString('PRODUCT_DISABLE_WEIGHT')) {
2284 // Brut Weight
2285 print '<tr><td>'.$langs->trans("Weight").'</td><td>';
2286 print '<input name="weight" size="5" value="'.(GETPOSTISSET('weight') ? GETPOST('weight') : $object->weight).'"> ';
2287 print $formproduct->selectMeasuringUnits("weight_units", "weight", GETPOSTISSET('weight_units') ? GETPOST('weight_units') : $object->weight_units, 0, 2);
2288 print '</td></tr>';
2289 }
2290
2291 if (!getDolGlobalString('PRODUCT_DISABLE_SIZE')) {
2292 // Brut Length
2293 print '<tr><td>'.$langs->trans("Length").' x '.$langs->trans("Width").' x '.$langs->trans("Height").'</td><td>';
2294 print '<input name="size" size="5" value="'.(GETPOSTISSET('size') ? GETPOST('size') : $object->length).'">x';
2295 print '<input name="sizewidth" size="5" value="'.(GETPOSTISSET('sizewidth') ? GETPOST('sizewidth') : $object->width).'">x';
2296 print '<input name="sizeheight" size="5" value="'.(GETPOSTISSET('sizeheight') ? GETPOST('sizeheight') : $object->height).'"> ';
2297 print $formproduct->selectMeasuringUnits("size_units", "size", GETPOSTISSET('size_units') ? GETPOST('size_units') : $object->length_units, 0, 2);
2298 print '</td></tr>';
2299 }
2300 if (!getDolGlobalString('PRODUCT_DISABLE_SURFACE')) {
2301 // Brut Surface
2302 print '<tr><td>'.$langs->trans("Surface").'</td><td>';
2303 print '<input name="surface" size="5" value="'.(GETPOSTISSET('surface') ? GETPOST('surface') : $object->surface).'"> ';
2304 print $formproduct->selectMeasuringUnits("surface_units", "surface", GETPOSTISSET('surface_units') ? GETPOST('surface_units') : $object->surface_units, 0, 2);
2305 print '</td></tr>';
2306 }
2307 if (!getDolGlobalString('PRODUCT_DISABLE_VOLUME')) {
2308 // Brut Volume
2309 print '<tr><td>'.$langs->trans("Volume").'</td><td>';
2310 print '<input name="volume" size="5" value="'.(GETPOSTISSET('volume') ? GETPOST('volume') : $object->volume).'"> ';
2311 print $formproduct->selectMeasuringUnits("volume_units", "volume", GETPOSTISSET('volume_units') ? GETPOST('volume_units') : $object->volume_units, 0, 2);
2312 print '</td></tr>';
2313 }
2314
2315 if (getDolGlobalString('PRODUCT_ADD_NET_MEASURE')) {
2316 // Net Measure
2317 print '<tr><td>'.$langs->trans("NetMeasure").'</td><td>';
2318 print '<input name="net_measure" size="5" value="'.(GETPOSTISSET('net_measure') ? GETPOST('net_measure') : $object->net_measure).'"> ';
2319 print $formproduct->selectMeasuringUnits("net_measure_units", "", GETPOSTISSET('net_measure_units') ? GETPOST('net_measure_units') : $object->net_measure_units, 0, 0);
2320 print '</td></tr>';
2321 }
2322 }
2323 // Units
2324 if (getDolGlobalString('PRODUCT_USE_UNITS')) {
2325 print '<tr><td>'.$langs->trans('DefaultUnitToShow').'</td>';
2326 print '<td>';
2327 print $form->selectUnits($object->fk_unit, 'units');
2328 print '</td></tr>';
2329 }
2330
2331 // Custom code
2332 if (!$object->isService() && !getDolGlobalString('PRODUCT_DISABLE_CUSTOM_INFO')) {
2333 print '<tr><td class="wordbreak">'.$langs->trans("CustomCode").'</td><td><input name="customcode" class="maxwidth100onsmartphone" value="'.(GETPOSTISSET('customcode') ? GETPOST('customcode') : $object->customcode).'"></td></tr>';
2334 // Origin country
2335 print '<tr><td>'.$langs->trans("CountryOrigin").'</td>';
2336 print '<td>';
2337 print img_picto('', 'globe-americas', 'class="paddingrightonly"');
2338 print $form->select_country(GETPOSTISSET('country_id') ? GETPOSTINT('country_id') : $object->country_id, 'country_id', '', 0, 'minwidth100 maxwidthonsmartphone');
2339 if ($user->admin) {
2340 print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
2341 }
2342 print '</td></tr>';
2343
2344 // State
2345 if (!getDolGlobalString('PRODUCT_DISABLE_STATE')) {
2346 print '<tr>';
2347 if (getDolGlobalString('MAIN_SHOW_REGION_IN_STATE_SELECT') && (getDolGlobalInt('MAIN_SHOW_REGION_IN_STATE_SELECT') == 1 || getDolGlobalInt('MAIN_SHOW_REGION_IN_STATE_SELECT') == 2)) {
2348 print '<td>'.$form->editfieldkey('RegionStateOrigin', 'state_id', '', $object, 0).'</td><td>';
2349 } else {
2350 print '<td>'.$form->editfieldkey('StateOrigin', 'state_id', '', $object, 0).'</td><td>';
2351 }
2352
2353 print img_picto('', 'state', 'class="pictofixedwidth"');
2354 print $formcompany->select_state(GETPOSTISSET('state_id') ? GETPOSTINT('state_id') : $object->state_id, $object->country_code);
2355 print '</td>';
2356 print '</tr>';
2357 }
2358 }
2359
2360 // Quality control
2361 if (getDolGlobalString('PRODUCT_LOT_ENABLE_QUALITY_CONTROL')) {
2362 print '<tr><td>'.$langs->trans("LifeTime").'</td><td><input name="lifetime" class="maxwidth100onsmartphone" value="'.$object->lifetime.'"></td></tr>';
2363 print '<tr><td>'.$langs->trans("QCFrequency").'</td><td><input name="qc_frequency" class="maxwidth100onsmartphone" value="'.$object->qc_frequency.'"></td></tr>';
2364 }
2365
2366 // Other attributes
2367 $parameters = array('colspan' => ' colspan="2"', 'cols' => 2);
2368 $reshook = $hookmanager->executeHooks('formObjectOptions', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
2369 print $hookmanager->resPrint;
2370 if (empty($reshook)) {
2371 print $object->showOptionals($extrafields, 'edit', $parameters);
2372 }
2373
2374 // Tags-Categories
2375 if (isModEnabled('category')) {
2376 print '<tr><td>'.$langs->trans("Categories").'</td><td>';
2377 $cate_arbo = $form->select_all_categories(Categorie::TYPE_PRODUCT, '', 'parent', 64, 0, 3);
2378 $c = new Categorie($db);
2379 $cats = $c->containing($object->id, Categorie::TYPE_PRODUCT);
2380 $arrayselected = array();
2381 if (is_array($cats)) {
2382 foreach ($cats as $cat) {
2383 $arrayselected[] = $cat->id;
2384 }
2385 }
2386 if (GETPOSTISARRAY('categories')) {
2387 foreach (GETPOST('categories', 'array') as $cat) {
2388 $arrayselected[] = $cat;
2389 }
2390 }
2391 print img_picto('', 'category', 'class="pictofixedwidth"').$form->multiselectarray('categories', $cate_arbo, $arrayselected, 0, 0, 'quatrevingtpercent widthcentpercentminusx', 0, 0);
2392 print "</td></tr>";
2393 }
2394
2395 // Note private
2396 if (getDolGlobalString('MAIN_DISABLE_NOTES_TAB')) {
2397 print '<tr><td class="tdtop">'.$langs->trans("NoteNotVisibleOnBill").'</td><td>';
2398
2399 $doleditor = new DolEditor('note_private', $object->note_private, '', 140, 'dolibarr_notes', '', false, true, getDolGlobalInt('FCKEDITOR_ENABLE_NOTE_PRIVATE'), ROWS_4, '90%');
2400 $doleditor->Create();
2401
2402 print "</td></tr>";
2403 }
2404
2405 print '</table>';
2406
2407 print '<br>';
2408
2409 print '<table class="border centpercent">';
2410
2411 if (!getDolGlobalString('PRODUCT_DISABLE_ACCOUNTING')) {
2412 if (isModEnabled('accounting')) {
2414 // Accountancy_code_sell
2415 print '<tr><td class="titlefieldcreate">'.$langs->trans("ProductAccountancySellCode").'</td>';
2416 print '<td>';
2417 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');
2418 print '</td></tr>';
2419
2420 // Accountancy_code_sell_intra
2421 if ($mysoc->isInEEC()) {
2422 print '<tr><td class="titlefieldcreate">'.$langs->trans("ProductAccountancySellIntraCode").'</td>';
2423 print '<td>';
2424 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');
2425 print '</td></tr>';
2426 }
2427
2428 // Accountancy_code_sell_export
2429 print '<tr><td class="titlefieldcreate">'.$langs->trans("ProductAccountancySellExportCode").'</td>';
2430 print '<td>';
2431 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');
2432 print '</td></tr>';
2433
2434 // Accountancy_code_buy
2435 print '<tr><td>'.$langs->trans("ProductAccountancyBuyCode").'</td>';
2436 print '<td>';
2437 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');
2438 print '</td></tr>';
2439
2440 // Accountancy_code_buy_intra
2441 if ($mysoc->isInEEC()) {
2442 print '<tr><td class="titlefieldcreate">'.$langs->trans("ProductAccountancyBuyIntraCode").'</td>';
2443 print '<td>';
2444 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');
2445 print '</td></tr>';
2446 }
2447
2448 // Accountancy_code_buy_export
2449 print '<tr><td class="titlefieldcreate">'.$langs->trans("ProductAccountancyBuyExportCode").'</td>';
2450 print '<td>';
2451 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');
2452 print '</td></tr>';
2453 } else {
2454 // For external software
2455 // Accountancy_code_sell
2456 print '<tr><td class="titlefieldcreate">'.$langs->trans("ProductAccountancySellCode").'</td>';
2457 print '<td><input name="accountancy_code_sell" class="maxwidth200" value="'.(GETPOSTISSET('accountancy_code_sell') ? GETPOST('accountancy_code_sell') : $object->accountancy_code_sell).'">';
2458 print '</td></tr>';
2459
2460 // Accountancy_code_sell_intra
2461 if ($mysoc->isInEEC()) {
2462 print '<tr><td class="titlefieldcreate">'.$langs->trans("ProductAccountancySellIntraCode").'</td>';
2463 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).'">';
2464 print '</td></tr>';
2465 }
2466
2467 // Accountancy_code_sell_export
2468 print '<tr><td class="titlefieldcreate">'.$langs->trans("ProductAccountancySellExportCode").'</td>';
2469 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).'">';
2470 print '</td></tr>';
2471
2472 // Accountancy_code_buy
2473 print '<tr><td>'.$langs->trans("ProductAccountancyBuyCode").'</td>';
2474 print '<td><input name="accountancy_code_buy" class="maxwidth200" value="'.(GETPOSTISSET('accountancy_code_buy') ? GETPOST('accountancy_code_buy') : $object->accountancy_code_buy).'">';
2475 print '</td></tr>';
2476
2477 // Accountancy_code_buy_intra
2478 if ($mysoc->isInEEC()) {
2479 print '<tr><td class="titlefieldcreate">'.$langs->trans("ProductAccountancyBuyIntraCode").'</td>';
2480 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).'">';
2481 print '</td></tr>';
2482 }
2483
2484 // Accountancy_code_buy_export
2485 print '<tr><td class="titlefieldcreate">'.$langs->trans("ProductAccountancyBuyExportCode").'</td>';
2486 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).'">';
2487 print '</td></tr>';
2488 }
2489 }
2490 print '</table>';
2491 }
2492
2493 print dol_get_fiche_end();
2494
2495 print $form->buttonsSaveCancel();
2496
2497 print '</form>';
2498 } else {
2499 // Card in view mode
2500
2501 $showbarcode = isModEnabled('barcode');
2502 if (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && !$user->hasRight('barcode', 'lire_advance')) {
2503 $showbarcode = 0;
2504 }
2505
2507 $titre = $langs->trans("CardProduct".$object->type);
2508 $picto = ($object->type == Product::TYPE_SERVICE ? 'service' : 'product');
2509
2510 print dol_get_fiche_head($head, 'card', $titre, -1, $picto);
2511
2512 $linkback = '<a href="'.DOL_URL_ROOT.'/product/list.php?restore_lastsearch_values=1&type='.$object->type.'">'.$langs->trans("BackToList").'</a>';
2513 $object->next_prev_filter = "fk_product_type:=:".((int) $object->type);
2514
2515 $shownav = 1;
2516 if ($user->socid && !in_array('product', explode(',', getDolGlobalString('MAIN_MODULES_FOR_EXTERNAL')))) {
2517 $shownav = 0;
2518 }
2519
2520 dol_banner_tab($object, 'ref', $linkback, $shownav, 'ref');
2521
2522 // Call Hook tabContentViewProduct
2523 $parameters = array();
2524 // Note that $action and $object may be modified by hook
2525 $reshook = $hookmanager->executeHooks('tabContentViewProduct', $parameters, $object, $action);
2526 if (empty($reshook)) {
2527 print '<div class="fichecenter">';
2528 print '<div class="fichehalfleft">';
2529
2530 print '<div class="underbanner clearboth"></div>';
2531 print '<table class="border tableforfield centpercent">';
2532
2533 // Type
2534 if (isModEnabled("product") && isModEnabled("service")) {
2535 $typeformat = 'select;0:'.$langs->trans("Product").',1:'.$langs->trans("Service");
2536 print '<tr><td class="titlefield">';
2537 print (!getDolGlobalString('PRODUCT_DENY_CHANGE_PRODUCT_TYPE')) ? $form->editfieldkey("Type", 'fk_product_type', $object->type, $object, $usercancreate, $typeformat) : $langs->trans('Type');
2538 print '</td><td>';
2539 print $form->editfieldval("Type", 'fk_product_type', $object->type, $object, $usercancreate, $typeformat);
2540 print '</td></tr>';
2541 }
2542
2543 if ($showbarcode) {
2544 // Barcode type
2545 print '<tr><td class="nowrap">';
2546 print '<table width="100%" class="nobordernopadding"><tr><td class="nowrap">';
2547 print $langs->trans("BarcodeType");
2548 print '</td>';
2549 if (($action != 'editbarcodetype') && $usercancreate && $createbarcode) {
2550 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>';
2551 }
2552 print '</tr></table>';
2553 print '</td><td>';
2554 if ($action == 'editbarcodetype' || $action == 'editbarcode') {
2555 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formbarcode.class.php';
2556 $formbarcode = new FormBarCode($db);
2557 }
2558
2559 $fk_barcode_type = '';
2560 if ($action == 'editbarcodetype' && is_object($formbarcode)) {
2561 print $formbarcode->formBarcodeType($_SERVER['PHP_SELF'].'?id='.$object->id, $object->barcode_type, 'fk_barcode_type');
2562 $fk_barcode_type = $object->barcode_type;
2563 } else {
2564 $object->fetchBarCode();
2565 $fk_barcode_type = $object->barcode_type;
2566 print $object->barcode_type_label ? $object->barcode_type_label : ($object->barcode ? '<div class="warning">'.$langs->trans("SetDefaultBarcodeType").'<div>' : '');
2567 }
2568 print '</td></tr>'."\n";
2569
2570 // Barcode value
2571 print '<tr><td class="nowrap">';
2572 print '<table width="100%" class="nobordernopadding"><tr><td class="nowrap">';
2573 print $langs->trans("BarcodeValue");
2574 print '</td>';
2575 if (($action != 'editbarcode') && $usercancreate && $createbarcode) {
2576 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>';
2577 }
2578 print '</tr></table>';
2579 print '</td><td class="wordbreak">';
2580 if ($action == 'editbarcode') {
2581 $tmpcode = GETPOSTISSET('barcode') ? GETPOST('barcode') : $object->barcode;
2582 if (empty($tmpcode) && !empty($modBarCodeProduct->code_auto)) {
2583 $tmpcode = $modBarCodeProduct->getNextValue($object, $fk_barcode_type);
2584 }
2585
2586 print '<form method="post" action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'">';
2587 print '<input type="hidden" name="token" value="'.newToken().'">';
2588 print '<input type="hidden" name="action" value="setbarcode">';
2589 print '<input type="hidden" name="barcode_type_code" value="'.$object->barcode_type_code.'">';
2590 print '<input class="width300" class="maxwidthonsmartphone" type="text" name="barcode" value="'.$tmpcode.'">';
2591 print '&nbsp;<input type="submit" class="button smallpaddingimp" value="'.$langs->trans("Modify").'">';
2592 print '</form>';
2593 } else {
2595 }
2596 print '</td></tr>'."\n";
2597 }
2598
2599 // Batch number management (to batch)
2600 if (isModEnabled('productbatch')) {
2601 if ($object->isProduct() || getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
2602 print '<tr><td>'.$langs->trans("ManageLotSerial").'</td><td>';
2603 print $object->getLibStatut(0, 2);
2604 print '</td></tr>';
2605 if ((($object->status_batch == '1' && getDolGlobalString('PRODUCTBATCH_LOT_USE_PRODUCT_MASKS') && getDolGlobalString('PRODUCTBATCH_LOT_ADDON') == 'mod_lot_advanced')
2606 || ($object->status_batch == '2' && getDolGlobalString('PRODUCTBATCH_SN_ADDON') == 'mod_sn_advanced' && getDolGlobalString('PRODUCTBATCH_SN_USE_PRODUCT_MASKS')))) {
2607 print '<tr><td>'.$langs->trans("ManageLotMask").'</td><td>';
2608 print $object->batch_mask;
2609 print '</td></tr>';
2610 }
2611 }
2612
2613 print '<tr><td>'.$langs->trans('BatchSellOrEatByMandatoryList', $langs->transnoentities('SellByDate'), $langs->transnoentities('EatByDate')).'</td><td>';
2614 print $object->getSellOrEatByMandatoryLabel();
2615 print '</td></tr>';
2616 }
2617
2618 if (!getDolGlobalString('PRODUCT_DISABLE_ACCOUNTING')) {
2619 // Accountancy sell code
2620 print '<tr><td class="nowrap">';
2621 print $langs->trans("ProductAccountancySellCode");
2622 print '</td><td>';
2623 if (isModEnabled('accounting')) {
2624 if (!empty($object->accountancy_code_sell)) {
2625 $accountingaccount = new AccountingAccount($db);
2626 $accountingaccount->fetch(0, $object->accountancy_code_sell, 1);
2627
2628 print $accountingaccount->getNomUrl(0, 1, 1, '', 1);
2629 }
2630 } else {
2631 print $object->accountancy_code_sell;
2632 }
2633 print '</td></tr>';
2634
2635 // Accountancy sell code intra-community
2636 if ($mysoc->isInEEC()) {
2637 print '<tr><td class="nowrap">';
2638 print $langs->trans("ProductAccountancySellIntraCode");
2639 print '</td><td>';
2640 if (isModEnabled('accounting')) {
2641 if (!empty($object->accountancy_code_sell_intra)) {
2642 $accountingaccount2 = new AccountingAccount($db);
2643 $accountingaccount2->fetch(0, $object->accountancy_code_sell_intra, 1);
2644
2645 print $accountingaccount2->getNomUrl(0, 1, 1, '', 1);
2646 }
2647 } else {
2648 print $object->accountancy_code_sell_intra;
2649 }
2650 print '</td></tr>';
2651 }
2652
2653 // Accountancy sell code export
2654 print '<tr><td class="nowrap">';
2655 print $langs->trans("ProductAccountancySellExportCode");
2656 print '</td><td>';
2657 if (isModEnabled('accounting')) {
2658 if (!empty($object->accountancy_code_sell_export)) {
2659 $accountingaccount3 = new AccountingAccount($db);
2660 $accountingaccount3->fetch(0, $object->accountancy_code_sell_export, 1);
2661
2662 print $accountingaccount3->getNomUrl(0, 1, 1, '', 1);
2663 }
2664 } else {
2665 print $object->accountancy_code_sell_export;
2666 }
2667 print '</td></tr>';
2668
2669 // Accountancy buy code
2670 print '<tr><td class="nowrap">';
2671 print $langs->trans("ProductAccountancyBuyCode");
2672 print '</td><td>';
2673 if (isModEnabled('accounting')) {
2674 if (!empty($object->accountancy_code_buy)) {
2675 $accountingaccount4 = new AccountingAccount($db);
2676 $accountingaccount4->fetch(0, $object->accountancy_code_buy, 1);
2677
2678 print $accountingaccount4->getNomUrl(0, 1, 1, '', 1);
2679 }
2680 } else {
2681 print $object->accountancy_code_buy;
2682 }
2683 print '</td></tr>';
2684
2685 // Accountancy buy code intra-community
2686 if ($mysoc->isInEEC()) {
2687 print '<tr><td class="nowrap">';
2688 print $langs->trans("ProductAccountancyBuyIntraCode");
2689 print '</td><td>';
2690 if (isModEnabled('accounting')) {
2691 if (!empty($object->accountancy_code_buy_intra)) {
2692 $accountingaccount5 = new AccountingAccount($db);
2693 $accountingaccount5->fetch(0, $object->accountancy_code_buy_intra, 1);
2694
2695 print $accountingaccount5->getNomUrl(0, 1, 1, '', 1);
2696 }
2697 } else {
2698 print $object->accountancy_code_buy_intra;
2699 }
2700 print '</td></tr>';
2701 }
2702
2703 // Accountancy buy code export
2704 print '<tr><td class="nowrap">';
2705 print $langs->trans("ProductAccountancyBuyExportCode");
2706 print '</td><td>';
2707 if (isModEnabled('accounting')) {
2708 if (!empty($object->accountancy_code_buy_export)) {
2709 $accountingaccount6 = new AccountingAccount($db);
2710 $accountingaccount6->fetch(0, $object->accountancy_code_buy_export, 1);
2711
2712 print $accountingaccount6->getNomUrl(0, 1, 1, '', 1);
2713 }
2714 } else {
2715 print $object->accountancy_code_buy_export;
2716 }
2717 print '</td></tr>';
2718 }
2719
2720 // Description
2721 print '<tr><td class="tdtop">'.$langs->trans("Description").'</td>';
2722 print '<td class="wordbreakimp wrapimp"><div class="longmessagecut">'.(dol_textishtml($object->description) ? $object->description : dol_nl2br($object->description, 1, true)).'</div></td></tr>';
2723
2724 // Public URL
2725 if (!getDolGlobalString('PRODUCT_DISABLE_PUBLIC_URL')) {
2726 print '<tr><td>'.$langs->trans("PublicUrl").'</td><td>';
2727 print dol_print_url($object->url, '_blank', 128);
2728 print '</td></tr>';
2729 }
2730
2731 // Default warehouse
2732 if (($object->isProduct() || getDolGlobalInt('STOCK_SUPPORTS_SERVICES')) && isModEnabled('stock')) {
2733 $warehouse = new Entrepot($db);
2734 $warehouse->fetch($object->fk_default_warehouse);
2735
2736 print '<tr><td>'.$langs->trans("DefaultWarehouse").'</td><td>';
2737 print(!empty($warehouse->id) ? $warehouse->getNomUrl(1) : '');
2738 print '</td>';
2739 }
2740
2741 if ($object->isService() && isModEnabled('workstation')) {
2742 $workstation = new Workstation($db);
2743 $res = $workstation->fetch($object->fk_default_workstation);
2744
2745 print '<tr><td>'.$langs->trans("DefaultWorkstation").'</td><td>';
2746 print(!empty($workstation->id) ? $workstation->getNomUrl(1) : '');
2747 print '</td>';
2748 }
2749
2750 // Parent product.
2751 if (isModEnabled('variants') && ($object->isProduct() || $object->isService())) {
2752 $combination = new ProductCombination($db);
2753
2754 if ($combination->fetchByFkProductChild($object->id) > 0) {
2755 $prodstatic = new Product($db);
2756 $prodstatic->fetch($combination->fk_product_parent);
2757
2758 // Parent product
2759 print '<tr><td>'.$langs->trans("ParentProduct").'</td><td>';
2760 print $prodstatic->getNomUrl(1);
2761 print '</td></tr>';
2762 }
2763 }
2764
2765 print '</table>';
2766 print '</div>';
2767 print '<div class="fichehalfright">';
2768
2769 print '<div class="underbanner clearboth"></div>';
2770 print '<table class="border tableforfield centpercent">';
2771
2772 if ($object->isService()) {
2773 // Duration
2774 require_once DOL_DOCUMENT_ROOT.'/core/class/cunits.class.php';
2775 $measuringUnits = new CUnits($db);
2776 $durations = [];
2777 $plural = '';
2778 if ($object->duration_value > 1) {
2779 $plural = 's';
2780 }
2781 $result = $measuringUnits->fetchAll('', 'scale', 0, 0, ['t.active' => 1, 't.unit_type' => 'time']);
2782 if ($result !== -1) {
2783 foreach ($measuringUnits->records as $record) {
2784 $durations[$record->short_label] = dol_ucfirst($record->label) . $plural;
2785 }
2786 }
2787 print '<tr><td class="titlefieldmiddle">'.$langs->trans("Duration").'</td><td>';
2788 print $object->duration_value ? $object->duration_value : '';
2789 print (!empty($object->duration_unit) && isset($durations[$object->duration_unit]) ? "&nbsp;".$langs->trans($durations[$object->duration_unit])."&nbsp;" : '');
2790
2791 // Mandatory period
2792 $htmltooltip = $langs->trans("mandatoryHelper");
2793 if (!getDolGlobalString('SERVICE_STRICT_MANDATORY_PERIOD')) {
2794 $htmltooltip .= '<br>'.$langs->trans("mandatoryHelper2");
2795 }
2796 if ($object->duration_value > 0) {
2797 print ' &nbsp; &nbsp; ';
2798 }
2799 print '<input type="checkbox" class="valignmiddle" name="mandatoryperiod"'.($object->mandatory_period == 1 ? ' checked="checked"' : '').' disabled>';
2800 print $form->textwithpicto($langs->trans("mandatoryperiod"), $htmltooltip, 1, 0);
2801
2802 print '</td></tr>';
2803 } else {
2804 if (!getDolGlobalString('PRODUCT_DISABLE_NATURE')) {
2805 // Nature
2806 print '<tr><td class="titlefieldmiddle">'.$form->textwithpicto($langs->trans("NatureOfProductShort"), $langs->trans("NatureOfProductDesc")).'</td><td>';
2807 print $object->getLibFinished();
2808 print '</td></tr>';
2809 }
2810 }
2811
2812 if (!$object->isService() && isModEnabled('bom') && $object->finished) {
2813 print '<tr><td class="titlefieldmiddle">'.$form->textwithpicto($langs->trans("DefaultBOM"), $langs->trans("DefaultBOMDesc", $langs->transnoentitiesnoconv("Finished"))).'</td><td>';
2814 if ($object->fk_default_bom) {
2815 $bom_static = new BOM($db);
2816 $bom_static->fetch($object->fk_default_bom);
2817 print $bom_static->getNomUrl(1);
2818 }
2819 print '</td></tr>';
2820 }
2821
2822 if (!$object->isService()) {
2823 // Brut Weight
2824 if (!getDolGlobalString('PRODUCT_DISABLE_WEIGHT')) {
2825 print '<tr><td class="titlefieldmiddle">'.$langs->trans("Weight").'</td><td>';
2826 if ($object->weight != '') {
2827 print $object->weight." ".measuringUnitString(0, "weight", $object->weight_units);
2828 } else {
2829 print '&nbsp;';
2830 }
2831 print "</td></tr>\n";
2832 }
2833
2834 if (!getDolGlobalString('PRODUCT_DISABLE_SIZE')) {
2835 // Brut Length
2836 print '<tr><td>'.$langs->trans("Length").' x '.$langs->trans("Width").' x '.$langs->trans("Height").'</td><td>';
2837 if ($object->length != '' || $object->width != '' || $object->height != '') {
2838 print $object->length;
2839 if ($object->width) {
2840 print " x ".$object->width;
2841 }
2842 if ($object->height) {
2843 print " x ".$object->height;
2844 }
2845 print ' '.measuringUnitString(0, "size", $object->length_units);
2846 } else {
2847 print '&nbsp;';
2848 }
2849 print "</td></tr>\n";
2850 }
2851 if (!getDolGlobalString('PRODUCT_DISABLE_SURFACE')) {
2852 // Brut Surface
2853 print '<tr><td>'.$langs->trans("Surface").'</td><td>';
2854 if ($object->surface != '') {
2855 print $object->surface." ".measuringUnitString(0, "surface", $object->surface_units);
2856 } else {
2857 print '&nbsp;';
2858 }
2859 print "</td></tr>\n";
2860 }
2861 if (!getDolGlobalString('PRODUCT_DISABLE_VOLUME')) {
2862 // Brut Volume
2863 print '<tr><td>'.$langs->trans("Volume").'</td><td>';
2864 if ($object->volume != '') {
2865 print $object->volume." ".measuringUnitString(0, "volume", $object->volume_units);
2866 } else {
2867 print '&nbsp;';
2868 }
2869 print "</td></tr>\n";
2870 }
2871
2872 if (getDolGlobalString('PRODUCT_ADD_NET_MEASURE')) {
2873 // Net Measure
2874 print '<tr><td class="titlefieldmiddle">'.$langs->trans("NetMeasure").'</td><td>';
2875 if ($object->net_measure != '') {
2876 print $object->net_measure." ".measuringUnitString($object->net_measure_units);
2877 } else {
2878 print '&nbsp;';
2879 }
2880 print '</td></tr>';
2881 }
2882 }
2883
2884 // Unit
2885 if (getDolGlobalString('PRODUCT_USE_UNITS')) {
2886 $unit = $object->getLabelOfUnit();
2887
2888 print '<tr><td>'.$langs->trans('DefaultUnitToShow').'</td><td>';
2889 if ($unit !== '') {
2890 print $langs->trans($unit);
2891 }
2892 print '</td></tr>';
2893 }
2894
2895 // Custom code
2896 if (!$object->isService() && !getDolGlobalString('PRODUCT_DISABLE_CUSTOM_INFO')) {
2897 print '<tr><td>'.$langs->trans("CustomCode").'</td><td>'.$object->customcode.'</td></tr>';
2898
2899 // Origin country code
2900 print '<tr><td>'.$langs->trans("Origin").'</td><td>'.getCountry($object->country_id, '', $db);
2901 if (!empty($object->state_id)) {
2902 print ' - '.getState($object->state_id, '0', $db);
2903 }
2904 print '</td></tr>';
2905 }
2906
2907 // Quality Control
2908 if (getDolGlobalString('PRODUCT_LOT_ENABLE_QUALITY_CONTROL')) {
2909 print '<tr><td>'.$langs->trans("LifeTime").'</td><td>'.$object->lifetime.'</td></tr>';
2910 print '<tr><td>'.$langs->trans("QCFrequency").'</td><td>'.$object->qc_frequency.'</td></tr>';
2911 }
2912
2913 // Other attributes
2914 $parameters = array();
2915 include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_view.tpl.php';
2916
2917 // Categories
2918 if (isModEnabled('category')) {
2919 print '<tr><td class="valignmiddle">'.$langs->trans("Categories").'</td><td>';
2920 print $form->showCategories($object->id, Categorie::TYPE_PRODUCT, 1);
2921 print "</td></tr>";
2922 }
2923
2924 // Note private
2925 if (getDolGlobalString('MAIN_DISABLE_NOTES_TAB')) {
2926 print '<!-- show Note --> '."\n";
2927 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";
2928 print '<!-- End show Note --> '."\n";
2929 }
2930
2931 print "</table>\n";
2932 print '</div>';
2933
2934 print '</div>';
2935 print '<div class="clearboth"></div>';
2936 }
2937
2938 print dol_get_fiche_end();
2939 }
2940 } elseif ($action != 'create') {
2941 exit;
2942 }
2943}
2944
2945$tmpcode = '';
2946if (!empty($modCodeProduct->code_auto)) {
2947 $tmpcode = $modCodeProduct->getNextValue($object, $object->type);
2948}
2949
2950$formconfirm = '';
2951
2952// Confirm delete product
2953if (($action == 'delete' && (empty($conf->use_javascript_ajax) || !empty($conf->dol_use_jmobile))) // Output when action = clone if jmobile or no js
2954 || (!empty($conf->use_javascript_ajax) && empty($conf->dol_use_jmobile))) { // Always output when not jmobile nor js
2955 $formconfirm = $form->formconfirm("card.php?id=".$object->id, $langs->trans("DeleteProduct"), $langs->trans("ConfirmDeleteProduct"), "confirm_delete", '', 0, "action-delete");
2956}
2957if ($action == 'merge') {
2958 $formquestion = array(
2959 array(
2960 'name' => 'product_origin',
2961 'label' => $langs->trans('MergeOriginProduct'),
2962 'type' => 'other',
2963 'value' => $form->select_produits(0, 'product_origin', '', 0, 0, 1, 2, '', 1, array(), 0, 1, 0, 'minwidth200', 0, '', null, 1),
2964 )
2965 );
2966 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"]."?id=".$object->id, $langs->trans("MergeProducts"), $langs->trans("ConfirmMergeProducts"), "confirm_merge", $formquestion, 'no', 1, 250);
2967}
2968
2969// Clone confirmation
2970if (($action == 'clone' && (empty($conf->use_javascript_ajax) || !empty($conf->dol_use_jmobile))) // Output when action = clone if jmobile or no js
2971 || (!empty($conf->use_javascript_ajax) && empty($conf->dol_use_jmobile))) { // Always output when not jmobile nor js
2972 // Define confirmation messages
2973 $formquestionclone = array(
2974 'text' => $langs->trans("ConfirmClone"),
2975 0 => array('type' => 'text', 'name' => 'clone_ref', 'label' => $langs->trans("NewRefForClone"), 'value' => empty($tmpcode) ? $langs->trans("CopyOf").' '.$object->ref : $tmpcode, 'morecss' => 'width150'),
2976 1 => array('type' => 'checkbox', 'name' => 'clone_content', 'label' => $langs->trans("CloneContentProduct"), 'value' => 1),
2977 2 => array('type' => 'checkbox', 'name' => 'clone_categories', 'label' => $langs->trans("CloneCategoriesProduct"), 'value' => 1),
2978 );
2979 if (getDolGlobalString('PRODUIT_MULTIPRICES')) {
2980 $formquestionclone[] = array('type' => 'checkbox', 'name' => 'clone_prices', 'label' => $langs->trans("ClonePricesProduct").' ('.$langs->trans("CustomerPrices").')', 'value' => 0);
2981 }
2982 if (getDolGlobalString('PRODUIT_SOUSPRODUITS')) {
2983 $formquestionclone[] = array('type' => 'checkbox', 'name' => 'clone_composition', 'label' => $langs->trans('CloneCompositionProduct'), 'value' => 1);
2984 }
2985 if (isModEnabled('bom') && $user->hasRight('bom', 'write')) {
2986 if ($object->fk_default_bom > 0) {
2987 $formquestionclone[] = array('type' => 'checkbox', 'name' => 'clone_defbom', 'label' => $langs->trans("CloneDefBomProduct"), 'value' => getDolGlobalInt('BOM_CLONE_DEFBOM'));
2988 }
2989 $bomstatic = new BOM($db);
2990 $bomlist = $bomstatic->fetchAll("", "", 0, 0, 'fk_product:=:'.(int) $object->id);
2991 if (is_array($bomlist) && count($bomlist) > 0) {
2992 $formquestionclone[] = array('type' => 'checkbox', 'name' => 'clone_otherboms', 'label' => $langs->trans("CloneOtherBomsProduct"), 'value' => 0);
2993 }
2994 }
2995 $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);
2996}
2997
2998// Call Hook formConfirm
2999$parameters = array('formConfirm' => $formconfirm, 'object' => $object);
3000$reshook = $hookmanager->executeHooks('formConfirm', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
3001if (empty($reshook)) {
3002 $formconfirm .= $hookmanager->resPrint;
3003} elseif ($reshook > 0) {
3004 $formconfirm = $hookmanager->resPrint;
3005}
3006
3007// Print form confirm
3008print $formconfirm;
3009
3010/*
3011 * Action bar
3012 */
3013if ($action != 'create' && $action != 'edit') {
3014 $cloneProductUrl = $_SERVER["PHP_SELF"].'?action=clone&token='.newToken();
3015 $cloneButtonId = 'action-clone-no-ajax';
3016
3017 print "\n".'<div class="tabsAction">'."\n";
3018
3019 $parameters = array();
3020 $reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
3021 if (empty($reshook)) {
3022 if ($usercancreate) {
3023 if (!isset($hookmanager->resArray['no_button_edit']) || $hookmanager->resArray['no_button_edit'] != 1) {
3024 print dolGetButtonAction('', $langs->trans('Modify'), 'default', $_SERVER["PHP_SELF"].'?action=edit&token='.newToken().'&id='.$object->id, '', $usercancreate);
3025 }
3026
3027 if (!isset($hookmanager->resArray['no_button_copy']) || $hookmanager->resArray['no_button_copy'] != 1) {
3028 if (!empty($conf->use_javascript_ajax) && empty($conf->dol_use_jmobile)) {
3029 $cloneProductUrl = '';
3030 $cloneButtonId = 'action-clone';
3031 }
3032 print dolGetButtonAction($langs->trans('ToClone'), '', 'default', $cloneProductUrl, $cloneButtonId, $usercancreate);
3033 }
3034 }
3035 $object_is_used = $object->isObjectUsed($object->id);
3036
3037 if ($usercandelete) {
3038 if (empty($object_is_used)) {
3039 if (!isset($hookmanager->resArray['no_button_delete']) || $hookmanager->resArray['no_button_delete'] != 1) {
3040 if (!empty($conf->use_javascript_ajax) && empty($conf->dol_use_jmobile)) {
3041 print dolGetButtonAction($langs->trans('Delete'), '', 'delete', '#', 'action-delete', true);
3042 } else {
3043 print dolGetButtonAction('', $langs->trans('Delete'), 'delete', $_SERVER["PHP_SELF"].'?action=delete&token='.newToken().'&id='.$object->id, '');
3044 }
3045 }
3046 } else {
3047 print dolGetButtonAction($langs->trans("ProductIsUsed"), $langs->trans('Delete'), 'delete', '#', '', false);
3048 }
3049 if (getDolGlobalInt('MAIN_FEATURES_LEVEL') > 1) {
3050 print '<a class="butActionDelete" href="card.php?action=merge&id='.$object->id.'" title="'.dol_escape_htmltag($langs->trans("MergeProducts")).'">'.$langs->trans('Merge').'</a>'."\n";
3051 }
3052 } else {
3053 print dolGetButtonAction($langs->trans("NotEnoughPermissions"), $langs->trans('Delete'), 'delete', '#', '', false);
3054 }
3055 }
3056
3057 print "\n</div>\n";
3058}
3059
3060
3061/*
3062 * All the "Add to" areas if PRODUCT_ADD_FORM_ADD_TO is set
3063 */
3064
3065if (getDolGlobalString('PRODUCT_ADD_FORM_ADD_TO') && $object->id && ($action == '' || $action == 'view') && $object->status) {
3066 //Variable used to check if any text is going to be printed
3067 $html = '';
3068 //print '<div class="fichecenter"><div class="fichehalfleft">';
3069
3070 // Propals
3071 if (isModEnabled("propal") && $user->hasRight('propal', 'creer')) {
3072 $propal = new Propal($db);
3073
3074 $langs->load("propal");
3075
3076 $otherprop = $propal->liste_array(2, 1, 0);
3077
3078 if (is_array($otherprop) && count($otherprop)) {
3079 $html .= '<tr><td style="width: 200px;">';
3080 $html .= $langs->trans("AddToDraftProposals").'</td><td>';
3081 $html .= $form->selectarray("propalid", $otherprop, 0, 1);
3082 $html .= '</td></tr>';
3083 } else {
3084 $html .= '<tr><td style="width: 200px;">';
3085 $html .= $langs->trans("AddToDraftProposals").'</td><td>';
3086 $html .= $langs->trans("NoDraftProposals");
3087 $html .= '</td></tr>';
3088 }
3089 }
3090
3091 // Commande
3092 if (isModEnabled('order') && $user->hasRight('commande', 'creer')) {
3093 $commande = new Commande($db);
3094
3095 $langs->load("orders");
3096
3097 $othercom = $commande->liste_array(2, 1, null);
3098 if (is_array($othercom) && count($othercom)) {
3099 $html .= '<tr><td style="width: 200px;">';
3100 $html .= $langs->trans("AddToDraftOrders").'</td><td>';
3101 $html .= $form->selectarray("commandeid", $othercom, 0, 1);
3102 $html .= '</td></tr>';
3103 } else {
3104 $html .= '<tr><td style="width: 200px;">';
3105 $html .= $langs->trans("AddToDraftOrders").'</td><td>';
3106 $html .= $langs->trans("NoDraftOrders");
3107 $html .= '</td></tr>';
3108 }
3109 }
3110
3111 // Factures
3112 if (isModEnabled('invoice') && $user->hasRight('facture', 'creer')) {
3113 $invoice = new Facture($db);
3114
3115 $langs->load("bills");
3116
3117 $otherinvoice = $invoice->liste_array(2, 1, null);
3118 if (is_array($otherinvoice) && count($otherinvoice)) {
3119 $html .= '<tr><td style="width: 200px;">';
3120 $html .= $langs->trans("AddToDraftInvoices").'</td><td>';
3121 $html .= $form->selectarray("factureid", $otherinvoice, 0, 1);
3122 $html .= '</td></tr>';
3123 } else {
3124 $html .= '<tr><td style="width: 200px;">';
3125 $html .= $langs->trans("AddToDraftInvoices").'</td><td>';
3126 $html .= $langs->trans("NoDraftInvoices");
3127 $html .= '</td></tr>';
3128 }
3129 }
3130
3131 // If any text is going to be printed, then we show the table
3132 if (!empty($html)) {
3133 print '<form method="POST" action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'">';
3134 print '<input type="hidden" name="token" value="'.newToken().'">';
3135 print '<input type="hidden" name="action" value="addin">';
3136
3137 print load_fiche_titre($langs->trans("AddToDraft"), '', '');
3138
3139 print dol_get_fiche_head();
3140
3141 $html .= '<tr><td class="nowrap">'.$langs->trans("Quantity").' ';
3142 $html .= '<input type="text" class="flat" name="qty" size="1" value="1"></td>';
3143 $html .= '<td class="nowrap">'.$langs->trans("ReductionShort").'(%) ';
3144 $html .= '<input type="text" class="flat" name="remise_percent" size="1" value="0">';
3145 $html .= '</td></tr>';
3146
3147 print '<table class="centpercent border">';
3148 print $html;
3149 print '</table>';
3150
3151 print '<div class="center">';
3152 print '<input type="submit" class="button button-add" value="'.$langs->trans("Add").'">';
3153 print '</div>';
3154
3155 print dol_get_fiche_end();
3156
3157 print '</form>';
3158 }
3159}
3160
3161
3162/*
3163 * Generated documents
3164 */
3165
3166if ($action != 'create' && $action != 'edit' && $action != 'delete') {
3167 print '<div class="fichecenter"><div class="fichehalfleft">';
3168 print '<a name="builddoc"></a>'; // ancre
3169
3170 // Documents
3171 $objectref = dol_sanitizeFileName($object->ref);
3172 if (!empty($conf->product->multidir_output[$object->entity])) {
3173 $filedir = $conf->product->multidir_output[$object->entity].'/'.$objectref; //Check repertories of current entities
3174 } else {
3175 $filedir = $conf->product->dir_output.'/'.$objectref;
3176 }
3177 $urlsource = $_SERVER["PHP_SELF"]."?id=".$object->id;
3178 $genallowed = $usercanread;
3179 $delallowed = $usercancreate;
3180
3181 print $formfile->showdocuments($modulepart, $object->ref, $filedir, $urlsource, $genallowed, $delallowed, '', 0, 0, 0, 28, 0, '', 0, '', $langs->getDefaultLang(), '', $object);
3182 $somethingshown = $formfile->numoffiles;
3183
3184 print '</div><div class="fichehalfright">';
3185
3186 $MAXEVENT = 10;
3187 $morehtmlcenter = '<div class="nowraponall">';
3188 $morehtmlcenter .= dolGetButtonTitle($langs->trans('FullConversation'), '', 'fa fa-comments imgforviewmode', DOL_URL_ROOT.'/product/messaging.php?id='.$object->id);
3189 $morehtmlcenter .= dolGetButtonTitle($langs->trans('SeeAll'), '', 'fa fa-bars imgforviewmode', DOL_URL_ROOT.'/product/agenda.php?id='.$object->id);
3190 $morehtmlcenter .= '</div>';
3191
3192 // List of actions on element
3193 include_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php';
3194 $formactions = new FormActions($db);
3195 $somethingshown = $formactions->showactions($object, 'product', 0, 1, '', $MAXEVENT, '', $morehtmlcenter); // Show all action for product
3196
3197 print '</div></div>';
3198}
3199
3200// End of page
3201llxFooter();
3202$db->close();
$id
Definition account.php:48
if( $user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition card.php:66
ajax_combobox($htmlname, $events=array(), $minLengthToAutocomplete=0, $forcefocus=0, $widthTypeOfAutocomplete='resolve', $idforemptyvalue='-1', $morecss='')
Convert a html select field into an ajax combobox.
Definition ajax.lib.php:459
llxFooter($comment='', $zone='private', $disabledoutputofmessages=0)
Empty footer.
Definition wrapper.php:87
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.
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.