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