32require
'../../main.inc.php';
33require_once DOL_DOCUMENT_ROOT.
'/product/class/html.formproduct.class.php';
34require_once DOL_DOCUMENT_ROOT.
'/core/lib/product.lib.php';
35require_once DOL_DOCUMENT_ROOT.
'/product/class/product.class.php';
36require_once DOL_DOCUMENT_ROOT.
'/categories/class/categorie.class.php';
39$langs->loadLangs(array(
'bills',
'products',
'stocks'));
43$action =
GETPOST(
'action',
'aZ09');
44$confirm =
GETPOST(
'confirm',
'alpha');
45$cancel =
GETPOST(
'cancel',
'alpha');
50if (!empty($user->socid)) {
51 $socid = $user->socid;
53$fieldvalue = (!empty($id) ? $id : (!empty($ref) ? $ref :
''));
54$fieldtype = (!empty($ref) ?
'ref' :
'rowid');
57$hookmanager->initHooks(array(
'productcompositioncard',
'globalcard'));
61if ($id > 0 || !empty($ref)) {
62 $result =
$object->fetch($id, $ref);
67$result =
restrictedArea($user,
'produit|service', $fieldvalue,
'product&product',
'',
'', $fieldtype);
70 if (
$object->type == $object::TYPE_PRODUCT) {
73 if (
$object->type == $object::TYPE_SERVICE) {
77 restrictedArea($user,
'produit|service', $fieldvalue,
'product&product',
'',
'', $fieldtype);
92$reshook = $hookmanager->executeHooks(
'doActions', [],
$object, $action);
99 if ($action ==
'add_prod' && ($user->hasRight(
'produit',
'creer') || $user->hasRight(
'service',
'creer'))) {
103 for ($i = 0; $i < $maxprod; $i++) {
112 if (
$object->error ==
"isFatherOfThis") {
113 setEventMessages($langs->trans(
"ErrorAssociationIsFatherOfThis"),
null,
'errors');
130 header(
"Location: " . $_SERVER[
"PHP_SELF"] .
'?id=' .
$object->id);
133 } elseif ($action ===
'save_composed_product') {
134 $TProduct =
GETPOST(
'TProduct',
'array');
135 if (!empty($TProduct)) {
136 foreach ($TProduct as $id_product => $row) {
137 if ($row[
'qty'] > 0) {
138 $object->update_sousproduit($id, $id_product, $row[
'qty'], isset($row[
'incdec']) ? 1 : 0);
140 $object->del_sousproduit($id, $id_product);
146 header(
"Location: " . $_SERVER[
"PHP_SELF"] .
'?id=' .
$object->id);
155$form =
new Form($db);
158$productstatic =
new Product($db);
161if ($action ==
'search') {
162 $current_lang = $langs->getDefaultLang();
164 $sql =
'SELECT DISTINCT p.rowid, p.ref, p.label, p.fk_product_type as type, p.barcode, p.price, p.price_ttc, p.price_base_type, p.entity,';
165 $sql .=
' p.fk_product_type, p.tms as datem, p.tobatch';
166 $sql .=
', p.tosell as status, p.tobuy as status_buy';
168 $sql .=
', pl.label as labelm, pl.description as descriptionm';
171 $parameters = array();
172 $reshook = $hookmanager->executeHooks(
'printFieldListSelect', $parameters,
$object);
173 $sql .= $hookmanager->resPrint;
175 $sql .=
' FROM '.MAIN_DB_PREFIX.
'product as p';
176 $sql .=
' LEFT JOIN '.MAIN_DB_PREFIX.
'categorie_product as cp ON p.rowid = cp.fk_product';
178 $sql .=
" LEFT JOIN ".MAIN_DB_PREFIX.
"product_lang as pl ON pl.fk_product = p.rowid AND lang='".($current_lang).
"'";
180 $sql .=
' WHERE p.entity IN ('.getEntity(
'product').
')';
182 $parameters = array();
183 $reshook = $hookmanager->executeHooks(
'printFieldListWhere', $parameters,
$object);
184 $sql .= $hookmanager->resPrint;
188 $params = array(
'p.ref',
'p.label',
'p.description',
'p.note');
191 $params[] =
'pl.label';
192 $params[] =
'pl.description';
193 $params[] =
'pl.note';
195 if (isModEnabled(
'barcode')) {
196 $params[] =
'p.barcode';
200 if (isModEnabled(
'category') && !empty($parent) && $parent != -1) {
201 $sql .=
" AND cp.fk_categorie ='".$db->escape($parent).
"'";
203 $sql .=
" ORDER BY p.ref ASC";
205 $resql = $db->query($sql);
208$title = $langs->trans(
'ProductServiceCard');
212 $title = $langs->trans(
'Product').
" ".$shortlabel.
" - ".$langs->trans(
'AssociatedProducts');
213 $help_url =
'EN:Module_Products|FR:Module_Produits|ES:Módulo_Productos|DE:Modul_Produkte';
216 $title = $langs->trans(
'Service').
" ".$shortlabel.
" - ".$langs->trans(
'AssociatedProducts');
217 $help_url =
'EN:Module_Services_En|FR:Module_Services|ES:Módulo_Servicios|DE:Modul_Leistungen';
220llxHeader(
'', $title, $help_url,
'', 0, 0,
'',
'',
'',
'mod-product page-composition_card');
224$titre = $langs->trans(
"CardProduct".
$object->type);
230if ($id > 0 || !empty($ref)) {
234 if ($user->hasRight(
'produit',
'lire') || $user->hasRight(
'service',
'lire')) {
235 $linkback =
'<a href="'.DOL_URL_ROOT.
'/product/list.php?restore_lastsearch_values=1&type='.
$object->type.
'">'.$langs->trans(
"BackToList").
'</a>';
238 if ($user->socid && !in_array(
'product', explode(
',',
getDolGlobalString(
'MAIN_MODULES_FOR_EXTERNAL')))) {
242 dol_banner_tab(
$object,
'ref', $linkback, $shownav,
'ref',
'');
245 print
'<div class="fichecenter">';
246 print
'<div class="fichehalfleft">';
247 print
'<div class="underbanner clearboth"></div>';
249 print
'<table class="border centpercent tableforfield">';
252 if (isModEnabled(
"product") && isModEnabled(
"service")) {
253 $typeformat =
'select;0:'.$langs->trans(
"Product").
',1:'.$langs->trans(
"Service");
254 print
'<tr><td class="titlefield">';
255 print (!
getDolGlobalString(
'PRODUCT_DENY_CHANGE_PRODUCT_TYPE')) ? $form->editfieldkey(
"Type",
'fk_product_type',
$object->type,
$object, $usercancreate, $typeformat) : $langs->trans(
'Type');
257 print $form->editfieldval(
"Type",
'fk_product_type',
$object->type,
$object, $usercancreate, $typeformat);
263 print
'</div><div class="fichehalfright">';
264 print
'<div class="underbanner clearboth"></div>';
266 print
'<table class="border centpercent tableforfield">';
271 print
'<tr><td>'.$form->textwithpicto($langs->trans(
"NatureOfProductShort"), $langs->trans(
"NatureOfProductDesc")).
'</td><td>';
272 print
$object->getLibFinished();
280 print
'<tr><td class="titlefield">'.$langs->trans(
"SellingPrice").
'</td><td>';
281 if (
$object->price_base_type ==
'TTC') {
289 print
'<tr><td>'.$langs->trans(
"MinPrice").
'</td><td>';
290 if (
$object->price_base_type ==
'TTC') {
308 $prodsfather =
$object->getFather();
309 $object->get_sousproduits_arbo();
310 $parent_label =
$object->label;
311 $prods_arbo =
$object->get_arbo_each_prod();
314 if (!empty($conf->use_javascript_ajax)) {
315 $nboflines = $prods_arbo;
316 $table_element_line=
'product_association';
318 include DOL_DOCUMENT_ROOT .
'/core/tpl/ajaxrow.tpl.php';
322 $nbofsubsubproducts = count($prods_arbo);
323 $prodschild =
$object->getChildsArbo($id, 1);
324 $nbofsubproducts = count($prodschild);
327 print
'<div class="fichecenter">';
331 print
'<table class="liste">';
332 print
'<tr class="liste_titre">';
333 print
'<td>'.$langs->trans(
'ParentProducts').
'</td>';
334 print
'<td>'.$langs->trans(
'Label').
'</td>';
335 print
'<td class="right">'.$langs->trans(
'Qty').
'</td>';
337 if (count($prodsfather) > 0) {
338 foreach ($prodsfather as $value) {
339 $idprod = $value[
"id"];
340 $productstatic->id = $idprod;
341 $productstatic->type = $value[
"fk_product_type"];
342 $productstatic->ref = $value[
'ref'];
343 $productstatic->label = $value[
'label'];
344 $productstatic->entity = $value[
'entity'];
345 $productstatic->status = $value[
'status'];
346 $productstatic->status_buy = $value[
'status_buy'];
348 print
'<tr class="oddeven">';
349 print
'<td>'.$productstatic->getNomUrl(1,
'composition').
'</td>';
350 print
'<td>'.dol_escape_htmltag($productstatic->label).
'</td>';
351 print
'<td class="right">'.dol_escape_htmltag($value[
'qty']).
'</td>';
355 print
'<tr class="oddeven">';
356 print
'<td colspan="3"><span class="opacitymedium">'.$langs->trans(
"None").
'</span></td>';
365 print
'<div class="fichecenter">';
367 $atleastonenotdefined = 0;
370 print
'<form name="formComposedProduct" action="'.$_SERVER[
'PHP_SELF'].
'" method="post">';
371 print
'<input type="hidden" name="token" value="'.newToken().
'" />';
372 print
'<input type="hidden" name="action" value="save_composed_product" />';
373 print
'<input type="hidden" name="id" value="'.$id.
'" />';
375 print
'<table id="tablelines" class="ui-sortable liste nobottom">';
377 print
'<tr class="liste_titre nodrag nodrop">';
379 print
'<td>'.$langs->trans(
'Position').
'</td>';
381 print
'<td>'.$langs->trans(
'ComposedProduct').
'</td>';
383 print
'<td>'.$langs->trans(
'Label').
'</td>';
385 print
'<td class="right" colspan="2">'.$langs->trans(
'MinSupplierPrice').
'</td>';
387 print
'<td class="right" colspan="2">'.$langs->trans(
'MinCustomerPrice').
'</td>';
389 if (isModEnabled(
'stock')) {
390 print
'<td class="right">'.$langs->trans(
'Stock').
'</td>';
393 $parameters = array();
394 $reshook = $hookmanager->executeHooks(
'printFieldListTitle', $parameters);
395 print $hookmanager->resPrint;
397 print
'<td class="right">'.$langs->trans(
'Qty').
'</td>';
399 print
'<td class="center">'.$langs->trans(
'ComposedProductIncDecStock').
'</td>';
401 print
'<td class="linecolmove" style="width: 10px"></td>';
406 if (count($prods_arbo)) {
407 foreach ($prods_arbo as $value) {
408 $productstatic->fetch($value[
'id']);
410 if ($value[
'level'] <= 1) {
411 print
'<tr id="'.$object->sousprods[$parent_label][$value[
'id']][6].
'" class="drag drop oddeven level1">';
414 print
'<td>'.$object->sousprods[$parent_label][$value[
'id']][7].
'</td>';
417 $nb_of_subproduct = $value[
'nb'];
420 print
'<td>'.$productstatic->getNomUrl(1,
'composition').
'</td>';
423 print
'<td title="'.dol_escape_htmltag($productstatic->label).
'" class="tdoverflowmax150">'.
dol_escape_htmltag($productstatic->label).
'</td>';
426 print
'<td class="right">';
427 if ($product_fourn->find_min_price_product_fournisseur($productstatic->id) > 0) {
428 print $langs->trans(
"BuyingPriceMinShort").
': ';
429 if ($product_fourn->product_fourn_price_id > 0) {
430 print $product_fourn->display_price_product_fournisseur(0, 0);
432 print $langs->trans(
"NotDefined");
434 $atleastonenotdefined++;
440 $fourn_unitprice = (!empty($product_fourn->fourn_unitprice) ? $product_fourn->fourn_unitprice : 0);
441 $fourn_remise_percent = (!empty($product_fourn->fourn_remise_percent) ? $product_fourn->fourn_remise_percent : 0);
442 $fourn_remise = (!empty($product_fourn->fourn_remise) ? $product_fourn->fourn_remise : 0);
444 $unitline =
price2num(($fourn_unitprice * (1 - ($fourn_remise_percent / 100)) - $fourn_remise),
'MU');
445 $totalline =
price2num($value[
'nb'] * ($fourn_unitprice * (1 - ($fourn_remise_percent / 100)) - $fourn_remise),
'MT');
446 $total += $totalline;
448 print
'<td class="right nowraponall">';
449 print($notdefined ?
'' : ($value[
'nb'] > 1 ? $value[
'nb'].
'x ' :
'').
'<span class="amount">'.
price($unitline, 0,
'', 0, 0, -1, $conf->currency)).
'</span>';
453 $pricesell = $productstatic->price;
455 $pricesell =
'Variable';
457 $totallinesell =
price2num($value[
'nb'] * ($pricesell),
'MT');
458 $totalsell += $totallinesell;
460 print
'<td class="right" colspan="2">';
461 print($notdefined ?
'' : ($value[
'nb'] > 1 ? $value[
'nb'].
'x ' :
''));
462 if (is_numeric($pricesell)) {
463 print
'<span class="amount">'.price($pricesell, 0,
'', 0, 0, -1, $conf->currency).
'</span>';
465 print
'<span class="opacitymedium">'.$langs->trans($pricesell).
'</span>';
470 if (isModEnabled(
'stock')) {
471 print
'<td class="right">'.$value[
'stock'].
'</td>';
475 $parameters = array();
476 $reshook=$hookmanager->executeHooks(
'printFieldListValue', $parameters, $productstatic);
477 print $hookmanager->resPrint;
480 if ($user->hasRight(
'produit',
'creer') || $user->hasRight(
'service',
'creer')) {
481 print
'<td class="center"><input type="text" value="'.$nb_of_subproduct.
'" name="TProduct['.$productstatic->id.
'][qty]" class="right width40" /></td>';
482 print
'<td class="center"><input type="checkbox" name="TProduct['.$productstatic->id.
'][incdec]" value="1" '.($value[
'incdec'] == 1 ?
'checked' :
'').
' /></td>';
484 print
'<td>'.$nb_of_subproduct.
'</td>';
485 print
'<td>'.($value[
'incdec'] == 1 ?
'x' :
'').
'</td>';
489 print
'<td class="linecolmove tdlineupdown center"></td>';
495 $hide =
' hideobject';
498 print
'<tr class="oddeven'.$hide.
'" id="sub-'.$value[
'id_parent'].
'" data-ignoreidfordnd=1>';
501 $productstatic->ref = $value[
'ref'];
508 for ($i = 0; $i < $value[
'level']; $i++) {
509 print
' ';
511 print $productstatic->getNomUrl(1,
'composition');
515 print
'<td>'.dol_escape_htmltag($productstatic->label).
'</td>';
518 print
'<td> </td>';
519 print
'<td> </td>';
521 print
'<td> </td>';
522 print
'<td> </td>';
525 if (isModEnabled(
'stock')) {
530 $parameters = array();
531 $reshook=$hookmanager->executeHooks(
'printFieldListValue', $parameters, $productstatic);
532 print $hookmanager->resPrint;
535 print
'<td class="right">'.dol_escape_htmltag($value[
'nb']).
'</td>';
538 print
'<td> </td>';
541 print
'<td> </td>';
550 print
'<tr class="liste_total">';
556 print
'<td class="liste_total"></td>';
559 print
'<td class="liste_total"></td>';
562 print
'<td class="liste_total right">';
563 print $langs->trans(
"TotalBuyingPriceMinShort");
566 print
'<td class="liste_total right">';
567 if ($atleastonenotdefined) {
568 print $langs->trans(
"Unknown").
' ('.$langs->trans(
"SomeSubProductHaveNoPrices").
')';
570 print($atleastonenotdefined ?
'' :
price($total, 0,
'', 0, 0, -1, $conf->currency));
574 print
'<td class="liste_total right">';
575 print $langs->trans(
"TotalSellingPriceMinShort");
578 print
'<td class="liste_total right">';
579 if ($atleastonenotdefined) {
580 print $langs->trans(
"Unknown").
' ('.$langs->trans(
"SomeSubProductHaveNoPrices").
')';
582 print($atleastonenotdefined ?
'' :
price($totalsell, 0,
'', 0, 0, -1, $conf->currency));
586 if (isModEnabled(
'stock')) {
587 print
'<td class="liste_total right"> </td>';
592 print
'<td class="center">';
593 if ($user->hasRight(
'produit',
'creer') || $user->hasRight(
'service',
'creer')) {
594 print
'<input type="submit" class="button button-save" value="'.$langs->trans(
"Save").
'">';
603 if (isModEnabled(
'stock')) {
607 print
'<tr class="oddeven">';
608 print
'<td colspan="'.$colspan.
'"><span class="opacitymedium">'.$langs->trans(
"None").
'</span></td>';
624 if ((empty($action) || $action ==
'view' || $action ==
'edit' || $action ==
'search' || $action ==
're-edit') && ($user->hasRight(
'produit',
'creer') || $user->hasRight(
'service',
'creer'))) {
628 if (isModEnabled(
'category')) {
633 print
'<form action="'.DOL_URL_ROOT.
'/product/composition/card.php?id='.$id.
'" method="POST">';
634 print
'<input type="hidden" name="action" value="search">';
635 print
'<input type="hidden" name="id" value="'.$id.
'">';
636 print
'<div class="inline-block">';
637 print
'<input type="hidden" name="token" value="'.newToken().
'">';
638 print $langs->trans(
"KeywordFilter").
': ';
639 print
'<input type="text" name="key" value="'.$key.
'"> ';
641 if (isModEnabled(
'category')) {
642 require_once DOL_DOCUMENT_ROOT.
'/categories/class/categorie.class.php';
643 print
'<div class="inline-block">'.$langs->trans(
"CategoryFilter").
': ';
644 print $form->select_all_categories(Categorie::TYPE_PRODUCT, $parent,
'parent').
' </div>';
647 print
'<div class="inline-block">';
648 print
'<input type="submit" class="button small" value="'.$langs->trans(
"Search").
'">';
655 if ($action ==
'search') {
657 print
'<form action="'.DOL_URL_ROOT.
'/product/composition/card.php?id='.$id.
'" method="post">';
658 print
'<input type="hidden" name="token" value="'.newToken().
'">';
659 print
'<input type="hidden" name="action" value="add_prod">';
660 print
'<input type="hidden" name="id" value="'.$id.
'">';
662 print
'<table class="noborder centpercent">';
663 print
'<tr class="liste_titre">';
664 print
'<th class="liste_titre">'.$langs->trans(
"ComposedProduct").
'</td>';
665 print
'<th class="liste_titre">'.$langs->trans(
"Label").
'</td>';
667 print
'<th class="liste_titre right">'.$langs->trans(
"Qty").
'</td>';
668 print
'<th class="center">'.$langs->trans(
'ComposedProductIncDecStock').
'</th>';
671 $num = $db->num_rows($resql);
675 print
'<tr><td colspan="4">'.$langs->trans(
"NoMatchFound").
'</td></tr>';
680 while ($i < min($num, $MAX)) {
681 $objp = $db->fetch_object($resql);
682 if ($objp->rowid != $id) {
685 $prod_arbo->id = $objp->rowid;
688 if ($prod_arbo->type == 2 || $prod_arbo->type == 3) {
690 $prod_arbo->get_sousproduits_arbo();
692 $prods_arbo = $prod_arbo->get_arbo_each_prod();
693 if (count($prods_arbo) > 0) {
694 foreach ($prods_arbo as $key => $value) {
695 if ($value[1] == $id) {
708 print
'<tr class="oddeven">';
710 $productstatic->id = $objp->rowid;
711 $productstatic->ref = $objp->ref;
712 $productstatic->label = $objp->label;
713 $productstatic->type = $objp->type;
714 $productstatic->entity = $objp->entity;
715 $productstatic->status = $objp->status;
716 $productstatic->status_buy = $objp->status_buy;
717 $productstatic->status_batch = $objp->tobatch;
719 print
'<td>'.$productstatic->getNomUrl(1,
'', 24).
'</td>';
720 $labeltoshow = $objp->label;
722 $labeltoshow = $objp->labelm;
725 print
'<td>'.$labeltoshow.
'</td>';
728 if (
$object->is_sousproduit($id, $objp->rowid)) {
730 $qty =
$object->is_sousproduit_qty;
731 $incdec =
$object->is_sousproduit_incdec;
741 print
'<td class="right"><input type="hidden" name="prod_id_'.$i.
'" value="'.$objp->rowid.
'"><input type="text" size="2" name="prod_qty_'.$i.
'" value="'.($qty ? $qty :
'').
'"></td>';
744 print
'<td class="center">';
746 print
'<input type="checkbox" name="prod_incdec_'.$i.
'" value="1" '.($incdec ?
'checked' :
'').
'>';
749 print
'<input type="checkbox" name="prod_incdec_'.$i.
'" value="1" checked>';
759 print
'<tr class="oddeven">';
760 print
'<td><span class="opacitymedium">'.$langs->trans(
"More").
'...</span></td>';
770 print
'<input type="hidden" name="max_prod" value="'.$i.
'">';
773 print
'<div class="center">';
774 print
'<input type="submit" class="button button-save" name="save" value="'.$langs->trans(
"Add").
'/'.$langs->trans(
"Update").
'">';
775 print
'<input type="submit" class="button button-cancel" name="cancel" value="'.$langs->trans(
"Cancel").
'">';
if( $user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
ajax_combobox($htmlname, $events=array(), $minLengthToAutocomplete=0, $forcefocus=0, $widthTypeOfAutocomplete='resolve', $idforemptyvalue='-1', $morecss='')
Convert a html select field into an ajax combobox.
if(!defined('NOREQUIRESOC')) if(!defined( 'NOREQUIRETRAN')) if(!defined('NOTOKENRENEWAL')) if(!defined( 'NOREQUIREMENU')) if(!defined('NOREQUIREHTML')) if(!defined( 'NOREQUIREAJAX')) llxHeader()
Empty header.
Class to manage predefined suppliers products.
Class to manage products or services.
const TYPE_PRODUCT
Regular product.
const TYPE_SERVICE
Service.
load_fiche_titre($title, $morehtmlright='', $picto='generic', $pictoisfullpath=0, $id='', $morecssontable='', $morehtmlcenter='')
Load a title with picto.
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limittoshow=0, $moretabssuffix='', $dragdropfile=0)
Show tabs of a record.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_get_fiche_end($notab=0)
Return tab footer of a card.
natural_search($fields, $value, $mode=0, $nofirstand=0)
Generate natural SQL search string for a criteria (this criteria can be tested on one or several fiel...
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0)
Set event messages in dol_events session object.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '…' if string larger than length.
getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
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.
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.