Merge branch 'develop' into patch-8

This commit is contained in:
Laurent Destailleur 2020-08-16 23:54:04 +02:00 committed by GitHub
commit 0d59ac34d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
59 changed files with 1258 additions and 407 deletions

View File

@ -3,13 +3,13 @@
![Downloads per day](https://img.shields.io/sourceforge/dw/dolibarr.svg)
![Build status](https://img.shields.io/travis/Dolibarr/dolibarr/develop.svg)
Dolibarr ERP & CRM is a modern software package to manage your organization's activity (contacts, suppliers, invoices, orders, stocks, agenda…).
Dolibarr ERP & CRM is a modern software package that helps manage your organization's activity (contacts, suppliers, invoices, orders, stocks, agenda…).
It's an Open Source Software (written in PHP with optional JavaScript enhancements) designed for small, medium or large companies, foundations and freelancers.
It's an Open Source Software suite (written in PHP with optional JavaScript enhancements) designed for small, medium or large companies, foundations and freelancers.
You can freely use, study, modify or distribute it according to its Free Software licence.
You can freely use, study, modify or distribute it according to its licence.
You can use it as a standalone application or as a web application to be able to access it from the Internet or a LAN.
You can use it as a standalone application or as a web application to access it from the Internet or a LAN.
![ScreenShot](https://www.dolibarr.org/images/dolibarr_screenshot1_1920x1080.jpg)
@ -35,11 +35,15 @@ Releases can be downloaded from [official website](https://www.dolibarr.org/).
### Advanced setup
You can use a Web server and a supported database (MariaDB, MySQL or PostgreSQL) to install the standard version.
You can use a web server and a supported database (MariaDB, MySQL or PostgreSQL) to install the standard version.
On GNU/Linux, first check if your distribution has already packaged Dolibarr.
#### Generic install steps:
- Check that your installed PHP version is supported [see PHP support](https://wiki.dolibarr.org/index.php/Versions).
- Uncompress the downloaded .zip archive to copy the "dolibarr/htdocs" directory and all its files inside your web server root or get the files directly from GitHub (recommanded if you known git):
- Uncompress the downloaded .zip archive to copy the "dolibarr/htdocs" directory and all its files inside your web server root or get the files directly from GitHub (recommanded if you know git as it makes it easier if you want to upgrade later):
`git clone https://github.com/dolibarr/dolibarr -b x.y` (where x.y is main version like 3.6, 9.0, ...)
@ -70,11 +74,11 @@ If you don't have time to install it yourself, you can try some commercial 'read
## UPGRADING
- At first make a backup of your Dolibarr files & than see https://wiki.dolibarr.org/index.php/Installation_-_Upgrade#Upgrade_Dolibarr
- At first make a backup of your Dolibarr files & then read https://wiki.dolibarr.org/index.php/Installation_-_Upgrade#Upgrade_Dolibarr
- Check that your installed PHP version is supported by the new version [see PHP support](./doc/phpmatrix.md).
- Overwrite all old files from 'dolibarr' directory with files provided into the new version's package.
- At first next access, Dolibarr will redirect your to the "install/" page to follow the upgrade process.
 If an `install.lock` file exists to lock any other upgrade process, the application will ask you to remove the file manually (you should find the `install.lock` file into the directory used to store generated and uploaded documents, in most cases, it is the directory called "*documents*").
- At first next access, Dolibarr will redirect you to the "install/" page to follow the upgrade process.
 If an `install.lock` file exists to lock any other upgrade process, the application will ask you to remove the file manually (you should find the `install.lock` file in the directory used to store generated and uploaded documents, in most cases, it is the directory called "*documents*").
*Note: migration process can be safely done multiple times by calling the `/install/index.php` page*
@ -139,7 +143,7 @@ See the [ChangeLog](https://github.com/Dolibarr/dolibarr/blob/develop/ChangeLog)
- Highly customizable: enable only the modules you need, add user personalized fields, choose your skin, several menu managers (can be used by internal users as a back-office with a particular menu, or by external users as a front-office with another one)
- APIs
- An easy to understand, maintain and develop code (PHP with no heavy framework; trigger and hook architecture)
- Code that is easy to understand, maintain and develop (PHP with no heavy framework; trigger and hook architecture)
- Support a lot of country specific features:
- Spanish Tax RE and ISPF
- French NPR VAT rate (VAT called "Non Perçue Récupérable" for DOM-TOM)
@ -149,7 +153,7 @@ See the [ChangeLog](https://github.com/Dolibarr/dolibarr/blob/develop/ChangeLog)
- Compatible with [European directives](http://europa.eu/legislation_summaries/taxation/l31057_en.htm) (2006/112/CE ... 2010/45/UE)
- Compatible with European GDPR rules
- ...
- PDF or ODT generation for invoice, proposals, orders...
- Flexible PDF & ODT generation for invoices, proposals, orders...
- …
### System Environment / Requirements
@ -167,12 +171,12 @@ These are features that Dolibarr does **not** yet fully support:
- Tasks dependencies in projects
- Payroll module
- No native embedded Webmail
- No native embedded Webmail, but you can send email to contacts in Dolibarr with e.g. offers, invoices, etc.
- Dolibarr can't do coffee (yet)
## DOCUMENTATION
Administrator, user, developer and translator's documentations are available along with other community resources on the [Wiki](https://wiki.dolibarr.org).
Administrator, user, developer and translator's documentations are available along with other community resources in the [Wiki](https://wiki.dolibarr.org).
## CONTRIBUTING
@ -182,7 +186,7 @@ This project exists thanks to all the people who contribute. [[Contribute](https
## CREDITS
Dolibarr is the work of many contributors over the years and uses some fine libraries.
Dolibarr is the work of many contributors over the years and uses some fine PHP libraries.
See [COPYRIGHT](https://github.com/Dolibarr/dolibarr/blob/develop/COPYRIGHT) file.

View File

@ -14,7 +14,7 @@ Architecture: all
Depends: libapache2-mod-php5 | libapache2-mod-php5filter | php5-cgi | php5-fpm | php5 | libapache2-mod-php | libapache2-mod-phpfilter | php-cgi | php-fpm | php,
php5-cli | php-cli,
# Required PHP extensions
php5-mysql | php5-mysqli | php-mysql | php-mysqli, php5-curl | php-curl, php5-gd | php-gd, php5-ldap | php-gd,
php5-mysql | php5-mysqli | php-mysql | php-mysqli, php5-curl | php-curl, php5-gd | php-gd, php5-ldap | php-gd, php5-zip | php-zip,
# Required PHP libraries
php-pear, php-mail-mime,
# php-tcpdf,

View File

@ -79,6 +79,7 @@ $object = new Fiscalyear($db);
$max = 100;
$form = new Form($db);
$fiscalyearstatic = new Fiscalyear($db);
$title = $langs->trans('AccountingPeriods');
$helpurl = "";
@ -132,13 +133,15 @@ if ($result)
print '</tr>';
if ($num) {
$fiscalyearstatic = new Fiscalyear($db);
while ($i < $num && $i < $max) {
$obj = $db->fetch_object($result);
$fiscalyearstatic->id = $obj->rowid;
print '<tr class="oddeven">';
print '<td><a href="fiscalyear_card.php?id='.$obj->rowid.'">'.img_object($langs->trans("ShowFiscalYear"), "technic").' '.$obj->rowid.'</a></td>';
print '<td>';
print $fiscalyearstatic->getNomUrl(1);
print '</td>';
print '<td class="left">'.$obj->label.'</td>';
print '<td class="left">'.dol_print_date($db->jdate($obj->date_start), 'day').'</td>';
print '<td class="left">'.dol_print_date($db->jdate($obj->date_end), 'day').'</td>';

View File

@ -38,7 +38,7 @@ require_once DOL_DOCUMENT_ROOT.'/admin/dolistore/class/dolistore.class.php';
// Load translation files required by the page
$langs->loadLangs(array("errors", "admin", "modulebuilder"));
$mode = GETPOSTISSET('mode') ? GETPOST('mode', 'alpha') : 'commonkanban';
$mode = GETPOSTISSET('mode') ? GETPOST('mode', 'alpha') : (empty($conf->global->MAIN_MODULE_SETUP_ON_LIST_BY_DEFAULT) ? 'commonkanban' : 'common');
if (empty($mode)) $mode = 'common';
$action = GETPOST('action', 'alpha');
//var_dump($_POST);exit;

View File

@ -192,10 +192,10 @@ if (empty($reshook))
$error = 0;
// Set if we used free entry or predefined product
$qty = GETPOST('qty', 'int');
$qty = price2num(GETPOST('qty', 'int'));
$qty_frozen = GETPOST('qty_frozen', 'int');
$disable_stock_change = GETPOST('disable_stock_change', 'int');
$efficiency = GETPOST('efficiency', 'int');
$efficiency = price2num(GETPOST('efficiency', 'int'));
if ($qty == '') {
setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Qty')), null, 'errors');
@ -219,6 +219,8 @@ if (empty($reshook))
unset($_POST['qty']);
unset($_POST['qty_frozen']);
unset($_POST['disable_stock_change']);
$object->fetchLines();
}
}
}
@ -541,7 +543,7 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea
if (!empty($object->table_element_line))
{
print ' <form name="addproduct" id="addproduct" action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.(($action != 'editline') ? '#addline' : '#line_'.GETPOST('lineid', 'int')).'" method="POST">
print ' <form name="addproduct" id="addproduct" action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.(($action != 'editline') ? '#addline' : '').'" method="POST">
<input type="hidden" name="token" value="' . newToken().'">
<input type="hidden" name="action" value="' . (($action != 'editline') ? 'addline' : 'updateline').'">
<input type="hidden" name="mode" value="">

View File

@ -113,7 +113,7 @@ if ($conf->global->PRODUCT_USE_UNITS)
{
$coldisplay++;
print '<td class="nobottom linecoluseunit left">';
print $form->selectUnits($line->fk_unit, "units");
print $form->selectUnits(empty($line->fk_unit) ? $conf->global->PRODUCT_USE_UNITS : $line->fk_unit, "units");
print '</td>';
}

View File

@ -5,7 +5,7 @@
* Copyright (C) 2005 Marc Barilley / Ocebo <marc@ocebo.com>
* Copyright (C) 2005-2015 Regis Houssin <regis.houssin@inodbox.com>
* Copyright (C) 2006 Andre Cianfarani <acianfa@free.fr>
* Copyright (C) 2010-2012 Juanjo Menent <jmenent@2byte.es>
* Copyright (C) 2010-2020 Juanjo Menent <jmenent@2byte.es>
* Copyright (C) 2012 Christophe Battarel <christophe.battarel@altairis.fr>
* Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
* Copyright (C) 2013 Cédric Salvador <csalvador@gpcsolutions.fr>
@ -545,6 +545,8 @@ if (!$sall)
$sql .= ' f.paye, f.fk_statut, f.close_code,';
$sql .= ' f.datec, f.tms, f.date_closing,';
$sql .= ' f.retained_warranty, f.retained_warranty_date_limit, f.situation_final, f.situation_cycle_ref, f.situation_counter,';
$sql .= ' f.fk_user_author, f.fk_multicurrency, f.multicurrency_code, f.multicurrency_tx, f.multicurrency_total_ht, f.multicurrency_total_tva,';
$sql .= ' f.multicurrency_total_tva, f.multicurrency_total_ttc,';
$sql .= ' s.rowid, s.nom, s.email, s.town, s.zip, s.fk_pays, s.client, s.fournisseur, s.code_client, s.code_fournisseur, s.code_compta, s.code_compta_fournisseur,';
$sql .= ' typent.code,';
$sql .= ' state.code_departement, state.nom,';

View File

@ -38,8 +38,6 @@ require_once DOL_DOCUMENT_ROOT.'/societe/class/companybankaccount.class.php';
require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.class.php';
require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.facture.class.php';
if (!$user->rights->facture->lire) accessforbidden();
// Load translation files required by the page
$langs->loadLangs(array('bills', 'banks', 'withdrawals', 'companies'));
@ -51,7 +49,6 @@ $type = GETPOST('type', 'aZ09');
$fieldid = (!empty($ref) ? 'ref' : 'rowid');
if ($user->socid) $socid = $user->socid;
$result = restrictedArea($user, 'facture', $id, '', '', 'fk_soc', $fieldid);
if ($type == 'bank-transfer') {
$object = new FactureFournisseur($db);
@ -63,6 +60,7 @@ if ($type == 'bank-transfer') {
if ($id > 0 || !empty($ref))
{
$ret = $object->fetch($id, $ref);
$isdraft = (($object->statut == FactureFournisseur::STATUS_DRAFT) ? 1 : 0);
if ($ret > 0)
{
$object->fetch_thirdparty();
@ -71,6 +69,13 @@ if ($id > 0 || !empty($ref))
$hookmanager->initHooks(array('directdebitcard', 'globalcard'));
if ($type == 'bank-transfer') {
$result = restrictedArea($user, 'fournisseur', $id, 'facture_fourn', 'facture', 'fk_soc', $fieldid, $isdraft);
if (!$user->rights->fournisseur->facture->lire) accessforbidden();
} else {
$result = restrictedArea($user, 'facture', $id, '', '', 'fk_soc', $fieldid, $isdraft);
if (!$user->rights->facture->lire) accessforbidden();
}
/*

View File

@ -2,7 +2,7 @@
/* Copyright (C) 2001-2005 Rodolphe Quiedeville <rodolphe@quiedeville.org>
* Copyright (C) 2004-2013 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2005-2015 Regis Houssin <regis.houssin@inodbox.com>
* Copyright (C) 2015-2016 Juanjo Menent <jmenent@2byte.es>
* Copyright (C) 2015-2020 Juanjo Menent <jmenent@2byte.es>
* Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
* Copyright (C) 2015 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
* Copyright (C) 2016 Marcos García <marcosgdf@gmail.com>
@ -161,6 +161,14 @@ if (!empty($conf->facture->enabled) && $user->rights->facture->lire)
$reshook = $hookmanager->executeHooks('printFieldListWhereCustomerDraft', $parameters);
$sql .= $hookmanager->resPrint;
$sql.= " GROUP BY f.rowid, f.ref, f.datef, f.total, f.tva, f.total_ttc, f.ref_client, f.type, ";
$sql.= "s.email, s.nom, s.rowid, s.code_client, s.code_compta, s.code_fournisseur, s.code_compta_fournisseur";
// Add Group from hooks
$parameters = array();
$reshook = $hookmanager->executeHooks('printFieldListGroupByCustomerDraft', $parameters);
$sql .= $hookmanager->resPrint;
$resql = $db->query($sql);
if ($resql)
@ -458,7 +466,7 @@ if ((!empty($conf->fournisseur->enabled) && empty($conf->global->MAIN_USE_NEW_SU
$sql .= $hookmanager->resPrint;
$sql .= " GROUP BY ff.rowid, ff.ref, ff.fk_statut, ff.libelle, ff.total_ht, ff.tva, ff.total_tva, ff.total_ttc, ff.tms, ff.paye,";
$sql .= " s.nom, s.rowid, s.code_fournisseur, s.code_compta_fournisseur";
$sql .= " s.nom, s.rowid, s.code_fournisseur, s.code_compta_fournisseur, s.email";
$sql .= " ORDER BY ff.tms DESC ";
$sql .= $db->plimit($max, 0);

View File

@ -206,7 +206,13 @@ if (is_array($extrafields->attributes[$object->table_element]['label']) && count
foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $val)
{
if (!empty($extrafields->attributes[$object->table_element]['list'][$key]))
$arrayfields["ef.".$key] = array('label'=>$extrafields->attributes[$object->table_element]['label'][$key], 'checked'=>(($extrafields->attributes[$object->table_element]['list'][$key] < 0) ? 0 : 1), 'position'=>$extrafields->attributes[$object->table_element]['pos'][$key], 'enabled'=>(abs($extrafields->attributes[$object->table_element]['list'][$key]) != 3 && $extrafields->attributes[$object->table_element]['perms'][$key]));
$arrayfields["ef.".$key] = array(
'label'=>$extrafields->attributes[$object->table_element]['label'][$key],
'checked'=>(($extrafields->attributes[$object->table_element]['list'][$key] < 0) ? 0 : 1),
'position'=>$extrafields->attributes[$object->table_element]['pos'][$key],
'enabled'=>(abs($extrafields->attributes[$object->table_element]['list'][$key]) != 3 && $extrafields->attributes[$object->table_element]['perms'][$key]),
'langfile'=>$extrafields->attributes[$object->table_element]['langfile'][$key],
);
}
}
$object->fields = dol_sort_array($object->fields, 'position');

View File

@ -355,8 +355,8 @@ class CMailFile
}
$smtps->setSubject($subjecttouse);
$smtps->setTO($this->getValidAddress($this->to, 0, 1));
$smtps->setFrom($this->getValidAddress($this->from, 0, 1));
$smtps->setTO($this->getValidAddress($this->addr_to, 0, 1));
$smtps->setFrom($this->getValidAddress($this->addr_from, 0, 1));
$smtps->setTrackId($this->trackid);
$smtps->setReplyTo($this->getValidAddress($this->replyto, 0, 1));
@ -402,7 +402,7 @@ class CMailFile
$smtps->setDeliveryReceipt($this->deliveryreceipt);
$host = dol_getprefix('email');
$this->msgid = time().'.SMTPs-dolibarr-'.$trackid.'@'.$host;
$this->msgid = time().'.SMTPs-dolibarr-'.$this->trackid.'@'.$host;
$this->smtps = $smtps;
} elseif ($this->sendmode == 'swiftmailer') {
@ -786,14 +786,14 @@ class CMailFile
$from = $this->smtps->getFrom('org');
if ($res && !$from)
{
$this->error = "Failed to send mail with smtps lib to HOST=".$server.", PORT=".$conf->global->$keyforsmtpport."<br>Sender address '$from' invalid";
$this->error = "Failed to send mail with smtps lib to HOST=".$server.", PORT=".$conf->global->$keyforsmtpport." - Sender address '$from' invalid";
dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_ERR);
$res = false;
}
$dest = $this->smtps->getTo();
if ($res && !$dest)
{
$this->error = "Failed to send mail with smtps lib to HOST=".$server.", PORT=".$conf->global->$keyforsmtpport."<br>Recipient address '$dest' invalid";
$this->error = "Failed to send mail with smtps lib to HOST=".$server.", PORT=".$conf->global->$keyforsmtpport." - Recipient address '$dest' invalid";
dol_syslog("CMailFile::sendfile: mail end error=".$this->error, LOG_ERR);
$res = false;
}
@ -814,7 +814,7 @@ class CMailFile
$res = true;
} else {
if (empty($this->error)) $this->error = $result;
dol_syslog("CMailFile::sendfile: mail end error with smtps lib to HOST=".$server.", PORT=".$conf->global->$keyforsmtpport."<br>".$this->error, LOG_ERR);
dol_syslog("CMailFile::sendfile: mail end error with smtps lib to HOST=".$server.", PORT=".$conf->global->$keyforsmtpport." - ".$this->error, LOG_ERR);
$res = false;
}
}

View File

@ -35,6 +35,8 @@ class Fiscalyear extends CommonObject
*/
public $element = 'fiscalyear';
public $picto = 'technic';
/**
* @var string Name of table without prefix where object is stored
*/
@ -97,6 +99,7 @@ class Fiscalyear extends CommonObject
public $statuts = array();
public $statuts_short = array();
/**
* Constructor
*
@ -267,6 +270,78 @@ class Fiscalyear extends CommonObject
}
}
/**
* Return clicable link of object (with eventually picto)
*
* @param int $withpicto Add picto into link
* @param int $notooltip 1=Disable tooltip
* @param int $save_lastsearch_value -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
* @return string String with URL
*/
public function getNomUrl($withpicto = 0, $notooltip = 0, $save_lastsearch_value = -1)
{
global $conf, $langs, $user;
if (empty($this->ref)) $this->ref = $this->id;
if (!empty($conf->dol_no_mouse_hover)) $notooltip = 1; // Force disable tooltips
$result = '';
$url = DOL_URL_ROOT.'/accountancy/admin/fiscalyear_card.php?id='.$this->id;
if (!$user->rights->accounting->fiscalyear->write)
$option = 'nolink';
if ($option !== 'nolink')
{
// Add param to save lastsearch_values or not
$add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) $add_save_lastsearch_values = 1;
if ($add_save_lastsearch_values) $url .= '&save_lastsearch_values=1';
}
if ($short) return $url;
$label = '';
if ($user->rights->accounting->fiscalyear->write) {
$label = '<u>'.$langs->trans("FiscalPeriod").'</u>';
$label .= '<br><b>'.$langs->trans('Ref').':</b> '.$this->id;
if (isset($this->statut)) {
$label .= '<br><b>'.$langs->trans("Status").":</b> ".$this->getLibStatut(5);
}
}
$linkclose = '';
if (empty($notooltip) && $user->rights->accounting->fiscalyear->write)
{
if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER))
{
$label = $langs->trans("FiscalYear");
$linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
}
$linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
$linkclose .= ' class="classfortooltip"';
}
$linkstart = '<a href="'.$url.'"';
$linkstart .= $linkclose.'>';
$linkend = '</a>';
if ($option === 'nolink') {
$linkstart = '';
$linkend = '';
}
$result .= $linkstart;
if ($withpicto) $result .= img_object(($notooltip ? '' : $label), $this->picto, ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip ? 0 : 1);
if ($withpicto != 2) $result .= $this->ref;
$result .= $linkend;
return $result;
}
/**
* Give a label from a status
*

View File

@ -5830,7 +5830,7 @@ class Form
$urlforajaxcall = DOL_URL_ROOT.'/core/ajax/selectobject.php';
// No immediate load of all database
$urloption = 'htmlname='.$htmlname.'&outjson=1&objectdesc='.$objectdesc.'&filter='.urlencode($objecttmp->filter).($moreparams ? $moreparams : '');
$urloption = 'htmlname='.$htmlname.'&outjson=1&objectdesc='.$objectdesc.'&filter='.urlencode($objecttmp->filter);
// Activate the auto complete using ajax call.
$out .= ajax_autocompleter($preselectedvalue, $htmlname, $urlforajaxcall, $urloption, $conf->global->$confkeyforautocompletemode, 0, array());
$out .= '<style type="text/css">.ui-autocomplete { z-index: 250; }</style>';
@ -6496,7 +6496,7 @@ class Form
*/
public static function multiSelectArrayWithCheckbox($htmlname, &$array, $varpage)
{
global $conf, $langs, $user;
global $conf, $langs, $user, $extrafields;
if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) return '';
@ -6528,6 +6528,10 @@ class Form
}
if ($val['label'])
{
if (! empty($val['langfile']) && is_object($langs)) {
$langs->load($val['langfile']);
}
$lis .= '<li><input type="checkbox" id="checkbox'.$key.'" value="'.$key.'"'.(empty($val['checked']) ? '' : ' checked="checked"').'/><label for="checkbox'.$key.'">'.dol_escape_htmltag($langs->trans($val['label'])).'</label></li>';
$listcheckedstring .= (empty($val['checked']) ? '' : $key.',');
}

View File

@ -25,25 +25,26 @@
/**
* Generic function that return javascript to add to a page to transform a common input field into an autocomplete field by calling an Ajax page (ex: /societe/ajaxcompanies.php).
* The HTML field must be an input text with id=search_$htmlname.
* This use the jQuery "autocomplete" function. If we want to use the select2, we must also convert the input into select on funcntions that call this method.
* Generic function that return javascript to add to a page to transform a common input field into an autocomplete field by calling an Ajax page (ex: /societe/ajaxcompanies.php).
* The HTML field must be an input text with id=search_$htmlname.
* This use the jQuery "autocomplete" function. If we want to use the select2, we must also convert the input into select on funcntions that call this method.
*
* @param string $selected Preselected value
* @param string $htmlname HTML name of input field
* @param string $url Ajax Url to call for request: /path/page.php. Must return a json array ('key'=>id, 'value'=>String shown into input field once selected, 'label'=>String shown into combo list)
* @param string $urloption More parameters on URL request
* @param int $minLength Minimum number of chars to trigger that Ajax search
* @param int $autoselect Automatic selection if just one value
* @param array $ajaxoptions Multiple options array
* @param string $selected Preselected value
* @param string $htmlname HTML name of input field
* @param string $url Ajax Url to call for request: /path/page.php. Must return a json array ('key'=>id, 'value'=>String shown into input field once selected, 'label'=>String shown into combo list)
* @param string $urloption More parameters on URL request
* @param int $minLength Minimum number of chars to trigger that Ajax search
* @param int $autoselect Automatic selection if just one value
* @param array $ajaxoptions Multiple options array
* - Ex: array('update'=>array('field1','field2'...)) will reset field1 and field2 once select done
* - Ex: array('disabled'=> )
* - Ex: array('show'=> )
* - Ex: array('update_textarea'=> )
* - Ex: array('option_disabled'=> id to disable and warning to show if we select a disabled value (this is possible when using autocomplete ajax)
* @return string Script
* @param string $moreparams More params provided to ajax call
* @return string Script
*/
function ajax_autocompleter($selected, $htmlname, $url, $urloption = '', $minLength = 2, $autoselect = 0, $ajaxoptions = array())
function ajax_autocompleter($selected, $htmlname, $url, $urloption = '', $minLength = 2, $autoselect = 0, $ajaxoptions = array(), $moreparams = '')
{
if (empty($minLength)) $minLength = 1;
@ -55,7 +56,7 @@ function ajax_autocompleter($selected, $htmlname, $url, $urloption = '', $minLen
// Input search_htmlname is original field
// Input htmlname is a second input field used when using ajax autocomplete.
$script = '<input type="hidden" name="'.$htmlname.'" id="'.$htmlname.'" value="'.$selected.'" />';
$script = '<input type="hidden" name="'.$htmlname.'" id="'.$htmlname.'" value="'.$selected.'" '.($moreparams ? $moreparams : '').' />';
$script .= '<!-- Javascript code for autocomplete of field '.$htmlname.' -->'."\n";
$script .= '<script>'."\n";

View File

@ -3850,7 +3850,7 @@ function dol_print_error($db = '', $error = '', $errors = null)
{
$out .= "<b>".$langs->trans("OS").":</b> ".php_uname()."<br>\n";
}
$out .= "<b>".$langs->trans("UserAgent").":</b> ".$_SERVER["HTTP_USER_AGENT"]."<br>\n";
$out .= "<b>".$langs->trans("UserAgent").":</b> ".dol_htmlentities($_SERVER["HTTP_USER_AGENT"], ENT_COMPAT, 'UTF-8')."<br>\n";
$out .= "<br>\n";
$out .= "<b>".$langs->trans("RequestedUrl").":</b> ".dol_htmlentities($_SERVER["REQUEST_URI"], ENT_COMPAT, 'UTF-8')."<br>\n";
$out .= "<b>".$langs->trans("Referer").":</b> ".(isset($_SERVER["HTTP_REFERER"]) ?dol_htmlentities($_SERVER["HTTP_REFERER"], ENT_COMPAT, 'UTF-8') : '')."<br>\n";
@ -6071,6 +6071,7 @@ function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null,
$substitutionarray['__MEMBER_PHONE__'] = $object->phone;
$substitutionarray['__MEMBER_PHONEPRO__'] = $object->phone_perso;
$substitutionarray['__MEMBER_PHONEMOBILE__'] = $object->phone_mobile;
$substitutionarray['__MEMBER_TYPE__'] = $object->type;
$substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE__'] = dol_print_date($object->first_subscription_date, 'dayrfc');
$substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START__'] = dol_print_date($object->first_subscription_date_start, 'dayrfc');
$substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END__'] = dol_print_date($object->first_subscription_date_end, 'dayrfc');

View File

@ -48,13 +48,10 @@ function takepos_prepare_head()
$head[$h][2] = 'receipt';
$h++;
if ($conf->global->TAKEPOS_BAR_RESTAURANT)
{
$head[$h][0] = DOL_URL_ROOT.'/takepos/admin/bar.php';
$head[$h][1] = $langs->trans("BarRestaurant");
$head[$h][2] = 'bar';
$h++;
}
$head[$h][0] = DOL_URL_ROOT.'/takepos/admin/bar.php';
$head[$h][1] = $langs->trans("BarRestaurant");
$head[$h][2] = 'bar';
$h++;
$numterminals = max(1, $conf->global->TAKEPOS_NUM_TERMINALS);
for ($i = 1; $i <= $numterminals; $i++)

View File

@ -1740,7 +1740,7 @@ function print_left_eldy_menu($db, $menu_array_before, $menu_array_after, &$tabM
if (empty($conf->global->PROJECT_HIDE_TASKS))
{
// Project affected to user
$newmenu->add("/projet/activity/index.php?leftmenu=tasks".($search_project_user ? '&search_project_user='.$search_project_user : ''), $langs->trans("Activities"), 0, $user->rights->projet->lire);
$newmenu->add("/projet/activity/index.php?leftmenu=tasks".($search_project_user ? '&search_project_user='.$search_project_user : ''), $langs->trans("Activities"), 0, $user->rights->projet->lire, '', 'project', 'tasks');
$newmenu->add("/projet/tasks.php?leftmenu=tasks&action=create", $langs->trans("NewTask"), 1, $user->rights->projet->creer);
$newmenu->add("/projet/tasks/list.php?leftmenu=tasks".($search_project_user ? '&search_project_user='.$search_project_user : ''), $langs->trans("List"), 1, $user->rights->projet->lire);
$newmenu->add("/projet/tasks/stats/index.php?leftmenu=projects", $langs->trans("Statistics"), 1, $user->rights->projet->lire);

View File

@ -40,7 +40,7 @@ $langs->load("modulebuilder");
?>
<!-- BEGIN PHP TEMPLATE admin_extrafields_add.tpl.php -->
<script type="text/javascript">
<script>
jQuery(document).ready(function() {
function init_typeoffields(type)
{

View File

@ -39,7 +39,7 @@ $langs->load("modulebuilder");
?>
<!-- BEGIN PHP TEMPLATE admin_extrafields_edit.tpl.php -->
<script type="text/javascript">
<script>
jQuery(document).ready(function() {
function init_typeoffields(type)
{

View File

@ -19,7 +19,7 @@ if (!empty($conf->categorie->enabled) && $user->rights->categorie->lire) {
require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
}
print '<script type="text/javascript" language="javascript">
print '<script>
$(document).ready(function() {
// Click Function

View File

@ -25,7 +25,7 @@ if (empty($conf) || !is_object($conf))
?>
<!-- START TEMPLATE FILE UPLOAD MAIN -->
<script type="text/javascript">
<script>
window.locale = {
"fileupload": {
"errors": {

View File

@ -31,7 +31,7 @@ if ($object->element == 'propal')
?>
<!-- START TEMPLATE IMPORT OBJECT LINKED LINES -->
<script type="text/javascript">
<script>
$(document).ready(function(){
$('.objectlinked_importbtn').click(function (e) {

View File

@ -32,7 +32,7 @@ if (isset($object->extraparams[$blocname]['showhide'])) $hide = (empty($object->
<!-- BEGIN PHP TEMPLATE bloc_showhide.tpl.php -->
<?php
print '<script type="text/javascript">'."\n";
print '<script>'."\n";
print '$(document).ready(function() {'."\n";
print '$("#hide-'.$blocname.'").click(function(){'."\n";
print ' setShowHide(0);'."\n";

View File

@ -207,7 +207,7 @@ if (empty($reshook) && is_array($extrafields->attributes[$object->table_element]
{
print "\n";
print '
<script type="text/javascript">
<script>
jQuery(document).ready(function() {
function showOptions(child_list, parent_list)
{

View File

@ -101,7 +101,7 @@ if ((!empty($conf->use_javascript_ajax) && empty($conf->global->MAIN_ECM_DISABLE
if ((empty($section) || $section == -1) && ($module != 'medias'))
{
?>
<script type="text/javascript">
<script>
jQuery(document).ready(function() {
jQuery('#<?php echo $nameforformuserfile ?>').hide();
});

View File

@ -79,7 +79,7 @@ if (!empty($conf->global->ADD_UNSPLASH_LOGIN_BACKGROUND)) {
?>
<?php if (empty($conf->dol_use_jmobile)) { ?>
<script type="text/javascript">
<script>
$(document).ready(function () {
/* Set focus on correct field */
<?php if ($focus_element) { ?>$('#<?php echo $focus_element; ?>').focus(); <?php } ?> // Warning to use this only on visible element
@ -356,23 +356,22 @@ if (!empty($conf->google->enabled) && !empty($conf->global->MAIN_GOOGLE_AN_ID))
}
}
// Google Adsense
// TODO Replace this with a hook
// Google Adsense (need Google module)
if (!empty($conf->google->enabled) && !empty($conf->global->MAIN_GOOGLE_AD_CLIENT) && !empty($conf->global->MAIN_GOOGLE_AD_SLOT))
{
if (empty($conf->dol_use_jmobile))
{
?>
<div class="center"><br>
<script type="text/javascript"><!--
<script><!--
google_ad_client = "<?php echo $conf->global->MAIN_GOOGLE_AD_CLIENT ?>";
google_ad_slot = "<?php echo $conf->global->MAIN_GOOGLE_AD_SLOT ?>";
google_ad_width = <?php echo $conf->global->MAIN_GOOGLE_AD_WIDTH ?>;
google_ad_height = <?php echo $conf->global->MAIN_GOOGLE_AD_HEIGHT ?>;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
<script src="//pagead2.googlesyndication.com/pagead/show_ads.js"></script>
</div>
<?php
}

View File

@ -224,7 +224,7 @@ if ($nolinesbefore) {
if (!empty($conf->global->MAIN_AUTO_OPEN_SELECT2_ON_FOCUS_FOR_CUSTOMER_PRODUCTS))
{
?>
<script type="text/javascript">
<script>
$(document).ready(function(){
// On first focus on a select2 combo, auto open the menu (this allow to use the keyboard only)
$(document).on('focus', '.select2-selection.select2-selection--single', function (e) {
@ -259,7 +259,7 @@ if ($nolinesbefore) {
if (!empty($conf->global->MAIN_AUTO_OPEN_SELECT2_ON_FOCUS_FOR_SUPPLIER_PRODUCTS))
{
?>
<script type="text/javascript">
<script>
$(document).ready(function(){
// On first focus on a select2 combo, auto open the menu (this allow to use the keyboard only)
$(document).on('focus', '.select2-selection.select2-selection--single', function (e) {
@ -362,7 +362,7 @@ if ($nolinesbefore) {
if (!empty($conf->global->PRODUCT_USE_UNITS)) {
$coldisplay++;
print '<td class="nobottom linecoluseunit left">';
print $form->selectUnits($line->fk_unit, "units");
print $form->selectUnits(empty($line->fk_unit) ? $conf->global->PRODUCT_USE_UNITS : $line->fk_unit, "units");
print '</td>';
}
$remise_percent = $buyer->remise_percent;
@ -423,6 +423,27 @@ if ((!empty($conf->service->enabled) || ($object->element == 'contrat')) && $dat
print '<td colspan="'.($coldisplay - (empty($conf->global->MAIN_VIEW_LINE_NUMBER) ? 0 : 1)).'">';
$date_start = dol_mktime(GETPOST('date_starthour'), GETPOST('date_startmin'), 0, GETPOST('date_startmonth'), GETPOST('date_startday'), GETPOST('date_startyear'));
$date_end = dol_mktime(GETPOST('date_starthour'), GETPOST('date_startmin'), 0, GETPOST('date_endmonth'), GETPOST('date_endday'), GETPOST('date_endyear'));
$prefillDates = false;
if (!empty($conf->global->MAIN_FILL_SERVICE_DATES_FROM_LAST_SERVICE_LINE) && ! empty($object->lines))
{
for ($i = count($object->lines) - 1; $i >= 0; $i--)
{
$lastline = $object->lines[$i];
if ($lastline->product_type == Product::TYPE_SERVICE && (! empty($lastline->date_start) || ! empty($lastline->date_end)))
{
$date_start_prefill = $lastline->date_start;
$date_end_prefill = $lastline->date_end;
$prefillDates = true;
break;
}
}
}
if (!empty($object->element) && $object->element == 'contrat')
{
print $langs->trans("DateStartPlanned").' ';
@ -435,7 +456,33 @@ if ((!empty($conf->service->enabled) || ($object->element == 'contrat')) && $dat
print ' '.$langs->trans('to').' ';
print $form->selectDate($date_end, 'date_end', empty($conf->global->MAIN_USE_HOURMIN_IN_DATE_RANGE) ? 0 : 1, empty($conf->global->MAIN_USE_HOURMIN_IN_DATE_RANGE) ? 0 : 1, 1, "addproduct", 1, 0);
};
if ($prefillDates)
{
echo ' <span class="small"><a href="#" id="prefill_service_dates">' . $langs->trans('FillWithLastServiceDates') . '</a></span>';
}
print '<script>';
if ($prefillDates)
{
?>
function prefill_service_dates()
{
$('#date_start').val("<?php echo dol_escape_js(dol_print_date($date_start_prefill, '%d/%m/%Y')); ?>").trigger('change');
$('#date_end').val("<?php echo dol_escape_js(dol_print_date($date_end_prefill, '%d/%m/%Y')); ?>").trigger('change');
return false; // Prevent default link behaviour (which is go to href URL)
}
$(document).ready(function()
{
$('#prefill_service_dates').click(prefill_service_dates);
});
<?php
}
if (!$date_start) {
if (isset($conf->global->MAIN_DEFAULT_DATE_START_HOUR)) {
print 'jQuery("#date_starthour").val("'.$conf->global->MAIN_DEFAULT_DATE_START_HOUR.'");';

View File

@ -272,7 +272,7 @@ if (!empty($extrafields))
print $form->selectDate($line->date_start, 'date_start', $hourmin, $hourmin, $line->date_start ? 0 : 1, "updateline", 1, 0);
print ' '.$langs->trans('to').' ';
print $form->selectDate($line->date_end, 'date_end', $hourmin, $hourmin, $line->date_end ? 0 : 1, "updateline", 1, 0);
print '<script type="text/javascript">';
print '<script>';
if (!$line->date_start) {
if (isset($conf->global->MAIN_DEFAULT_DATE_START_HOUR)) {
print 'jQuery("#date_starthour").val("'.$conf->global->MAIN_DEFAULT_DATE_START_HOUR.'");';
@ -297,7 +297,7 @@ if (!empty($extrafields))
?>
<script type="text/javascript">
<script>
jQuery(document).ready(function()
{

View File

@ -148,7 +148,7 @@ if (!empty($conf->don->enabled))
if (!empty($conf->use_javascript_ajax))
{
print "\n".'<script type="text/javascript">';
print "\n".'<script>';
print '$(document).ready(function () {
$("#generate_token").click(function() {
$.get( "'.DOL_URL_ROOT.'/core/ajax/security.php", {

View File

@ -58,7 +58,7 @@ $colorbackhmenu1 = join(',', colorStringToArray($colorbackhmenu1)); // Normalize
<body class="body bodylogin"<?php print empty($conf->global->MAIN_LOGIN_BACKGROUND) ? '' : ' style="background-size: cover; background-position: center center; background-attachment: fixed; background-repeat: no-repeat; background-image: url(\''.DOL_URL_ROOT.'/viewimage.php?cache=1&noalt=1&modulepart=mycompany&file='.urlencode('logos/'.$conf->global->MAIN_LOGIN_BACKGROUND).'\')"'; ?>>
<?php if (empty($conf->dol_use_jmobile)) { ?>
<script type="text/javascript">
<script>
$(document).ready(function () {
// Set focus on correct field
<?php if ($focus_element) { ?>$('#<?php echo $focus_element; ?>').focus(); <?php } ?> // Warning to use this only on visible element
@ -246,23 +246,22 @@ if (!empty($conf->google->enabled) && !empty($conf->global->MAIN_GOOGLE_AN_ID))
}
}
// Google Adsense
// TODO Replace this with a hook
// Google Adsense (need Google module)
if (!empty($conf->google->enabled) && !empty($conf->global->MAIN_GOOGLE_AD_CLIENT) && !empty($conf->global->MAIN_GOOGLE_AD_SLOT))
{
if (empty($conf->dol_use_jmobile))
{
?>
<div class="center"><br>
<script type="text/javascript"><!--
<script><!--
google_ad_client = "<?php echo $conf->global->MAIN_GOOGLE_AD_CLIENT ?>";
google_ad_slot = "<?php echo $conf->global->MAIN_GOOGLE_AD_SLOT ?>";
google_ad_width = <?php echo $conf->global->MAIN_GOOGLE_AD_WIDTH ?>;
google_ad_height = <?php echo $conf->global->MAIN_GOOGLE_AD_HEIGHT ?>;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
<script src="//pagead2.googlesyndication.com/pagead/show_ads.js"></script>
</div>
<?php
}

View File

@ -1299,7 +1299,7 @@ if ((empty($id) && empty($ref)) || $action == 'add' || $action == 'request' || $
print '</tr>';
}
// Validator
// Approver
if (!$edit && $action != 'editvalidator') {
print '<tr>';
print '<td class="titlefield">';
@ -1310,7 +1310,7 @@ if ((empty($id) && empty($ref)) || $action == 'add' || $action == 'request' || $
$include_users = $object->fetch_users_approver_holiday();
if (is_array($include_users) && in_array($user->id, $include_users) && $object->statut == Holiday::STATUS_VALIDATED)
{
print '<a class="editfielda" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=editvalidator">'.img_edit($langs->trans("Edit")).'</a>';
print '<a class="editfielda paddingleft" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=editvalidator">'.img_edit($langs->trans("Edit")).'</a>';
}
print '</td>';
print '</tr>';

View File

@ -175,6 +175,16 @@ ALTER TABLE llx_recruitment_recruitmentcandidature_extrafields ADD INDEX idx_fk_
CREATE TABLE llx_product_attribute_combination_price_level
(
rowid INTEGER PRIMARY KEY NOT NULL AUTO_INCREMENT,
fk_product_attribute_combination INTEGER DEFAULT 1 NOT NULL,
fk_price_level INTEGER DEFAULT 1 NOT NULL,
variation_price DOUBLE(24,8) NOT NULL,
variation_price_percentage INTEGER NULL
)ENGINE=innodb;
ALTER TABLE llx_product_attribute_combination_price_level ADD UNIQUE( fk_product_attribute_combination, fk_price_level);
@ -219,3 +229,10 @@ create table llx_c_recruitment_origin
label varchar(64) NOT NULL,
active tinyint DEFAULT 1 NOT NULL
)ENGINE=innodb;
ALTER TABLE llx_product MODIFY COLUMN seuil_stock_alerte float;
ALTER TABLE llx_product MODIFY COLUMN desiredstock float;
ALTER TABLE llx_product_warehouse_properties MODIFY COLUMN seuil_stock_alerte float;
ALTER TABLE llx_product_warehouse_properties MODIFY COLUMN desiredstock float;

View File

@ -60,7 +60,7 @@ create table llx_product
tobatch tinyint DEFAULT 0 NOT NULL, -- Is it a product that need a batch management (eat-by or lot management)
fk_product_type integer DEFAULT 0, -- Type of product: 0 for regular product, 1 for service, 9 for other (used by external module)
duration varchar(6),
seuil_stock_alerte integer DEFAULT NULL,
seuil_stock_alerte float DEFAULT NULL,
url varchar(255),
barcode varchar(180) DEFAULT NULL, -- barcode
fk_barcode_type integer DEFAULT NULL, -- barcode type
@ -96,7 +96,7 @@ create table llx_product
import_key varchar(14), -- Import key
model_pdf varchar(255), -- model save dodument used
fk_price_expression integer, -- Link to the rule for dynamic price calculation
desiredstock integer DEFAULT 0,
desiredstock float DEFAULT 0,
fk_unit integer DEFAULT NULL,
price_autogen tinyint DEFAULT 0,
fk_project integer DEFAULT NULL -- Used when product was generated by a project or is specifif to a project

View File

@ -0,0 +1,28 @@
-- ============================================================================
-- Copyright (C) 2020 John BOTELLA <john.botella@atm-consulting.fr>
--
-- This program is free software; you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation; either version 3 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program. If not, see <https://www.gnu.org/licenses/>.
--
-- ============================================================================
CREATE TABLE llx_product_attribute_combination_price_level
(
rowid INTEGER PRIMARY KEY NOT NULL AUTO_INCREMENT,
fk_product_attribute_combination INTEGER DEFAULT 1 NOT NULL,
fk_price_level INTEGER DEFAULT 1 NOT NULL,
variation_price DOUBLE(24,8) NOT NULL,
variation_price_percentage INTEGER NULL
)ENGINE=innodb;
ALTER TABLE llx_product_attribute_combination_price_level ADD UNIQUE( fk_product_attribute_combination, fk_price_level);

View File

@ -24,8 +24,8 @@ create table llx_product_warehouse_properties
tms timestamp,
fk_product integer NOT NULL,
fk_entrepot integer NOT NULL,
seuil_stock_alerte integer DEFAULT '0',
desiredstock integer DEFAULT '0',
seuil_stock_alerte float DEFAULT '0',
desiredstock float DEFAULT '0',
import_key varchar(14) -- Import key
)ENGINE=innodb;

View File

@ -77,7 +77,7 @@ POSModule=POS Module
BasicPhoneLayout=Use basic layout for phones
SetupOfTerminalNotComplete=Setup of terminal %s is not complete
DirectPayment=Direct payment
DirectPaymentButton=Direct cash payment button
DirectPaymentButton=Add a "Direct cash payment" button
InvoiceIsAlreadyValidated=Invoice is already validated
NoLinesToBill=No lines to bill
CustomReceipt=Custom Receipt
@ -94,12 +94,12 @@ TakeposConnectorMethodDescription=External module with extra features. Posibilit
PrintMethod=Print method
ReceiptPrinterMethodDescription=Powerful method with a lot of parameters. Full customizable with templates. Cannot print from the cloud.
ByTerminal=By terminal
TakeposNumpadUsePaymentIcon=Use payment icon on numpad
TakeposNumpadUsePaymentIcon=Use icon instead of text on payment buttons of numpad
CashDeskRefNumberingModules=Numbering module for POS sales
CashDeskGenericMaskCodes6 = <br><b>{TN}</b> tag is used to add the terminal number
TakeposGroupSameProduct=Group same products lines
StartAParallelSale=Start a new parallel sale
ControlCashOpening=Control cash box at opening pos
ControlCashOpening=Control cash box at opening POS
CloseCashFence=Close cash fence
CashReport=Cash report
MainPrinterToUse=Main printer to use
@ -117,6 +117,6 @@ HideCategoryImages=Hide Category Images
HideProductImages=Hide Product Images
NumberOfLinesToShow=Number of lines of images to show
DefineTablePlan=Define tables plan
GiftReceiptButton=Gift receipt button
GiftReceiptButton=Add a "Gift receipt" button
GiftReceipt=Gift receipt
ModuleReceiptPrinterMustBeEnabled=Module Receipt printer must have been enabled first

View File

@ -355,8 +355,8 @@ PriceUTTC=U.P. (inc. tax)
Amount=Amount
AmountInvoice=Invoice amount
AmountInvoiced=Amount invoiced
AmountInvoicedHT=Amount invoiced (incl. tax)
AmountInvoicedTTC=Amount invoiced (excl. tax)
AmountInvoicedHT=Amount invoiced (excl. tax)
AmountInvoicedTTC=Amount invoiced (inc. tax)
AmountPayment=Payment amount
AmountHTShort=Amount (excl.)
AmountTTCShort=Amount (inc. tax)

View File

@ -104,6 +104,7 @@ SetDefaultBarcodeType=Set barcode type
BarcodeValue=Barcode value
NoteNotVisibleOnBill=Note (not visible on invoices, proposals...)
ServiceLimitedDuration=If product is a service with limited duration:
FillWithLastServiceDates=Fill with last service line dates
MultiPricesAbility=Multiple price segments per product/service (each customer is in one price segment)
MultiPricesNumPrices=Number of prices
DefaultPriceType=Base of prices per default (with versus without tax) when adding new sale prices
@ -361,6 +362,9 @@ SelectCombination=Select combination
ProductCombinationGenerator=Variants generator
Features=Features
PriceImpact=Price impact
ImpactOnPriceLevel=Impact on price level %s
ApplyToAllPriceImpactLevel= Apply to all levels
ApplyToAllPriceImpactLevelHelp=By clicking here you set the same price impact on all levels
WeightImpact=Weight impact
NewProductAttribute=New attribute
NewProductAttributeValue=New attribute value

View File

@ -211,9 +211,9 @@ ProjectNbProjectByMonth=No. of created projects by month
ProjectNbTaskByMonth=No. of created tasks by month
ProjectOppAmountOfProjectsByMonth=Amount of leads by month
ProjectWeightedOppAmountOfProjectsByMonth=Weighted amount of leads by month
ProjectOpenedProjectByOppStatus=Open project/lead by lead status
ProjectsStatistics=Statistics on projects/leads
TasksStatistics=Statistics on project/lead tasks
ProjectOpenedProjectByOppStatus=Open project|lead by lead status
ProjectsStatistics=Statistics on projects or leads
TasksStatistics=Statistics on tasks of projects or leads
TaskAssignedToEnterTime=Task assigned. Entering time on this task should be possible.
IdTaskTime=Id task time
YouCanCompleteRef=If you want to complete the ref with some suffix, it is recommanded to add a - character to separate it, so the automatic numbering will still work correctly for next projects. For example %s-MYSUFFIX

View File

@ -355,8 +355,8 @@ PriceUTTC=P.U TTC
Amount=Montant
AmountInvoice=Montant facture
AmountInvoiced=Montant facturé
AmountInvoicedHT=Montant facturé (TTC)
AmountInvoicedTTC=Montant facturé (HT)
AmountInvoicedHT=Montant facturé (HT)
AmountInvoicedTTC=Montant facturé (TTC)
AmountPayment=Montant paiement
AmountHTShort=Montant HT
AmountTTCShort=Montant TTC

View File

@ -147,7 +147,8 @@ if (is_array($extrafields->attributes[$object->table_element]['label']) && count
'label'=>$extrafields->attributes[$object->table_element]['label'][$key],
'checked'=>(($extrafields->attributes[$object->table_element]['list'][$key] < 0) ? 0 : 1),
'position'=>$extrafields->attributes[$object->table_element]['pos'][$key],
'enabled'=>(abs($extrafields->attributes[$object->table_element]['list'][$key]) != 3 && $extrafields->attributes[$object->table_element]['perms'][$key])
'enabled'=>(abs($extrafields->attributes[$object->table_element]['list'][$key]) != 3 && $extrafields->attributes[$object->table_element]['perms'][$key]),
'langfile'=>$extrafields->attributes[$object->table_element]['langfile'][$key]
);
}
}

View File

@ -1107,7 +1107,7 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action))
{
print '<tr><td>'.$langs->trans('DefaultUnitToShow').'</td>';
print '<td colspan="3">';
print $form->selectUnits('', 'units');
print $form->selectUnits(empty($line->fk_unit) ? $conf->global->PRODUCT_USE_UNITS : $line->fk_unit, 'units');
print '</td></tr>';
}

View File

@ -287,8 +287,8 @@ class Products extends DolibarrApi
foreach ($request_data as $field => $value) {
if ($field == 'id') { continue;
}
if ($field == 'stock_reel') {
throw new RestException(400, 'Stock reel cannot be updated here. Use the /stockmovements endpoint instead');
if ($field == 'stock_reel') {
throw new RestException(400, 'Stock reel cannot be updated here. Use the /stockmovements endpoint instead');
}
$this->product->$field = $value;
}
@ -578,12 +578,12 @@ class Products extends DolibarrApi
}
if ($result > 0) {
require_once DOL_DOCUMENT_ROOT.'/product/class/productcustomerprice.class.php';
$prodcustprice = new Productcustomerprice($this->db);
$filter = array();
$filter['t.fk_product'] .= $id;
if ($thirdparty_id) $filter['t.fk_soc'] .= $thirdparty_id;
$result = $prodcustprice->fetch_all('', '', 0, 0, $filter);
require_once DOL_DOCUMENT_ROOT.'/product/class/productcustomerprice.class.php';
$prodcustprice = new Productcustomerprice($this->db);
$filter = array();
$filter['t.fk_product'] .= $id;
if ($thirdparty_id) $filter['t.fk_soc'] .= $thirdparty_id;
$result = $prodcustprice->fetch_all('', '', 0, 0, $filter);
}
if (empty($prodcustprice->lines)) {
@ -624,8 +624,8 @@ class Products extends DolibarrApi
}
return array(
'prices_by_qty'=>$this->product->prices_by_qty[0], // 1 if price by quantity was activated for the product
'prices_by_qty_list'=>$this->product->prices_by_qty_list[0]
'prices_by_qty'=>$this->product->prices_by_qty[0], // 1 if price by quantity was activated for the product
'prices_by_qty_list'=>$this->product->prices_by_qty_list[0]
);
}
@ -641,11 +641,11 @@ class Products extends DolibarrApi
* @param string $ref_fourn Supplier ref
* @param float $tva_tx New VAT Rate (For example 8.5. Should not be a string)
* @param string $charges costs affering to product
* @param float $remise_percent Discount regarding qty (percent)
* @param float $remise Discount regarding qty (amount)
* @param int $newnpr Set NPR or not
* @param int $delivery_time_days Delay in days for delivery (max). May be '' if not defined.
* @param string $supplier_reputation Reputation with this product to the defined supplier (empty, FAVORITE, DONOTORDER)
* @param float $remise_percent Discount regarding qty (percent)
* @param float $remise Discount regarding qty (amount)
* @param int $newnpr Set NPR or not
* @param int $delivery_time_days Delay in days for delivery (max). May be '' if not defined.
* @param string $supplier_reputation Reputation with this product to the defined supplier (empty, FAVORITE, DONOTORDER)
* @param array $localtaxes_array Array with localtaxes info array('0'=>type1,'1'=>rate1,'2'=>type2,'3'=>rate2) (loaded by getLocalTaxesFromRate(vatrate, 0, ...) function).
* @param string $newdefaultvatcode Default vat code
* @param float $multicurrency_buyprice Purchase price for the quantity min in currency
@ -749,77 +749,77 @@ class Products extends DolibarrApi
*/
public function getSupplierProducts($sortfield = "t.ref", $sortorder = 'ASC', $limit = 100, $page = 0, $mode = 0, $category = 0, $supplier = 0, $sqlfilters = '')
{
global $db, $conf;
$obj_ret = array();
$socid = DolibarrApiAccess::$user->socid ? DolibarrApiAccess::$user->socid : '';
$sql = "SELECT t.rowid, t.ref, t.ref_ext";
$sql .= " FROM ".MAIN_DB_PREFIX."product as t";
if ($category > 0) {
$sql .= ", ".MAIN_DB_PREFIX."categorie_product as c";
}
$sql .= ", ".MAIN_DB_PREFIX."product_fournisseur_price as s";
global $db, $conf;
$obj_ret = array();
$socid = DolibarrApiAccess::$user->socid ? DolibarrApiAccess::$user->socid : '';
$sql = "SELECT t.rowid, t.ref, t.ref_ext";
$sql .= " FROM ".MAIN_DB_PREFIX."product as t";
if ($category > 0) {
$sql .= ", ".MAIN_DB_PREFIX."categorie_product as c";
}
$sql .= ", ".MAIN_DB_PREFIX."product_fournisseur_price as s";
$sql .= ' WHERE t.entity IN ('.getEntity('product').')';
$sql .= ' WHERE t.entity IN ('.getEntity('product').')';
if ($supplier > 0) {
$sql .= " AND s.fk_soc = ".$db->escape($supplier);
}
$sql .= " AND s.fk_product = t.rowid";
// Select products of given category
if ($category > 0) {
$sql .= " AND c.fk_categorie = ".$db->escape($category);
$sql .= " AND c.fk_product = t.rowid";
}
if ($mode == 1) {
// Show only products
$sql .= " AND t.fk_product_type = 0";
} elseif ($mode == 2) {
// Show only services
$sql .= " AND t.fk_product_type = 1";
}
// Add sql filters
if ($sqlfilters) {
if (!DolibarrApi::_checkFilters($sqlfilters)) {
throw new RestException(503, 'Error when validating parameter sqlfilters '.$sqlfilters);
}
$regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^:\(\)]+)\)';
$sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")";
}
$sql .= $db->order($sortfield, $sortorder);
if ($limit) {
if ($page < 0) {
$page = 0;
}
$offset = $limit * $page;
$sql .= $db->plimit($limit + 1, $offset);
}
$result = $db->query($sql);
if ($result) {
$num = $db->num_rows($result);
$min = min($num, ($limit <= 0 ? $num : $limit));
$i = 0;
while ($i < $min)
{
$obj = $db->fetch_object($result);
if ($supplier > 0) {
$sql .= " AND s.fk_soc = ".$db->escape($supplier);
}
$sql .= " AND s.fk_product = t.rowid";
// Select products of given category
if ($category > 0) {
$sql .= " AND c.fk_categorie = ".$db->escape($category);
$sql .= " AND c.fk_product = t.rowid";
}
if ($mode == 1) {
// Show only products
$sql .= " AND t.fk_product_type = 0";
} elseif ($mode == 2) {
// Show only services
$sql .= " AND t.fk_product_type = 1";
}
// Add sql filters
if ($sqlfilters) {
if (!DolibarrApi::_checkFilters($sqlfilters)) {
throw new RestException(503, 'Error when validating parameter sqlfilters '.$sqlfilters);
}
$regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^:\(\)]+)\)';
$sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")";
}
$sql .= $db->order($sortfield, $sortorder);
if ($limit) {
if ($page < 0) {
$page = 0;
}
$offset = $limit * $page;
$sql .= $db->plimit($limit + 1, $offset);
}
$result = $db->query($sql);
if ($result) {
$num = $db->num_rows($result);
$min = min($num, ($limit <= 0 ? $num : $limit));
$i = 0;
while ($i < $min)
{
$obj = $db->fetch_object($result);
$product_fourn = new ProductFournisseur($this->db);
$product_fourn_list = $product_fourn->list_product_fournisseur_price($obj->rowid, '', '', 0, 0);
foreach ($product_fourn_list as $tmpobj) {
$this->_cleanObjectDatas($tmpobj);
}
$product_fourn = new ProductFournisseur($this->db);
$product_fourn_list = $product_fourn->list_product_fournisseur_price($obj->rowid, '', '', 0, 0);
foreach ($product_fourn_list as $tmpobj) {
$this->_cleanObjectDatas($tmpobj);
}
//var_dump($product_fourn_list->db);exit;
$obj_ret[$obj->rowid] = $product_fourn_list;
//var_dump($product_fourn_list->db);exit;
$obj_ret[$obj->rowid] = $product_fourn_list;
$i++;
}
} else {
throw new RestException(503, 'Error when retrieve product list : '.$db->lasterror());
}
if (!count($obj_ret)) {
throw new RestException(404, 'No product found');
}
return $obj_ret;
$i++;
}
} else {
throw new RestException(503, 'Error when retrieve product list : '.$db->lasterror());
}
if (!count($obj_ret)) {
throw new RestException(404, 'No product found');
}
return $obj_ret;
}
/**
@ -1057,7 +1057,7 @@ class Products extends DolibarrApi
$result = $prodattr->delete(DolibarrApiAccess::$user);
if ($result <= 0) {
throw new RestException(500, "Error deleting attribute");
throw new RestException(500, "Error deleting attribute");
}
return $result;
@ -1161,9 +1161,23 @@ class Products extends DolibarrApi
throw new RestException(401);
}
$sql = "DELETE FROM ".MAIN_DB_PREFIX."product_attribute_value WHERE ref LIKE '".trim($ref)."' AND fk_product_attribute = ".(int) $id;
$sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."product_attribute_value WHERE ref LIKE '".trim($ref)."' AND fk_product_attribute = ".(int) $id." AND entity IN (".getEntity('product').")";
$query = $this->db->query($sql);
if ($this->db->query($sql)) {
if (!$query) {
throw new RestException(401);
}
if (!$this->db->num_rows($query)) {
throw new RestException(404, 'Attribute value not found');
}
$result = $this->db->fetch_object($query);
$attrval = new ProductAttributeValue($this->db);
$attrval->id = $result->rowid;
$result = $attrval->delete(DolibarrApiAccess::$user);
if ($result > 0) {
return 1;
}
@ -1328,7 +1342,7 @@ class Products extends DolibarrApi
$objectval = new ProductAttributeValue($this->db);
$objectval->id = (int) $id;
if ($objectval->delete() > 0) {
if ($objectval->delete(DolibarrApiAccess::$user) > 0) {
return 1;
}
throw new RestException(500, "Error deleting attribute value");
@ -1448,9 +1462,9 @@ class Products extends DolibarrApi
$result = $prodcomb->createProductCombination(DolibarrApiAccess::$user, $this->product, $features, array(), $price_impact_is_percent, $price_impact, $weight_impact, $reference);
if ($result > 0)
{
return $result;
return $result;
} else {
throw new RestException(500, "Error creating new product variant");
throw new RestException(500, "Error creating new product variant");
}
}
@ -1573,7 +1587,7 @@ class Products extends DolibarrApi
$result = $prodcomb->delete(DolibarrApiAccess::$user);
if ($result <= 0)
{
throw new RestException(500, "Error deleting variant");
throw new RestException(500, "Error deleting variant");
}
return $result;
}
@ -1665,17 +1679,17 @@ class Products extends DolibarrApi
}
if ($includestockdata) {
$this->product->load_stock();
$this->product->load_stock();
if (is_array($this->product->stock_warehouse)) {
foreach ($this->product->stock_warehouse as $keytmp => $valtmp) {
if (is_array($this->product->stock_warehouse[$keytmp]->detail_batch)) {
foreach ($this->product->stock_warehouse[$keytmp]->detail_batch as $keytmp2 => $valtmp2) {
unset($this->product->stock_warehouse[$keytmp]->detail_batch[$keytmp2]->db);
}
}
}
}
if (is_array($this->product->stock_warehouse)) {
foreach ($this->product->stock_warehouse as $keytmp => $valtmp) {
if (is_array($this->product->stock_warehouse[$keytmp]->detail_batch)) {
foreach ($this->product->stock_warehouse[$keytmp]->detail_batch as $keytmp2 => $valtmp2) {
unset($this->product->stock_warehouse[$keytmp]->detail_batch[$keytmp2]->db);
}
}
}
}
}
if ($includesubproducts) {

View File

@ -205,7 +205,7 @@ class Product extends CommonObject
/**
* Stock alert
*
* @var int
* @var float
*/
public $seuil_stock_alerte = 0;
@ -995,7 +995,7 @@ class Product extends CommonObject
$sql .= ", volume = ".($this->volume != '' ? "'".$this->db->escape($this->volume)."'" : 'null');
$sql .= ", volume_units = ".($this->volume_units != '' ? "'".$this->db->escape($this->volume_units)."'" : 'null');
$sql .= ", fk_default_warehouse = ".($this->fk_default_warehouse > 0 ? $this->db->escape($this->fk_default_warehouse) : 'null');
$sql .= ", seuil_stock_alerte = ".((isset($this->seuil_stock_alerte) && is_numeric($this->seuil_stock_alerte)) ? (int) $this->seuil_stock_alerte : 'null');
$sql .= ", seuil_stock_alerte = ".((isset($this->seuil_stock_alerte) && is_numeric($this->seuil_stock_alerte)) ? (float) $this->seuil_stock_alerte : 'null');
$sql .= ", description = '".$this->db->escape($this->description)."'";
$sql .= ", url = ".($this->url ? "'".$this->db->escape($this->url)."'" : 'null');
$sql .= ", customcode = '".$this->db->escape($this->customcode)."'";
@ -1008,7 +1008,7 @@ class Product extends CommonObject
$sql .= ", accountancy_code_sell= '".$this->db->escape($this->accountancy_code_sell)."'";
$sql .= ", accountancy_code_sell_intra= '".$this->db->escape($this->accountancy_code_sell_intra)."'";
$sql .= ", accountancy_code_sell_export= '".$this->db->escape($this->accountancy_code_sell_export)."'";
$sql .= ", desiredstock = ".((isset($this->desiredstock) && is_numeric($this->desiredstock)) ? (int) $this->desiredstock : "null");
$sql .= ", desiredstock = ".((isset($this->desiredstock) && is_numeric($this->desiredstock)) ? (float) $this->desiredstock : "null");
$sql .= ", cost_price = ".($this->cost_price != '' ? $this->db->escape($this->cost_price) : 'null');
$sql .= ", fk_unit= ".(!$this->fk_unit ? 'NULL' : (int) $this->fk_unit);
$sql .= ", price_autogen = ".(!$this->price_autogen ? 0 : 1);
@ -1077,7 +1077,7 @@ class Product extends CommonObject
$comb = new ProductCombination($this->db);
foreach ($comb->fetchAllByFkProductParent($this->id) as $currcomb) {
$currcomb->updateProperties($this, $user);
$currcomb->updateProperties($this, $user);
}
}

View File

@ -66,7 +66,7 @@ if ($id > 0 || !empty($ref))
if ($cancel) $action = '';
// Action association d'un sousproduit
// Add subproduct to product
if ($action == 'add_prod' && ($user->rights->produit->creer || $user->rights->service->creer))
{
$error = 0;

View File

@ -101,7 +101,9 @@ $search_all = trim(GETPOST("search_all", 'alpha'));
$search = array();
foreach ($object->fields as $key => $val)
{
if (GETPOST('search_'.$key, 'alpha') !== '') $search[$key] = GETPOST('search_'.$key, 'alpha');
$search_key = $key;
if ($search_key == 'statut') $search_key = 'status'; // remove this after refactor entrepot.class property statut to status
if (GETPOST('search_'.$search_key, 'alpha') !== '') $search[$search_key] = GETPOST('search_'.$search_key, 'alpha');
}
// Definition of fields for list
@ -225,13 +227,15 @@ if (!empty($conf->categorie->enabled))
}
foreach ($search as $key => $val)
{
$class_key = $key;
if ($class_key == 'status') $class_key = 'statut'; // remove this after refactor entrepot.class property statut to status
if (($key == 'status' && $search[$key] == -1) || $key=='entity') continue;
$mode_search = (($object->isInt($object->fields[$key]) || $object->isFloat($object->fields[$key])) ? 1 : 0);
if (strpos($object->fields[$key]['type'], 'integer:') === 0) {
if ($search[$key] == '-1') $search[$key] = '';
$mode_search = 2;
}
if ($search[$key] != '') $sql .= natural_search((($key == 'ref') ? 't.ref' : 't.' . $key), $search[$key], (($key == 'status') ? 2 : $mode_search));
if ($search[$key] != '') $sql .= natural_search((($key == 'ref') ? 't.ref' : 't.' . $class_key), $search[$key], (($key == 'status') ? 2 : $mode_search));
}
if ($search_all) $sql .= natural_search(array_keys($fieldstosearchall), $search_all);
// Add where from extra fields

View File

@ -61,7 +61,7 @@ $search_agenda_label = GETPOST('search_agenda_label');
$id = GETPOST("id", 'int');
$socid = 0;
//if ($user->socid > 0) $socid = $user->socid; // For external user, no check is done on company because readability is managed by public status of project and assignement.
$result = restrictedArea($user, 'projet', $id, '');
$result = restrictedArea($user, 'projet', $id, 'projet&project');
if (!$user->rights->projet->lire) accessforbidden();

View File

@ -417,16 +417,14 @@ if (!empty($conf->google->enabled) && !empty($conf->global->MAIN_GOOGLE_AD_CLIEN
if (empty($conf->dol_use_jmobile))
{
print '<div align="center">'."\n";
print '<script type="text/javascript"><!--'."\n";
print '<script><!--'."\n";
print 'google_ad_client = "'.$conf->global->MAIN_GOOGLE_AD_CLIENT.'";'."\n";
print 'google_ad_slot = "'.$conf->global->MAIN_GOOGLE_AD_SLOT.'";'."\n";
print 'google_ad_width = '.$conf->global->MAIN_GOOGLE_AD_WIDTH.';'."\n";
print 'google_ad_height = '.$conf->global->MAIN_GOOGLE_AD_HEIGHT.';'."\n";
print '//-->'."\n";
print '</script>'."\n";
print '<script type="text/javascript"'."\n";
print 'src="http://pagead2.googlesyndication.com/pagead/show_ads.js">'."\n";
print '</script>'."\n";
print '<script src="//pagead2.googlesyndication.com/pagead/show_ads.js"></script>'."\n";
print '</div>'."\n";
} else {
print '<!-- google js advert tag disabled with jmobile -->'."\n";

View File

@ -34,7 +34,8 @@ if (!$user->admin) accessforbidden();
$langs->loadLangs(array("admin", "cashdesk", "printing"));
global $db;
$res = 0;
/*
* Actions
@ -44,9 +45,15 @@ if (GETPOST('action', 'alpha') == 'set')
{
$db->begin();
dol_syslog("admin/cashdesk: level ".GETPOST('level', 'alpha'));
dol_syslog("admin/bar");
if (!$res > 0) $error++;
$suplement_category = GETPOST('TAKEPOS_SUPPLEMENTS_CATEGORY', 'alpha');
if ($suplement_category < 0) $suplement_category= 0;
$res = dolibarr_set_const($db, "TAKEPOS_SUPPLEMENTS_CATEGORY", $suplement_category, 'chaine', 0, '', $conf->entity);
if ($res <= 0) {
$error++;
}
if (!$error)
{
@ -74,7 +81,6 @@ $linkback = '<a href="'.DOL_URL_ROOT.'/admin/modules.php">'.$langs->trans("BackT
print load_fiche_titre($langs->trans("CashDeskSetup").' (TakePOS)', $linkback, 'title_setup');
$head = takepos_prepare_head();
dol_fiche_head($head, 'bar', 'TakePOS', -1, 'cash-register');
print '<br>';
// Mode
@ -93,129 +99,138 @@ function Floors() {
<?php
print '<a href="" onclick="Floors(); return false;"><span class="fa fa-glass-cheers"></span> '.$langs->trans("DefineTablePlan").'</a><br>';
print '<br><br>';
print '<div class="div-table-responsive">';
print '<table class="noborder centpercent">';
print '<tr class="liste_titre">';
print '<td class="titlefieldcreate">'.$langs->trans("Parameters").'</td><td>'.$langs->trans("Value").'</td>';
print "</tr>\n";
if ($conf->global->TAKEPOS_BAR_RESTAURANT && $conf->global->TAKEPOS_PRINT_METHOD != "browser") {
print '<tr class="oddeven value"><td>';
print $langs->trans("OrderPrinters").' (<a href="'.DOL_URL_ROOT.'/takepos/admin/orderprinters.php?leftmenu=setup">'.$langs->trans("Setup").'</a>)';
print '</td>';
print '<td>';
print ajax_constantonoff("TAKEPOS_ORDER_PRINTERS", array(), $conf->entity, 0, 0, 1, 0);
//print $form->selectyesno("TAKEPOS_ORDER_PRINTERS", $conf->global->TAKEPOS_ORDER_PRINTERS, 1);
print '</td></tr>';
print '<tr class="oddeven value"><td>';
print $langs->trans("OrderNotes");
print '</td>';
print '<td>';
print ajax_constantonoff("TAKEPOS_ORDER_NOTES", array(), $conf->entity, 0, 0, 1, 0);
//print $form->selectyesno("TAKEPOS_ORDER_NOTES", $conf->global->TAKEPOS_ORDER_NOTES, 1);
print '</td></tr>';
}
print '<tr class="oddeven value"><td>';
print $langs->trans("BasicPhoneLayout");
print '</td>';
print '<td>';
//print $form->selectyesno("TAKEPOS_PHONE_BASIC_LAYOUT", $conf->global->TAKEPOS_PHONE_BASIC_LAYOUT, 1);
print ajax_constantonoff("TAKEPOS_PHONE_BASIC_LAYOUT", array(), $conf->entity, 0, 0, 1, 0);
print '</td></tr>';
print '<tr class="oddeven value"><td>';
print $langs->trans("ProductSupplements");
print '</td>';
print '<td>';
//print $form->selectyesno("TAKEPOS_SUPPLEMENTS", $conf->global->TAKEPOS_SUPPLEMENTS, 1);
print ajax_constantonoff("TAKEPOS_SUPPLEMENTS", array(), $conf->entity, 0, 0, 1, 0);
print '</td></tr>';
if ($conf->global->TAKEPOS_SUPPLEMENTS)
{
print '<tr class="oddeven"><td>';
print $langs->trans("SupplementCategory");
print '</td>';
print '<td>';
print $form->select_all_categories(Categorie::TYPE_PRODUCT, $conf->global->TAKEPOS_SUPPLEMENTS_CATEGORY, 'TAKEPOS_SUPPLEMENTS_CATEGORY', 64, 0, 0);
print ajax_combobox('TAKEPOS_SUPPLEMENTS_CATEGORY');
print "</td></tr>\n";
}
print '<tr class="oddeven value"><td>';
print 'QR - '.$langs->trans("CustomerMenu");
print '</td>';
print '<td>';
print ajax_constantonoff("TAKEPOS_QR_MENU", array(), $conf->entity, 0, 0, 1, 0);
print '</td></tr>';
print '<tr class="oddeven value"><td>';
print 'QR - '.$langs->trans("AutoOrder");
print '</td>';
print '<td>';
print ajax_constantonoff("TAKEPOS_AUTO_ORDER", array(), $conf->entity, 0, 0, 1, 0);
print '</td></tr>';
print '</table>';
if ($conf->global->TAKEPOS_QR_MENU)
{
$urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
$urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
print '<br>';
print '<table class="noborder centpercent">';
print '<tr class="liste_titre">';
print '<td>'.$langs->trans("URL").'</td><td class="right">'.$langs->trans("QR").'</td>';
print "</tr>\n";
print '<tr class="oddeven value"><td>';
print "<a target='_blank' href='".$urlwithroot."/takepos/public/menu.php'>".$urlwithroot."/takepos/public/menu.php</a>";
print '</td>';
print '<td class="right">';
print "<a target='_blank' href='printqr.php'><img src='".DOL_URL_ROOT."/takepos/genimg/qr.php' height='42' width='42'></a>";
print '</td></tr>';
print '</table>';
}
if ($conf->global->TAKEPOS_AUTO_ORDER)
{
print '<br>';
print '<table class="noborder centpercent">';
print '<tr class="liste_titre">';
print '<td>'.$langs->trans("Table").'</td><td>'.$langs->trans("URL").'</td><td class="right">'.$langs->trans("QR").'</td>';
print "</tr>\n";
//global $dolibarr_main_url_root;
$urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
$urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
$sql = "SELECT rowid, entity, label, leftpos, toppos, floor FROM ".MAIN_DB_PREFIX."takepos_floor_tables";
$resql = $db->query($sql);
$rows = array();
while ($row = $db->fetch_array($resql)) {
print '<tr class="oddeven value"><td>';
print $langs->trans("Table")." ".$row['label'];
print '</td>';
print '<td>';
print "<a target='_blank' href='".$urlwithroot."/takepos/public/auto_order.php?key=".dol_encode($row['rowid'])."'>".$urlwithroot."/takepos/public/auto_order.php?key=".dol_encode($row['rowid'])."</a>";
print '</td>';
print '<td class="right">';
print "<a target='_blank' href='printqr.php?id=".$row['rowid']."'><img src='".DOL_URL_ROOT."/takepos/genimg/qr.php?key=".dol_encode($row['rowid'])."' height='42' width='42'></a>";
print '</td></tr>';
}
print '</table>';
}
print '</div>';
print $langs->trans("EnableBarOrRestaurantFeatures");
print ajax_constantonoff("TAKEPOS_BAR_RESTAURANT", array(), $conf->entity, 0, 0, 1, 0);
print '<br>';
print '<div class="center"><input type="submit" class="button" value="'.$langs->trans("Save").'"></div>';
if ($conf->global->TAKEPOS_BAR_RESTAURANT) {
print '<br>';
print '<a href="" onclick="Floors(); return false;"><span class="fa fa-glass-cheers"></span> '.$langs->trans("DefineTablePlan").'</a><br>';
print '<br><br>';
print '<div class="div-table-responsive-no-min">';
print '<table class="noborder centpercent">';
print '<tr class="liste_titre">';
print '<td class="titlefieldcreate">'.$langs->trans("Parameters").'</td><td>'.$langs->trans("Value").'</td>';
print "</tr>\n";
if ($conf->global->TAKEPOS_PRINT_METHOD != "browser") {
print '<tr class="oddeven value"><td>';
print $langs->trans("OrderPrinters").' (<a href="'.DOL_URL_ROOT.'/takepos/admin/orderprinters.php?leftmenu=setup">'.$langs->trans("Setup").'</a>)';
print '</td>';
print '<td>';
print ajax_constantonoff("TAKEPOS_ORDER_PRINTERS", array(), $conf->entity, 0, 0, 1, 0);
//print $form->selectyesno("TAKEPOS_ORDER_PRINTERS", $conf->global->TAKEPOS_ORDER_PRINTERS, 1);
print '</td></tr>';
print '<tr class="oddeven value"><td>';
print $langs->trans("OrderNotes");
print '</td>';
print '<td>';
print ajax_constantonoff("TAKEPOS_ORDER_NOTES", array(), $conf->entity, 0, 0, 1, 0);
//print $form->selectyesno("TAKEPOS_ORDER_NOTES", $conf->global->TAKEPOS_ORDER_NOTES, 1);
print '</td></tr>';
}
print '<tr class="oddeven value"><td>';
print $langs->trans("BasicPhoneLayout");
print '</td>';
print '<td>';
//print $form->selectyesno("TAKEPOS_PHONE_BASIC_LAYOUT", $conf->global->TAKEPOS_PHONE_BASIC_LAYOUT, 1);
print ajax_constantonoff("TAKEPOS_PHONE_BASIC_LAYOUT", array(), $conf->entity, 0, 0, 1, 0);
print '</td></tr>';
print '<tr class="oddeven value"><td>';
print $langs->trans("ProductSupplements");
print '</td>';
print '<td>';
//print $form->selectyesno("TAKEPOS_SUPPLEMENTS", $conf->global->TAKEPOS_SUPPLEMENTS, 1);
print ajax_constantonoff("TAKEPOS_SUPPLEMENTS", array(), $conf->entity, 0, 0, 1, 0);
print '</td></tr>';
if ($conf->global->TAKEPOS_SUPPLEMENTS)
{
print '<tr class="oddeven"><td>';
print $langs->trans("SupplementCategory");
print '</td>';
print '<td>';
print $form->select_all_categories(Categorie::TYPE_PRODUCT, $conf->global->TAKEPOS_SUPPLEMENTS_CATEGORY, 'TAKEPOS_SUPPLEMENTS_CATEGORY', 64, 0, 0);
print ajax_combobox('TAKEPOS_SUPPLEMENTS_CATEGORY');
print "</td></tr>\n";
}
print '<tr class="oddeven value"><td>';
print 'QR - '.$langs->trans("CustomerMenu");
print '</td>';
print '<td>';
print ajax_constantonoff("TAKEPOS_QR_MENU", array(), $conf->entity, 0, 0, 1, 0);
print '</td></tr>';
print '<tr class="oddeven value"><td>';
print 'QR - '.$langs->trans("AutoOrder");
print '</td>';
print '<td>';
print ajax_constantonoff("TAKEPOS_AUTO_ORDER", array(), $conf->entity, 0, 0, 1, 0);
print '</td></tr>';
print '</table>';
print '</div>';
print '<br>';
print '<div class="center"><input type="submit" class="button" value="'.$langs->trans("Save").'"></div>';
}
if ($conf->global->TAKEPOS_BAR_RESTAURANT) {
if ($conf->global->TAKEPOS_QR_MENU)
{
$urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
$urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
print '<br>';
print '<table class="noborder centpercent">';
print '<tr class="liste_titre">';
print '<td>'.$langs->trans("URL").'</td><td class="right">'.$langs->trans("QR").'</td>';
print "</tr>\n";
print '<tr class="oddeven value"><td>';
print "<a target='_blank' href='".$urlwithroot."/takepos/public/menu.php'>".$urlwithroot."/takepos/public/menu.php</a>";
print '</td>';
print '<td class="right">';
print "<a target='_blank' href='printqr.php'><img src='".DOL_URL_ROOT."/takepos/genimg/qr.php' height='42' width='42'></a>";
print '</td></tr>';
print '</table>';
}
if ($conf->global->TAKEPOS_AUTO_ORDER)
{
print '<br>';
print '<table class="noborder centpercent">';
print '<tr class="liste_titre">';
print '<td>'.$langs->trans("Table").'</td><td>'.$langs->trans("URL").'</td><td class="right">'.$langs->trans("QR").'</td>';
print "</tr>\n";
//global $dolibarr_main_url_root;
$urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
$urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
$sql = "SELECT rowid, entity, label, leftpos, toppos, floor FROM ".MAIN_DB_PREFIX."takepos_floor_tables";
$resql = $db->query($sql);
$rows = array();
while ($row = $db->fetch_array($resql)) {
print '<tr class="oddeven value"><td>';
print $langs->trans("Table")." ".$row['label'];
print '</td>';
print '<td>';
print "<a target='_blank' href='".$urlwithroot."/takepos/public/auto_order.php?key=".dol_encode($row['rowid'])."'>".$urlwithroot."/takepos/public/auto_order.php?key=".dol_encode($row['rowid'])."</a>";
print '</td>';
print '<td class="right">';
print "<a target='_blank' href='printqr.php?id=".$row['rowid']."'><img src='".DOL_URL_ROOT."/takepos/genimg/qr.php?key=".dol_encode($row['rowid'])."' height='42' width='42'></a>";
print '</td></tr>';
}
print '</table>';
}
}
print "</form>\n";

View File

@ -87,6 +87,7 @@ print '<input type="hidden" name="action" value="set">';
print load_fiche_titre($langs->trans("PrintMethod"), '', '');
print '<div class="div-table-responsive-no-min">';
print '<table class="noborder centpercent">';
print '<tr class="liste_titre">';
print '<td>'.$langs->trans("Name").'</td><td>'.$langs->trans("Description").'</td><td class="right">'.$langs->trans("Status").'</td>';
@ -143,14 +144,25 @@ if ($conf->global->TAKEPOS_PRINT_METHOD == "takeposconnector")
}
print "</td></tr>\n";
print '</table>';
print '</div>';
print load_fiche_titre($langs->trans("Setup"), '', '');
print '<div class="div-table-responsive-no-min">';
print '<table class="noborder centpercent">';
print '<tr class="liste_titre">';
print '<td>'.$langs->trans("Parameters").'</td><td>'.$langs->trans("Value").'</td>';
print "</tr>\n";
// VAT Grouped on ticket
print '<tr class="oddeven"><td>';
print $langs->trans('TicketVatGrouped');
print '<td colspan="2">';
print ajax_constantonoff("TAKEPOS_TICKET_VAT_GROUPPED", array(), $conf->entity, 0, 0, 1, 0);
//print $form->selectyesno("TAKEPOS_TICKET_VAT_GROUPPED", $conf->global->TAKEPOS_TICKET_VAT_GROUPPED, 1);
print "</td></tr>\n";
if ($conf->global->TAKEPOS_PRINT_METHOD == "takeposconnector") {
print '<tr class="oddeven value"><td>';
print $langs->trans("URL")." / ".$langs->trans("IPAddress").' (<a href="http://en.takepos.com/connector" target="_blank">'.$langs->trans("TakeposConnectorNecesary").'</a>)';
@ -214,6 +226,7 @@ print $form->selectyesno("TAKEPOS_AUTO_PRINT_TICKETS", $conf->global->TAKEPOS_AU
print "</td></tr>\n";
print '</table>';
print '</div>';
print '<br>';

View File

@ -274,14 +274,6 @@ print img_object('', 'category', 'class="paddingright"').$form->select_all_categ
print ajax_combobox('TAKEPOS_ROOT_CATEGORY_ID');
print "</td></tr>\n";
// VAT Grouped on ticket
print '<tr class="oddeven"><td>';
print $langs->trans('TicketVatGrouped');
print '<td colspan="2">';
print ajax_constantonoff("TAKEPOS_TICKET_VAT_GROUPPED", array(), $conf->entity, 0, 0, 1, 0);
//print $form->selectyesno("TAKEPOS_TICKET_VAT_GROUPPED", $conf->global->TAKEPOS_TICKET_VAT_GROUPPED, 1);
print "</td></tr>\n";
// Sort product
print '<tr class="oddeven"><td>';
print $langs->trans("SortProductField");
@ -429,14 +421,6 @@ print "</td></tr>\n";
//print $form->selectarray('TAKEPOS_ADDON', $array, (empty($conf->global->TAKEPOS_ADDON) ? '0' : $conf->global->TAKEPOS_ADDON), 0);
//print "</td></tr>\n";
print '<tr class="oddeven"><td>';
print $langs->trans("EnableBarOrRestaurantFeatures");
print '</td>';
print '<td colspan="2">';
print ajax_constantonoff("TAKEPOS_BAR_RESTAURANT", array(), $conf->entity, 0, 0, 1, 0);
//print $form->selectyesno("TAKEPOS_BAR_RESTAURANT", $conf->global->TAKEPOS_BAR_RESTAURANT, 1);
print "</td></tr>\n";
print '</table>';
print '</div>';

View File

@ -398,6 +398,7 @@ if ($action == "deleteline") {
$invoice->deleteline($deletelineid);
$invoice->fetch($placeid);
}
if (count($invoice->lines)==0) $invoice->delete($user);
}
if ($action == "delete") {

View File

@ -77,7 +77,12 @@ if (!defined('ISLOADEDBYSTEELSHEET')) die('Must be call by steelsheet'); ?>
text-align: center;
font-size: 45px;
line-height: 90px;
background: rgba(0, 0, 0, 0.08) !important
background: rgba(0, 0, 0, 0.08) !important;
}
.info-box-module .info-box-icon {
padding-top: 5px;
padding-bottom: 5px;
}
.info-box-sm .info-box-icon {
height: 80px;
@ -436,7 +441,7 @@ if (GETPOSTISSET('THEME_SATURATE_RATIO')) $conf->global->THEME_SATURATE_RATIO =
.info-box-module .info-box-content {
height: 6.4em;
height: 98px;
}
/* Disabled. This break the responsive on smartphone
.box{

View File

@ -71,7 +71,7 @@ $colortexttitle = '0,0,0';
$colortexttitlelink = '10, 20, 100';
$colortext = '0,0,0';
$colortextlink = '10, 20, 100';
$fontsize = '0.86em';
$fontsize = '0.95em';
$fontsizesmaller = '0.75em';
$topMenuFontSize = '1.1em';
$toolTipBgColor = 'rgba(255, 255, 255, 0.96)';

View File

@ -90,9 +90,9 @@ if ($confirm == 'yes') {
if ($action == 'confirm_delete') {
$db->begin();
$res = $objectval->deleteByFkAttribute($object->id);
$res = $objectval->deleteByFkAttribute($object->id, $user);
if ($res < 1 || ($object->delete() < 1)) {
if ($res < 1 || ($object->delete($user) < 1)) {
$db->rollback();
setEventMessages($langs->trans('CoreErrorMessage'), $object->errors, 'errors');
header('Location: '.dol_buildpath('/variants/card.php?id='.$object->id, 2));
@ -105,7 +105,7 @@ if ($confirm == 'yes') {
} elseif ($action == 'confirm_deletevalue')
{
if ($objectval->fetch($valueid) > 0) {
if ($objectval->delete() < 1) {
if ($objectval->delete($user) < 1) {
setEventMessages($langs->trans('CoreErrorMessage'), $objectval->errors, 'errors');
} else {
setEventMessages($langs->trans('RecordSaved'), null, 'mesgs');

View File

@ -16,17 +16,18 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
/**
* Class ProductAttribute
* Used to represent a product attribute
*/
class ProductAttribute
class ProductAttribute extends CommonObject
{
/**
* Database handler
* @var DoliDB
*/
private $db;
public $db;
/**
* Id of the product attribute
@ -119,7 +120,8 @@ class ProductAttribute
$return[] = $tmp;
}
} else dol_print_error($this->db);
}
else dol_print_error($this->db);
return $return;
}
@ -127,11 +129,21 @@ class ProductAttribute
/**
* Creates a product attribute
*
* @param User $user Object user that create
* @param User $user Object user
* @param int $notrigger Do not execute trigger
* @return int <0 KO, Id of new variant if OK
*/
public function create(User $user)
public function create(User $user, $notrigger = 0)
{
if (empty($notrigger)) {
// Call trigger
$result = $this->call_trigger('PRODUCT_ATTRIBUTE_CREATE', $user);
if ($result < 0) {
return -1;
}
// End call triggers
}
//Ref must be uppercase
$this->ref = strtoupper($this->ref);
@ -152,11 +164,21 @@ class ProductAttribute
/**
* Updates a product attribute
*
* @param User $user Object user
* @param User $user Object user
* @param int $notrigger Do not execute trigger
* @return int <0 KO, >0 OK
*/
public function update(User $user)
public function update(User $user, $notrigger = 0)
{
if (empty($notrigger)) {
// Call trigger
$result = $this->call_trigger('PRODUCT_ATTRIBUTE_MODIFY', $user);
if ($result < 0) {
return -1;
}
// End call triggers
}
//Ref must be uppercase
$this->ref = trim(strtoupper($this->ref));
$this->label = trim($this->label);
@ -173,11 +195,21 @@ class ProductAttribute
/**
* Deletes a product attribute
*
* @param User $user Object user
* @return int <0 KO, >0 OK
* @param User $user Object user
* @param int $notrigger Do not execute trigger
* @return int <0 KO, >0 OK
*/
public function delete($user = null)
public function delete(User $user, $notrigger = 0)
{
if (empty($notrigger)) {
// Call trigger
$result = $this->call_trigger('PRODUCT_ATTRIBUTE_DELETE', $user);
if ($result < 0) {
return -1;
}
// End call triggers
}
$sql = "DELETE FROM ".MAIN_DB_PREFIX."product_attribute WHERE rowid = ".(int) $this->id;
if ($this->db->query($sql)) {

View File

@ -16,17 +16,18 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
/**
* Class ProductAttributeValue
* Used to represent a product attribute value
*/
class ProductAttributeValue
class ProductAttributeValue extends CommonObject
{
/**
* Database handler
* @var DoliDB
*/
private $db;
public $db;
/**
* Attribute value id
@ -144,10 +145,11 @@ class ProductAttributeValue
/**
* Creates a value for a product attribute
*
* @param User $user Object user
* @return int <0 KO >0 OK
* @param User $user Object user
* @param int $notrigger Do not execute trigger
* @return int <0 KO >0 OK
*/
public function create(User $user)
public function create(User $user, $notrigger = 0)
{
if (!$this->fk_product_attribute) {
return -1;
@ -155,15 +157,25 @@ class ProductAttributeValue
// Ref must be uppercase
$this->ref = strtoupper($this->ref);
$this->value = $this->db->escape($this->value);
$sql = "INSERT INTO ".MAIN_DB_PREFIX."product_attribute_value (fk_product_attribute, ref, value, entity)
VALUES ('".(int) $this->fk_product_attribute."', '".$this->db->escape($this->ref)."',
'".$this->db->escape($this->value)."', ".(int) $this->entity.")";
'".$this->value."', ".(int) $this->entity.")";
$query = $this->db->query($sql);
if ($query) {
$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'product_attribute_value');
if (empty($notrigger)) {
// Call trigger
$result = $this->call_trigger('PRODUCT_ATTRIBUTE_VALUE_CREATE', $user);
if ($result < 0) {
return -1;
}
// End call triggers
}
return 1;
}
@ -173,11 +185,21 @@ class ProductAttributeValue
/**
* Updates a product attribute value
*
* @param User $user Object user
* @return int <0 if KO, >0 if OK
* @param User $user Object user
* @param int $notrigger Do not execute trigger
* @return int <0 if KO, >0 if OK
*/
public function update(User $user)
public function update(User $user, $notrigger = 0)
{
if (empty($notrigger)) {
// Call trigger
$result = $this->call_trigger('PRODUCT_ATTRIBUTE_VALUE_MODIFY', $user);
if ($result < 0) {
return -1;
}
// End call triggers
}
//Ref must be uppercase
$this->ref = trim(strtoupper($this->ref));
$this->value = trim($this->value);
@ -196,33 +218,62 @@ class ProductAttributeValue
/**
* Deletes a product attribute value
*
* @param User $user Object user
* @param int $notrigger Do not execute trigger
* @return int <0 KO, >0 OK
*/
public function delete()
public function delete(User $user, $notrigger = 0)
{
$sql = "DELETE FROM ".MAIN_DB_PREFIX."product_attribute_value WHERE rowid = ".(int) $this->id;
if ($this->db->query($sql)) {
return 1;
}
if (empty($notrigger)) {
// Call trigger
$result = $this->call_trigger('PRODUCT_ATTRIBUTE_VALUE_DELETE', $user);
if ($result < 0) {
return -1;
}
// End call triggers
}
$sql = "DELETE FROM ".MAIN_DB_PREFIX."product_attribute_value WHERE rowid = ".(int) $this->id;
if ($this->db->query($sql)) {
return 1;
}
return -1;
return -1;
}
/**
* Deletes all product attribute values by a product attribute id
*
* @param int $fk_attribute Product attribute id
* @param int $fk_attribute Product attribute id
* @param User $user Object user
* @return int <0 KO, >0 OK
*/
public function deleteByFkAttribute($fk_attribute)
public function deleteByFkAttribute($fk_attribute, User $user)
{
$sql = "DELETE FROM ".MAIN_DB_PREFIX."product_attribute_value WHERE fk_product_attribute = ".(int) $fk_attribute;
$sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."product_attribute_value WHERE fk_product_attribute = ".(int) $fk_attribute;
if ($this->db->query($sql)) {
return 1;
}
$query = $this->db->query($sql);
return -1;
if (!$query) {
return -1;
}
if (!$this->db->num_rows($query)) {
return 1;
}
while ($obj = $this->db->fetch_object($query)) {
$tmp = new ProductAttributeValue($this->db);
if ($tmp->fetch($obj->rowid) > 0) {
$result = $tmp->delete($user);
if ($result < 0) {
return -1;
}
} else {
return -1;
}
}
return 1;
}
}

View File

@ -71,6 +71,12 @@ class ProductCombination
*/
public $entity;
/**
* Combination price level
* @var ProductCombinationLevel[]
*/
public $combination_price_levels;
/**
* Constructor
*
@ -92,6 +98,8 @@ class ProductCombination
*/
public function fetch($rowid)
{
global $conf;
$sql = "SELECT rowid, fk_product_parent, fk_product_child, variation_price, variation_price_percentage, variation_weight FROM ".MAIN_DB_PREFIX."product_attribute_combination WHERE rowid = ".(int) $rowid." AND entity IN (".getEntity('product').")";
$query = $this->db->query($sql);
@ -113,9 +121,118 @@ class ProductCombination
$this->variation_price_percentage = $obj->variation_price_percentage;
$this->variation_weight = $obj->variation_weight;
if (!empty($conf->global->PRODUIT_MULTIPRICES)) {
$this->fetchCombinationPriceLevels();
}
return 1;
}
/**
* Retrieves combination price levels
*
* @param int $fk_price_level the price level to fetch, use 0 for all
* @param bool $useCache to use cache or not
* @return int <0 KO, >0 OK
*/
public function fetchCombinationPriceLevels($fk_price_level = 0, $useCache = true)
{
global $conf;
// Check cache
if (!empty($this->combination_price_levels) && $useCache){
if ((!empty($fk_price_level) && isset($this->combination_price_levels[$fk_price_level])) || empty($fk_price_level)){
return 1;
}
}
if (!is_array($this->combination_price_levels)
|| empty($fk_price_level) // if fetch an unique level dont erase all already fetched
){
$this->combination_price_levels = array();
}
$staticProductCombinationLevel = new ProductCombinationLevel($this->db);
$combination_price_levels = $staticProductCombinationLevel->fetchAll($this->id, $fk_price_level);
if (!is_array($combination_price_levels)) {
return -1;
}
if (empty($combination_price_levels)){
/**
* for auto retrocompatibility with last behavior
*/
$productCombinationLevel = new ProductCombinationLevel($this->db);
$productCombinationLevel->fk_price_level = intval($fk_price_level);
$productCombinationLevel->fk_product_attribute_combination = $this->id;
$productCombinationLevel->variation_price = $this->variation_price;
$productCombinationLevel->variation_price_percentage = $this->variation_price_percentage;
if ($fk_price_level>0){
$combination_price_levels[$fk_price_level] = $productCombinationLevel;
}
else {
for ($i = 1; $i <= $conf->global->PRODUIT_MULTIPRICES_LIMIT; $i++){
$combination_price_levels[$i] = $productCombinationLevel;
}
}
}
$this->combination_price_levels = $combination_price_levels;
return 1;
}
/**
* Retrieves combination price levels
*
* @param int $clean levels off PRODUIT_MULTIPRICES_LIMIT
* @return int <0 KO, >0 OK
*/
public function saveCombinationPriceLevels($clean = 1)
{
global $conf;
$errors = 0;
$staticProductCombinationLevel = new ProductCombinationLevel($this->db);
// Delete all
if (empty($this->combination_price_levels)){
return $staticProductCombinationLevel->deleteAllForCombination($this->id);
}
// Clean not needed price levels
if ($clean){
$res = $staticProductCombinationLevel->clean($this->id);
if ($res<0){
$this->errors[] = 'Fail to clean not needed price levels';
return -1;
}
}
foreach ($this->combination_price_levels as $fk_price_level => $combination_price_level){
$res = $combination_price_level->save();
if ($res<1){
$this->error = 'save combination price level '.$fk_price_level . ' '.$combination_price_level->error;
$this->errors[] = $this->error;
$errors ++;
}
}
if ($errors > 0){
return $errors*-1;
}
else {
return 1;
}
}
/**
* Retrieves a product combination by a child product row id
*
@ -124,6 +241,8 @@ class ProductCombination
*/
public function fetchByFkProductChild($fk_child)
{
global $conf;
$sql = "SELECT rowid, fk_product_parent, fk_product_child, variation_price, variation_price_percentage, variation_weight FROM ".MAIN_DB_PREFIX."product_attribute_combination WHERE fk_product_child = ".(int) $fk_child." AND entity IN (".getEntity('product').")";
$query = $this->db->query($sql);
@ -145,6 +264,10 @@ class ProductCombination
$this->variation_price_percentage = $result->variation_price_percentage;
$this->variation_weight = $result->variation_weight;
if (!empty($conf->global->PRODUIT_MULTIPRICES)) {
$this->fetchCombinationPriceLevels();
}
return 1;
}
@ -156,6 +279,8 @@ class ProductCombination
*/
public function fetchAllByFkProductParent($fk_product_parent)
{
global $conf;
$sql = "SELECT rowid, fk_product_parent, fk_product_child, variation_price, variation_price_percentage, variation_weight FROM ".MAIN_DB_PREFIX."product_attribute_combination WHERE fk_product_parent = ".(int) $fk_product_parent." AND entity IN (".getEntity('product').")";
$query = $this->db->query($sql);
@ -175,6 +300,10 @@ class ProductCombination
$tmp->variation_price_percentage = $result->variation_price_percentage;
$tmp->variation_weight = $result->variation_weight;
if (!empty($conf->global->PRODUIT_MULTIPRICES)) {
$tmp->fetchCombinationPriceLevels();
}
$return[] = $tmp;
}
@ -209,6 +338,8 @@ class ProductCombination
*/
public function create($user)
{
global $conf;
$sql = "INSERT INTO ".MAIN_DB_PREFIX."product_attribute_combination
(fk_product_parent, fk_product_child, variation_price, variation_price_percentage, variation_weight, entity)
VALUES (".(int) $this->fk_product_parent.", ".(int) $this->fk_product_child.",
@ -221,6 +352,13 @@ class ProductCombination
return -1;
}
if (!empty($conf->global->PRODUIT_MULTIPRICES)) {
$res = $this->saveCombinationPriceLevels();
if ($res<0){
return -2;
}
}
$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'product_attribute_combination');
return 1;
@ -234,6 +372,8 @@ class ProductCombination
*/
public function update(User $user)
{
global $conf;
$sql = "UPDATE ".MAIN_DB_PREFIX."product_attribute_combination
SET fk_product_parent = ".(int) $this->fk_product_parent.", fk_product_child = ".(int) $this->fk_product_child.",
variation_price = ".(float) $this->variation_price.", variation_price_percentage = ".(int) $this->variation_price_percentage.",
@ -245,6 +385,14 @@ class ProductCombination
return -1;
}
if (!empty($conf->global->PRODUIT_MULTIPRICES)) {
$res = $this->saveCombinationPriceLevels();
if ($res<0){
return -2;
}
}
$parent = new Product($this->db);
$parent->fetch($this->fk_product_parent);
@ -266,6 +414,12 @@ class ProductCombination
$comb2val = new ProductCombination2ValuePair($this->db);
$comb2val->deleteByFkCombination($this->id);
// remove combination price levels
if (!$this->db->query("DELETE FROM ".MAIN_DB_PREFIX."product_attribute_combination_price_level WHERE fk_product_attribute_combination = ".(int) $this->id)) {
$this->db->rollback();
return -1;
}
$sql = "DELETE FROM ".MAIN_DB_PREFIX."product_attribute_combination WHERE rowid = ".(int) $this->id;
if ($this->db->query($sql)) {
@ -341,6 +495,7 @@ class ProductCombination
$child->label = $parent->label.$varlabel;;
}
if ($child->update($child->id, $user) > 0) {
$new_vat = $parent->tva_tx;
$new_npr = $parent->tva_npr;
@ -349,9 +504,12 @@ class ProductCombination
if (!empty($conf->global->PRODUIT_MULTIPRICES)) {
for ($i = 1; $i <= $conf->global->PRODUIT_MULTIPRICES_LIMIT; $i++)
{
if ($parent->multiprices[$i] != '') {
if ($parent->multiprices[$i] != '' || isset($this->combination_price_levels[$i]->variation_price)) {
$new_type = $parent->multiprices_base_type[$i];
$new_min_price = $parent->multiprices_min[$i];
$variation_price = doubleval(!isset($this->combination_price_levels[$i]->variation_price) ? $this->variation_price : $this->combination_price_levels[$i]->variation_price);
$variation_price_percentage = doubleval(!isset($this->combination_price_levels[$i]->variation_price_percentage) ? $this->variation_price_percentage : $this->combination_price_levels[$i]->variation_price_percentage);
if ($parent->prices_by_qty_list[$i]) {
$new_psq = 1;
} else {
@ -364,12 +522,12 @@ class ProductCombination
$new_price = $parent->multiprices[$i];
}
if ($this->variation_price_percentage) {
if ($variation_price_percentage) {
if ($new_price != 0) {
$new_price *= 1 + ($this->variation_price / 100);
$new_price *= 1 + ($variation_price / 100);
}
} else {
$new_price += $this->variation_price;
$new_price += $variation_price;
}
$child->updatePrice($new_price, $new_type, $user, $new_vat, $new_min_price, $i, $new_npr, $new_psq);
@ -508,7 +666,7 @@ WHERE c.fk_product_parent = ".(int) $productid." AND p.tosell = 1";
* @param Product $product Parent product
* @param array $combinations Attribute and value combinations.
* @param array $variations Price and weight variations
* @param bool $price_var_percent Is the price variation a relative variation?
* @param bool|array $price_var_percent Is the price variation a relative variation?
* @param bool|float $forced_pricevar If the price variation is forced
* @param bool|float $forced_weightvar If the weight variation is forced
* @param bool|string $forced_refvar If the reference is forced
@ -523,6 +681,8 @@ WHERE c.fk_product_parent = ".(int) $productid." AND p.tosell = 1";
$db->begin();
$price_impact = array(1=>0); // init level price impact
$forced_refvar = trim($forced_refvar);
if (!empty($forced_refvar) && $forced_refvar != $product->ref) {
@ -545,7 +705,12 @@ WHERE c.fk_product_parent = ".(int) $productid." AND p.tosell = 1";
$weight_impact = (float) $forced_weightvar; // If false, return 0
//Final price impact
$price_impact = (float) $forced_pricevar; // If false, return 0
if (!is_array($forced_pricevar)){
$price_impact[1] = (float) $forced_pricevar; // If false, return 0
}
else {
$price_impact = $forced_pricevar;
}
$newcomb = new ProductCombination($db);
$existingCombination = $newcomb->fetchByProductCombination2ValuePairs($product->id, $combinations);
@ -587,7 +752,15 @@ WHERE c.fk_product_parent = ".(int) $productid." AND p.tosell = 1";
$weight_impact += (float) price2num($variations[$currcombattr][$currcombval]['weight']);
}
if ($forced_pricevar === false) {
$price_impact += (float) price2num($variations[$currcombattr][$currcombval]['price']);
$price_impact[1] += (float) price2num($variations[$currcombattr][$currcombval]['price']);
// Manage Price levels
if ($conf->global->PRODUIT_MULTIPRICES){
for ($i = 2; $i <= $conf->global->PRODUIT_MULTIPRICES_LIMIT; $i++)
{
$price_impact[$i] += (float) price2num($variations[$currcombattr][$currcombval]['price']);
}
}
}
if ($forced_refvar === false) {
@ -606,9 +779,27 @@ WHERE c.fk_product_parent = ".(int) $productid." AND p.tosell = 1";
}
$newcomb->variation_price_percentage = $price_var_percent;
$newcomb->variation_price = $price_impact;
$newcomb->variation_price = $price_impact[1];
$newcomb->variation_weight = $weight_impact;
// Init price level
if ($conf->global->PRODUIT_MULTIPRICES){
for ($i = 1; $i <= $conf->global->PRODUIT_MULTIPRICES_LIMIT; $i++){
$productCombinationLevel = new ProductCombinationLevel($this->db);
$productCombinationLevel->fk_product_attribute_combination = 0;
$productCombinationLevel->fk_price_level = $i;
$productCombinationLevel->variation_price = $price_impact[$i];
if (is_array($price_var_percent)){
$productCombinationLevel->variation_price_percentage = !empty($price_var_percent[$i]) ? $price_var_percent[$i] : 0;
}else {
$productCombinationLevel->variation_price_percentage = $price_var_percent;
}
$newcomb->combination_price_levels[$i] = $productCombinationLevel;
}
}
$newproduct->weight += $weight_impact;
// Now create the product
@ -764,3 +955,260 @@ WHERE c.fk_product_parent = ".(int) $productid." AND p.tosell = 1";
return $label;
}
}
/**
* Class ProductCombinationLevel
* Used to represent a product combination Level
*/
class ProductCombinationLevel
{
/**
* Database handler
* @var DoliDB
*/
private $db;
/**
* @var string Name of table without prefix where object is stored
*/
public $table_element = 'product_attribute_combination_price_level';
/**
* Rowid of combination
* @var int
*/
public $id;
/**
* Rowid of parent product combination
* @var int
*/
public $fk_product_attribute_combination;
/**
* Combination price level
* @var int
*/
public $fk_price_level;
/**
* Price variation
* @var float
*/
public $variation_price;
/**
* Is the price variation a relative variation?
* @var bool
*/
public $variation_price_percentage = false;
/**
* Constructor
*
* @param DoliDB $db Database handler
*/
public function __construct(DoliDB $db)
{
$this->db = $db;
}
/**
* Retrieves a combination level by its rowid
*
* @param int $rowid Row id
* @return int <0 KO, >0 OK
*/
public function fetch($rowid)
{
$sql = "SELECT rowid, fk_product_attribute_combination, fk_price_level, variation_price, variation_price_percentage FROM " . MAIN_DB_PREFIX . $this->table_element." WHERE rowid = " . (int) $rowid;
$obj = $this->db->getRow($sql);
if ($obj){
return $this->fetchFormObj($obj);
}
return -1;
}
/**
* Retrieves combination price levels
*
* @param int $fk_product_attribute_combination
* @param int $fk_price_level the price level to fetch, use 0 for all
* @return self[] | -1 on KO
*/
public function fetchAll($fk_product_attribute_combination, $fk_price_level = 0)
{
$sql = "SELECT rowid, fk_product_attribute_combination, fk_price_level, variation_price, variation_price_percentage"
." FROM ".MAIN_DB_PREFIX.$this->table_element
." WHERE fk_product_attribute_combination = ".intval($fk_product_attribute_combination);
if (!empty($fk_price_level)){
$sql.= ' AND fk_price_level = '.intval($fk_price_level);
}
$combination_price_levels = $this->db->getRows($sql);
if (!is_array($combination_price_levels)) {
return -1;
}
$result = array();
if (!empty($combination_price_levels)) {
// For more simple usage set level as array key
foreach ($combination_price_levels as $k => $row){
$productCombinationLevel = new ProductCombinationLevel($this->db);
$productCombinationLevel->fetchFormObj($row);
$result[$row->fk_price_level] = $productCombinationLevel;
}
}
return $result;
}
/**
* assign vars form an stdclass like sql obj
*
* @param int $rowid Row id
* @return int <0 KO, >0 OK
*/
public function fetchFormObj($obj)
{
if (!$obj) {
return -1;
}
$this->id = $obj->rowid;
$this->fk_product_attribute_combination = doubleval($obj->fk_product_attribute_combination);
$this->fk_price_level = intval($obj->fk_price_level);
$this->variation_price = doubleval($obj->variation_price);
$this->variation_price_percentage = (bool) $obj->variation_price_percentage;
return 1;
}
/**
* save
*
* @return int <0 KO, >0 OK
*/
public function save()
{
$errors = 0;
if (empty($this->fk_product_attribute_combination) || empty($this->fk_price_level)){
return -1;
}
// check if level exist in DB before add
if (empty($this->id)){
$sql = "SELECT rowid id"
." FROM ".MAIN_DB_PREFIX . $this->table_element
." WHERE fk_product_attribute_combination = ".(int) $this->fk_product_attribute_combination
.' AND fk_price_level = '.intval($this->fk_price_level);
$existObj = $this->db->getRow($sql);
if ($existObj){
$this->id = $existObj->id;
}
}
// Update
if (!empty($this->id)) {
$sql = 'UPDATE ' . MAIN_DB_PREFIX . $this->table_element . ' SET '
. ' variation_price = '.doubleval($this->variation_price)
. ' , variation_price_percentage = '.intval($this->variation_price_percentage)
. ' WHERE rowid = '.intval($this->id);
$res = $this->db->query($sql);
if ($res>0){
return $this->id;
}
else {
$this->error = $this->db->error();
$this->errors[] = $this->error;
return -1;
}
}
else {
// ADD
$sql = "INSERT INTO " . MAIN_DB_PREFIX . $this->table_element . " ("
. " fk_product_attribute_combination, fk_price_level, variation_price, variation_price_percentage"
. " ) VALUES ( "
. intval($this->fk_product_attribute_combination)
. ' , '.intval($this->fk_price_level)
. ' , '.doubleval($this->variation_price)
. ' , '.intval($this->variation_price_percentage)
. " )";
$res = $this->db->query($sql);
if ($res){
$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . $this->table_element);
}
else {
$this->error = $this->db->error();
$this->errors[] = $this->error;
return -1;
}
}
return $this->id;
}
/**
* delete
*
* @return int <0 KO, >0 OK
*/
public function delete()
{
$res = $this->db->query("DELETE FROM ".MAIN_DB_PREFIX.$this->table_element
." WHERE rowid = ".(int) $this->id);
return $res ? 1 : -1;
}
/**
* delete all for a combination
*
* @param $fk_product_attribute_combination
* @return int <0 KO, >0 OK
*/
public function deleteAllForCombination($fk_product_attribute_combination)
{
$res = $this->db->query("DELETE FROM ".MAIN_DB_PREFIX.$this->table_element
." WHERE fk_product_attribute_combination = ".(int) $fk_product_attribute_combination);
return $res ? 1 : -1;
}
/**
* Clean not needed price levels for a combination
*
* @param $fk_product_attribute_combination
* @return int <0 KO, >0 OK
*/
public function clean($fk_product_attribute_combination)
{
global $conf;
$res = $this->db->query("DELETE FROM ".MAIN_DB_PREFIX.$this->table_element
. " WHERE fk_product_attribute_combination = ".(int) $fk_product_attribute_combination
. " AND fk_price_level > ".intval($conf->global->PRODUIT_MULTIPRICES_LIMIT) );
return $res ? 1 : -1;
}
}

View File

@ -33,6 +33,10 @@ $ref = GETPOST('ref', 'alpha');
$weight_impact = GETPOST('weight_impact', 'alpha');
$price_impact = GETPOST('price_impact', 'alpha');
$price_impact_percent = (bool) GETPOST('price_impact_percent');
$level_price_impact = GETPOST('level_price_impact', 'array');
$level_price_impact_percent = GETPOST('level_price_impact_percent', 'array');
$reference = GETPOST('reference', 'alpha');
$form = new Form($db);
@ -112,6 +116,18 @@ if ($_POST) {
}
$weight_impact = price2num($weight_impact);
$price_impact = price2num($price_impact);
// for conf PRODUIT_MULTIPRICES
if ($conf->global->PRODUIT_MULTIPRICES) {
$level_price_impact = array_map('price2num', $level_price_impact);
$level_price_impact_percent = array_map('price2num', $level_price_impact_percent);
}
else {
$level_price_impact = array(1 => $weight_impact);
$level_price_impact_percent = array(1 => $price_impact_percent);
}
$sanit_features = array();
//First, sanitize
@ -141,11 +157,10 @@ if ($_POST) {
// sanit_feature is an array with 1 (and only 1) value per attribute.
// For example: Color->blue, Size->Small, Option->2
//var_dump($sanit_features);
//var_dump($productCombination2ValuePairs1); exit;
if (!$prodcomb->fetchByProductCombination2ValuePairs($id, $sanit_features))
{
$result = $prodcomb->createProductCombination($user, $object, $sanit_features, array(), $price_impact_percent, $price_impact, $weight_impact, $reference);
$result = $prodcomb->createProductCombination($user, $object, $sanit_features, array(), $level_price_impact_percent, $level_price_impact, $weight_impact, $reference);
if ($result > 0)
{
setEventMessages($langs->trans('RecordSaved'), null, 'mesgs');
@ -227,6 +242,32 @@ if ($_POST) {
$prodcomb->variation_price = $price_impact;
$prodcomb->variation_weight = $weight_impact;
// for conf PRODUIT_MULTIPRICES
if ($conf->global->PRODUIT_MULTIPRICES) {
$level_price_impact = array_map('price2num', $level_price_impact);
$level_price_impact_percent = array_map(function ($a) {
return !empty($a);}, $level_price_impact_percent);
$prodcomb->variation_price = $level_price_impact[1];
$prodcomb->variation_price_percentage = (bool) $level_price_impact_percent[1];
}
else {
$level_price_impact = array(1 => $weight_impact);
$level_price_impact_percent = array(1 => $price_impact_percent);
}
if ($conf->global->PRODUIT_MULTIPRICES){
$prodcomb->combination_price_levels = array();
for ($i = 1; $i <= $conf->global->PRODUIT_MULTIPRICES_LIMIT; $i++){
$productCombinationLevel = new ProductCombinationLevel($db);
$productCombinationLevel->fk_product_attribute_combination = $prodcomb->id;
$productCombinationLevel->fk_price_level = $i;
$productCombinationLevel->variation_price = $level_price_impact[$i];
$productCombinationLevel->variation_price_percentage = $level_price_impact_percent[$i];
$prodcomb->combination_price_levels[$i] = $productCombinationLevel;
}
}
if ($prodcomb->update($user) > 0) {
setEventMessages($langs->trans('RecordSaved'), null, 'mesgs');
header('Location: '.dol_buildpath('/variants/combinations.php?id='.$id, 2));
@ -594,12 +635,33 @@ if (!empty($id) || !empty($ref))
<td><label for="reference"><?php echo $langs->trans('Reference') ?></label></td>
<td><input type="text" id="reference" name="reference" value="<?php echo trim($reference) ?>"></td>
</tr>
<?php if (empty($conf->global->PRODUIT_MULTIPRICES)){ ?>
<tr>
<td><label for="price_impact"><?php echo $langs->trans('PriceImpact') ?></label></td>
<td><input type="text" id="price_impact" name="price_impact" value="<?php echo price($price_impact) ?>">
<input type="checkbox" id="price_impact_percent" name="price_impact_percent" <?php echo $price_impact_percent ? ' checked' : '' ?>> <label for="price_impact_percent"><?php echo $langs->trans('PercentageVariation') ?></label></td>
<input type="checkbox" id="price_impact_percent" name="price_impact_percent" <?php echo $price_impact_percent ? ' checked' : '' ?>> <label for="price_impact_percent"><?php echo $langs->trans('PercentageVariation') ?></label>
</td>
</tr>
<?php
<?php }
else {
$prodcomb->fetchCombinationPriceLevels();
for ($i = 1; $i <= $conf->global->PRODUIT_MULTIPRICES_LIMIT; $i++)
{
print '<tr>';
print '<td><label for="level_price_impact_'.$i.'">'.$langs->trans('ImpactOnPriceLevel', $i).'</label>';
if ($i===1){
print ' <a id="apply-price-impact-to-all-level" class="classfortooltip" href="#" title="'.$langs->trans('ApplyToAllPriceImpactLevelHelp').'">('.$langs->trans('ApplyToAllPriceImpactLevel').')</a>';
}
print '</td>';
print '<td><input type="text" class="level_price_impact" id="level_price_impact_'.$i.'" name="level_price_impact['.$i.']" value="'.price($prodcomb->combination_price_levels[$i]->variation_price).'">';
print '<input type="checkbox" class="level_price_impact_percent" id="level_price_impact_percent_'.$i.'" name="level_price_impact_percent['.$i.']" '. (!empty($prodcomb->combination_price_levels[$i]->variation_price_percentage) ? ' checked' : '' ).'> <label for="level_price_impact_percent_'.$i.'">'.$langs->trans('PercentageVariation').'</label>';
print '</td>';
print '</tr>';
}
}
if ($object->isProduct()) {
print '<tr>';
print '<td><label for="weight_impact">'.$langs->trans('WeightImpact').'</label></td>';
@ -609,9 +671,30 @@ if (!empty($id) || !empty($ref))
print '</table>';
}
dol_fiche_end();
?>
if (!empty($conf->global->PRODUIT_MULTIPRICES)){
?>
<script>
$(document).ready(function() {
// Apply level 1 impact to all prices impact levels
$('body').on('click', '#apply-price-impact-to-all-level', function(e) {
e.preventDefault();
let priceImpact = $( "#level_price_impact_1" ).val();
let priceImpactPrecent = $( "#level_price_impact_percent_1" ).prop("checked");
var multipricelimit = <?php print intval($conf->global->PRODUIT_MULTIPRICES_LIMIT); ?>
for (let i = 2; i <= multipricelimit; i++) {
$( "#level_price_impact_" + i ).val(priceImpact);
$( "#level_price_impact_percent_" + i ).prop("checked", priceImpactPrecent);
}
});
});
</script>
<?php
}
dol_fiche_end();
?>
<div style="text-align: center">
<input type="submit" name="create" <?php if (!is_array($productCombination2ValuePairs1)) print ' disabled="disabled"'; ?> value="<?php echo $action == 'add' ? $langs->trans('Create') : $langs->trans('Save') ?>" class="button">
&nbsp;