diff --git a/htdocs/adherents/class/api_members.class.php b/htdocs/adherents/class/api_members.class.php index e7b26e6a3d2..6a65bc99ced 100644 --- a/htdocs/adherents/class/api_members.class.php +++ b/htdocs/adherents/class/api_members.class.php @@ -425,7 +425,7 @@ class Members extends DolibarrApi } if ($field == 'array_options' && is_array($value)) { foreach ($value as $index => $val) { - $member->array_options[$index] = $val; + $member->array_options[$index] = $this->_checkValForAPI($field, $val, $member); } continue; } diff --git a/htdocs/adherents/class/api_memberstypes.class.php b/htdocs/adherents/class/api_memberstypes.class.php new file mode 100644 index 00000000000..32e9e105fa2 --- /dev/null +++ b/htdocs/adherents/class/api_memberstypes.class.php @@ -0,0 +1,336 @@ + + * + * 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 . + */ + +use Luracast\Restler\RestException; + +require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent_type.class.php'; + +/** + * API class for members types + * + * @access protected + * @class DolibarrApiAccess {@requires user,external} + */ +class MembersTypes extends DolibarrApi +{ + /** + * @var array $FIELDS Mandatory fields, checked when create and update object + */ + public static $FIELDS = array( + 'label', + ); + + /** + * Constructor + */ + public function __construct() + { + global $db, $conf; + $this->db = $db; + } + + /** + * Get properties of a member type object + * + * Return an array with member type information + * + * @param int $id ID of member type + * @return Object Object with cleaned properties + * + * @throws RestException + */ + public function get($id) + { + if (!DolibarrApiAccess::$user->hasRight('adherent', 'lire')) { + throw new RestException(401); + } + + $membertype = new AdherentType($this->db); + $result = $membertype->fetch($id); + if (!$result) { + throw new RestException(404, 'member type not found'); + } + + if (!DolibarrApi::_checkAccessToResource('member', $membertype->id, 'adherent_type')) { + throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login); + } + + return $this->_cleanObjectDatas($membertype); + } + + /** + * List members types + * + * Get a list of members types + * + * @param string $sortfield Sort field + * @param string $sortorder Sort order + * @param int $limit Limit for list + * @param int $page Page number + * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.libelle:like:'SO-%') and (t.subscription:=:'1')" + * @param string $properties Restrict the data returned to these properties. Ignored if empty. Comma separated list of properties names + * @return array Array of member type objects + * + * @throws RestException + */ + public function index($sortfield = "t.rowid", $sortorder = 'ASC', $limit = 100, $page = 0, $sqlfilters = '', $properties = '') + { + global $db, $conf; + + $obj_ret = array(); + + if (!DolibarrApiAccess::$user->hasRight('adherent', 'lire')) { + throw new RestException(401); + } + + $sql = "SELECT t.rowid"; + $sql .= " FROM ".MAIN_DB_PREFIX."adherent_type AS t LEFT JOIN ".MAIN_DB_PREFIX."adherent_type_extrafields AS ef ON (ef.fk_object = t.rowid)"; // Modification VMR Global Solutions to include extrafields as search parameters in the API GET call, so we will be able to filter on extrafields + $sql .= ' WHERE t.entity IN ('.getEntity('member_type').')'; + + // Add sql filters + if ($sqlfilters) { + $errormessage = ''; + $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage); + if ($errormessage) { + throw new RestException(503, 'Error when validating parameter sqlfilters -> '.$errormessage); + } + } + + $sql .= $this->db->order($sortfield, $sortorder); + if ($limit) { + if ($page < 0) { + $page = 0; + } + $offset = $limit * $page; + + $sql .= $this->db->plimit($limit + 1, $offset); + } + + $result = $this->db->query($sql); + if ($result) { + $i = 0; + $num = $this->db->num_rows($result); + $min = min($num, ($limit <= 0 ? $num : $limit)); + while ($i < $min) { + $obj = $this->db->fetch_object($result); + $membertype = new AdherentType($this->db); + if ($membertype->fetch($obj->rowid)) { + $obj_ret[] = $this->_filterObjectProperties($this->_cleanObjectDatas($membertype), $properties); + } + $i++; + } + } else { + throw new RestException(503, 'Error when retrieve member type list : '.$this->db->lasterror()); + } + + return $obj_ret; + } + + /** + * Create member type object + * + * @param array $request_data Request data + * @return int ID of member type + */ + public function post($request_data = null) + { + if (!DolibarrApiAccess::$user->hasRight('adherent', 'configurer')) { + throw new RestException(401); + } + // Check mandatory fields + $result = $this->_validate($request_data); + + $membertype = new AdherentType($this->db); + foreach ($request_data as $field => $value) { + if ($field === 'caller') { + // Add a mention of caller so on trigger called after action, we can filter to avoid a loop if we try to sync back again with the caller + $membertype->context['caller'] = $request_data['caller']; + continue; + } + + $membertype->$field = $value; + } + if ($membertype->create(DolibarrApiAccess::$user) < 0) { + throw new RestException(500, 'Error creating member type', array_merge(array($membertype->error), $membertype->errors)); + } + return $membertype->id; + } + + /** + * Update member type + * + * @param int $id ID of member type to update + * @param array $request_data Datas + * @return int + */ + public function put($id, $request_data = null) + { + if (!DolibarrApiAccess::$user->hasRight('adherent', 'configurer')) { + throw new RestException(401); + } + + $membertype = new AdherentType($this->db); + $result = $membertype->fetch($id); + if (!$result) { + throw new RestException(404, 'member type not found'); + } + + if (!DolibarrApi::_checkAccessToResource('member', $membertype->id, 'adherent_type')) { + throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login); + } + + foreach ($request_data as $field => $value) { + if ($field == 'id') { + continue; + } + if ($field === 'caller') { + // Add a mention of caller so on trigger called after action, we can filter to avoid a loop if we try to sync back again with the caller + $membertype->context['caller'] = $request_data['caller']; + continue; + } + if ($field == 'array_options' && is_array($value)) { + foreach ($value as $index => $val) { + $membertype->array_options[$index] = $this->_checkValForAPI($field, $val, $membertype); + } + continue; + } + // Process the status separately because it must be updated using + // the validate(), resiliate() and exclude() methods of the class AdherentType. + $membertype->$field = $this->_checkValForAPI($field, $value, $membertype); + } + + // If there is no error, update() returns the number of affected rows + // so if the update is a no op, the return value is zero. + if ($membertype->update(DolibarrApiAccess::$user) >= 0) { + return $this->get($id); + } else { + throw new RestException(500, 'Error when updating member type: '.$membertype->error); + } + } + + /** + * Delete member type + * + * @param int $id member type ID + * @return array + */ + public function delete($id) + { + if (!DolibarrApiAccess::$user->hasRight('adherent', 'configurer')) { + throw new RestException(401); + } + $membertype = new AdherentType($this->db); + $result = $membertype->fetch($id); + if (!$result) { + throw new RestException(404, 'member type not found'); + } + + if (!DolibarrApi::_checkAccessToResource('member', $membertype->id, 'adherent_type')) { + throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login); + } + + $res = $membertype->delete(DolibarrApiAccess::$user); + if ($res < 0) { + throw new RestException(500, "Can't delete, error occurs"); + } elseif ($res == 0) { + throw new RestException(409, "Can't delete, that product is probably used"); + } + + return array( + 'success' => array( + 'code' => 200, + 'message' => 'Member type deleted' + ) + ); + } + + /** + * Validate fields before creating an object + * + * @param array|null $data Data to validate + * @return array + * + * @throws RestException + */ + private function _validate($data) + { + $membertype = array(); + foreach (MembersTypes::$FIELDS as $field) { + if (!isset($data[$field])) { + throw new RestException(400, "$field field missing"); + } + $membertype[$field] = $data[$field]; + } + return $membertype; + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore + /** + * Clean sensible object datas + * + * @param Object $object Object to clean + * @return Object Object with cleaned properties + */ + protected function _cleanObjectDatas($object) + { + // phpcs:enable + $object = parent::_cleanObjectDatas($object); + + unset($object->array_options); + unset($object->linkedObjectsIds); + unset($object->context); + unset($object->canvas); + unset($object->fk_project); + unset($object->contact); + unset($object->contact_id); + unset($object->thirdparty); + unset($object->user); + unset($object->origin); + unset($object->origin_id); + unset($object->ref_ext); + unset($object->country); + unset($object->country_id); + unset($object->country_code); + unset($object->barcode_type); + unset($object->barcode_type_code); + unset($object->barcode_type_label); + unset($object->barcode_type_coder); + unset($object->mode_reglement_id); + unset($object->cond_reglement_id); + unset($object->cond_reglement); + unset($object->fk_delivery_address); + unset($object->shipping_method_id); + unset($object->model_pdf); + unset($object->fk_account); + unset($object->note_public); + unset($object->note_private); + unset($object->fk_incoterms); + unset($object->label_incoterms); + unset($object->location_incoterms); + unset($object->name); + unset($object->lastname); + unset($object->firstname); + unset($object->civility_id); + unset($object->total_ht); + unset($object->total_tva); + unset($object->total_localtax1); + unset($object->total_localtax2); + unset($object->total_ttc); + + return $object; + } +} diff --git a/htdocs/adherents/class/api_subscriptions.class.php b/htdocs/adherents/class/api_subscriptions.class.php index 88b60506280..5f583495677 100644 --- a/htdocs/adherents/class/api_subscriptions.class.php +++ b/htdocs/adherents/class/api_subscriptions.class.php @@ -234,6 +234,12 @@ class Subscriptions extends DolibarrApi continue; } + if ($field == 'array_options' && is_array($value)) { + foreach ($value as $index => $val) { + $subscription->array_options[$index] = $this->_checkValForAPI($field, $val, $subscription); + } + continue; + } $subscription->$field = $this->_checkValForAPI($field, $value, $subscription); } diff --git a/htdocs/admin/dict.php b/htdocs/admin/dict.php index 320d8ca9e82..c201ca97b73 100644 --- a/htdocs/admin/dict.php +++ b/htdocs/admin/dict.php @@ -2367,7 +2367,10 @@ if ($id > 0) { continue; } - if ($value == 'element') { + // Management of several special cases and exceptions + if ($value == 'code' && $id == DICT_PRODUCT_NATURE) { + $valuetoshow = (int) $valuetoshow; + } elseif ($value == 'element') { $valuetoshow = isset($elementList[$valuetoshow]) ? $elementList[$valuetoshow] : $valuetoshow; } elseif ($value == 'source') { $valuetoshow = isset($sourceList[$valuetoshow]) ? $sourceList[$valuetoshow] : $valuetoshow; @@ -2543,6 +2546,7 @@ if ($id > 0) { $valuetoshow = $TDurationTypes[$obj->{$value}]; } } + $class .= ($class ? ' ' : '').'tddict'; if ($value == 'name') { $class .= ' tdoverflowmax200'; @@ -2790,12 +2794,11 @@ function dictFieldList($fieldlist, $obj = null, $tabname = '', $context = '') print ''; } elseif ($value == 'department_buyer') { if ($context == 'edit') { - print ''; + print ''; // show department buyer list $country_code = (!empty($obj->country_code) ? $obj->country_code : ''); $department_buyer_id = (!empty($obj->department_buyer_id) ? (int) $obj->department_buyer_id : 0); if ($country_code != '') { - print img_picto('', 'state', 'class="pictofixedwidth"'); print $formcompany->select_state($department_buyer_id, $country_code, 'department_buyer_id', 'minwidth100 maxwidth150 maxwidthonsmartphone'); } print ''; diff --git a/htdocs/bom/class/api_boms.class.php b/htdocs/bom/class/api_boms.class.php index c907b12f54f..24d16d6bcf2 100644 --- a/htdocs/bom/class/api_boms.class.php +++ b/htdocs/bom/class/api_boms.class.php @@ -249,11 +249,10 @@ class Boms extends DolibarrApi if ($field == 'array_options' && is_array($value)) { foreach ($value as $index => $val) { - $this->bom->array_options[$index] = $this->_checkValForAPI('extrafields', $val, $this->bom); + $this->bom->array_options[$index] = $this->_checkValForAPI($field, $val, $this->bom); } continue; } - $this->bom->$field = $this->_checkValForAPI($field, $value, $this->bom); } diff --git a/htdocs/categories/class/api_categories.class.php b/htdocs/categories/class/api_categories.class.php index f6d21306360..5082bda1b15 100644 --- a/htdocs/categories/class/api_categories.class.php +++ b/htdocs/categories/class/api_categories.class.php @@ -252,6 +252,13 @@ class Categories extends DolibarrApi continue; } + if ($field == 'array_options' && is_array($value)) { + foreach ($value as $index => $val) { + $this->category->array_options[$index] = $this->_checkValForAPI($field, $val, $this->category); + } + continue; + } + $this->category->$field = $this->_checkValForAPI($field, $value, $this->category); } diff --git a/htdocs/comm/action/class/actioncomm.class.php b/htdocs/comm/action/class/actioncomm.class.php index a96ec4e0e82..6eb05106686 100644 --- a/htdocs/comm/action/class/actioncomm.class.php +++ b/htdocs/comm/action/class/actioncomm.class.php @@ -1883,7 +1883,7 @@ class ActionComm extends CommonObject $color = 'style="color: #'.$this->type_color.' !important;"'; } if ($this->type_picto) { - $imgpicto = img_picto($titlealt, $this->type_picto, '', 0, 0, 0, '', ($morecss ? ' '.$morecss : '')); + $imgpicto = img_picto($titlealt.'rr', $this->type_picto, '', 0, 0, 0, '', ($morecss ? ' '.$morecss : '')); } else { if ($this->type_code == 'AC_RDV') { $imgpicto = img_picto($titlealt, 'meeting', $color, 0, 0, 0, '', ($morecss ? ' '.$morecss : '')); @@ -1891,7 +1891,7 @@ class ActionComm extends CommonObject $imgpicto = img_picto($titlealt, 'object_phoning', $color, 0, 0, 0, '', ($morecss ? ' '.$morecss : '')); } elseif ($this->type_code == 'AC_FAX') { $imgpicto = img_picto($titlealt, 'object_phoning_fax', $color, 0, 0, 0, '', ($morecss ? ' '.$morecss : '')); - } elseif ($this->type_code == 'AC_EMAIL' || $this->type_code == 'AC_EMAIL_IN' || (!empty($this->code) && preg_match('/_SENTBYMAIL/', $this->code))) { + } elseif ($this->type_code == 'AC_EMAIL' || $this->type_code == 'AC_EMAIL_IN' || $this->type_code == 'AC_EMAILING' || (!empty($this->code) && preg_match('/_SENTBYMAIL/', $this->code))) { $imgpicto = img_picto($titlealt, 'object_email', $color, 0, 0, 0, '', ($morecss ? ' '.$morecss : '')); } elseif ($this->type_code == 'AC_INT') { $imgpicto = img_picto($titlealt, 'object_intervention', $color, 0, 0, 0, '', ($morecss ? ' '.$morecss : '')); diff --git a/htdocs/comm/action/class/api_agendaevents.class.php b/htdocs/comm/action/class/api_agendaevents.class.php index 9d6f0cca1fb..d2dd06c919c 100644 --- a/htdocs/comm/action/class/api_agendaevents.class.php +++ b/htdocs/comm/action/class/api_agendaevents.class.php @@ -287,6 +287,12 @@ class AgendaEvents extends DolibarrApi continue; } + if ($field == 'array_options' && is_array($value)) { + foreach ($value as $index => $val) { + $this->actioncomm->array_options[$index] = $this->_checkValForAPI($field, $val, $this->actioncomm); + } + continue; + } $this->actioncomm->$field = $this->_checkValForAPI($field, $value, $this->actioncomm); } diff --git a/htdocs/comm/action/class/cactioncomm.class.php b/htdocs/comm/action/class/cactioncomm.class.php index 9c28468c1c7..db3fcfe1bbc 100644 --- a/htdocs/comm/action/class/cactioncomm.class.php +++ b/htdocs/comm/action/class/cactioncomm.class.php @@ -163,11 +163,13 @@ class CActionComm global $langs, $conf, $user; $langs->load("commercial"); + /* $actionstatic = new ActionComm($this->db); $rep_id = array(); $rep_code = array(); $rep_all = array(); + */ $sql = "SELECT id, code, libelle as label, module, type, color, picto"; $sql .= " FROM ".MAIN_DB_PREFIX."c_actioncomm"; diff --git a/htdocs/compta/bank/class/api_bankaccounts.class.php b/htdocs/compta/bank/class/api_bankaccounts.class.php index f36faead44b..4f8f7ad6b94 100644 --- a/htdocs/compta/bank/class/api_bankaccounts.class.php +++ b/htdocs/compta/bank/class/api_bankaccounts.class.php @@ -348,6 +348,12 @@ class BankAccounts extends DolibarrApi continue; } + if ($field == 'array_options' && is_array($value)) { + foreach ($value as $index => $val) { + $account->array_options[$index] = $this->_checkValForAPI($field, $val, $account); + } + continue; + } $account->$field = $this->_checkValForAPI($field, $value, $account); } diff --git a/htdocs/compta/facture/card.php b/htdocs/compta/facture/card.php index b9b152943ab..bb5431eb32b 100644 --- a/htdocs/compta/facture/card.php +++ b/htdocs/compta/facture/card.php @@ -3336,9 +3336,9 @@ if ($action == 'create') { $expesrc->fetch_optionals(); $object->array_options = $expesrc->array_options; } else { - $cond_reglement_id = (!empty($objectsrc->cond_reglement_id) ? $objectsrc->cond_reglement_id : (!empty($soc->cond_reglement_id) ? $soc->cond_reglement_id : 0)); - $mode_reglement_id = (!empty($objectsrc->mode_reglement_id) ? $objectsrc->mode_reglement_id : (!empty($soc->mode_reglement_id) ? $soc->mode_reglement_id : 0)); - $fk_account = (!empty($objectsrc->fk_account) ? $objectsrc->fk_account : (!empty($soc->fk_account) ? $soc->fk_account : 0)); + $cond_reglement_id = (!empty($objectsrc->cond_reglement_id) ? $objectsrc->cond_reglement_id : (!empty($soc->cond_reglement_id) ? $soc->cond_reglement_id : (!empty($cond_reglement_id) ? $cond_reglement_id : 0))); + $mode_reglement_id = (!empty($objectsrc->mode_reglement_id) ? $objectsrc->mode_reglement_id : (!empty($soc->mode_reglement_id) ? $soc->mode_reglement_id : (!empty($mode_reglement_id) ? $mode_reglement_id : 0))); + $fk_account = (!empty($objectsrc->fk_account) ? $objectsrc->fk_account : (!empty($soc->fk_account) ? $soc->fk_account : (!empty($fk_account) ? $fk_account : 0))); if (isModEnabled('multicurrency')) { if (!empty($objectsrc->multicurrency_code)) { diff --git a/htdocs/core/class/ccountry.class.php b/htdocs/core/class/ccountry.class.php index 1d798698e84..22a1d2d84d2 100644 --- a/htdocs/core/class/ccountry.class.php +++ b/htdocs/core/class/ccountry.class.php @@ -52,6 +52,21 @@ class Ccountry extends CommonDict 'label' => array('type' => 'varchar(250)', 'label' => 'Label', 'enabled' => 1, 'visible' => 1, 'position' => 15, 'notnull' => -1, 'showoncombobox' => 1) ); + /** + * @var int + */ + public $favorite; + + /** + * @var int + */ + public $eec; + + /** + * @var string + */ + public $numeric_code; + /** * Constructor @@ -150,7 +165,10 @@ class Ccountry extends CommonDict $sql .= " t.code,"; $sql .= " t.code_iso,"; $sql .= " t.label,"; - $sql .= " t.active"; + $sql .= " t.eec,"; + $sql .= " t.active,"; + $sql .= " t.favorite,"; + $sql .= " t.numeric_code"; $sql .= " FROM ".$this->db->prefix()."c_country as t"; if ($id) { $sql .= " WHERE t.rowid = ".((int) $id); @@ -161,6 +179,7 @@ class Ccountry extends CommonDict } dol_syslog(get_class($this)."::fetch", LOG_DEBUG); + $resql = $this->db->query($sql); if ($resql) { if ($this->db->num_rows($resql)) { @@ -172,6 +191,9 @@ class Ccountry extends CommonDict $this->code_iso = $obj->code_iso; $this->label = $obj->label; $this->active = $obj->active; + $this->favorite = $obj->favorite; + $this->eec = $obj->eec; + $this->numeric_code = $obj->numeric_code; } $this->db->free($resql); diff --git a/htdocs/core/class/html.form.class.php b/htdocs/core/class/html.form.class.php index 145af617c47..9a1409b9b0d 100644 --- a/htdocs/core/class/html.form.class.php +++ b/htdocs/core/class/html.form.class.php @@ -3040,11 +3040,6 @@ class Form $sql .= ' AND e.statut IN (' . $this->db->sanitize($this->db->escape(implode(',', $warehouseStatusArray))) . ')'; // Return line if product is inside the selected stock. If not, an empty line will be returned so we will count 0. } - // include search in supplier ref - if (getDolGlobalString('MAIN_SEARCH_PRODUCT_BY_FOURN_REF')) { - $sql .= " LEFT JOIN " . $this->db->prefix() . "product_fournisseur_price as pfp ON p.rowid = pfp.fk_product"; - } - //Price by customer if ((getDolGlobalString('PRODUIT_CUSTOMER_PRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) && !empty($socid)) { $sql .= " LEFT JOIN " . $this->db->prefix() . "product_customer_price as pcp ON pcp.fk_soc=" . ((int) $socid) . " AND pcp.fk_product=p.rowid"; @@ -3110,6 +3105,8 @@ class Form $sql .= $hookmanager->resPrint; // Add criteria on ref/label if ($filterkey != '') { + $sqlSupplierSearch= ''; + $sql .= ' AND ('; $prefix = !getDolGlobalString('PRODUCT_DONOTSEARCH_ANYWHERE') ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on // For natural search @@ -3135,8 +3132,11 @@ class Form $sql .= " OR pl.description LIKE '" . $this->db->escape($prefix . $crit) . "%'"; } } + + // include search in supplier ref if (getDolGlobalString('MAIN_SEARCH_PRODUCT_BY_FOURN_REF')) { - $sql .= " OR pfp.ref_fourn LIKE '" . $this->db->escape($prefix . $crit) . "%'"; + $sqlSupplierSearch .= !empty($sqlSupplierSearch) ? ' OR ':''; + $sqlSupplierSearch .= " pfp.ref_fourn LIKE '" . $this->db->escape($prefix . $crit) . "%'"; } $sql .= ")"; $i++; @@ -3147,6 +3147,15 @@ class Form if (isModEnabled('barcode')) { $sql .= " OR p.barcode LIKE '" . $this->db->escape($prefix . $filterkey) . "%'"; } + + // include search in supplier ref + if (getDolGlobalString('MAIN_SEARCH_PRODUCT_BY_FOURN_REF')) { + $sql .= " OR EXISTS (SELECT pfp.fk_product FROM " . $this->db->prefix() . "product_fournisseur_price as pfp WHERE p.rowid = pfp.fk_product"; + $sql .= " AND ("; + $sql .= $sqlSupplierSearch; + $sql .= "))"; + } + $sql .= ')'; } if (count($warehouseStatusArray)) { diff --git a/htdocs/core/lib/company.lib.php b/htdocs/core/lib/company.lib.php index 1e3f5fe9db2..fc8177a2993 100644 --- a/htdocs/core/lib/company.lib.php +++ b/htdocs/core/lib/company.lib.php @@ -1828,6 +1828,7 @@ function show_actions_done($conf, $langs, $db, $filterobj, $objcon = null, $nopr $sql .= " a.percent as percent, 'action' as type,"; $sql .= " a.fk_element, a.elementtype,"; $sql .= " a.fk_contact,"; + $sql .= " a.code,"; $sql .= " c.code as acode, c.libelle as alabel, c.picto as apicto,"; $sql .= " u.rowid as user_id, u.login as user_login, u.photo as user_photo, u.firstname as user_firstname, u.lastname as user_lastname"; if (is_object($filterobj) && in_array(get_class($filterobj), array('Societe', 'Client', 'Fournisseur'))) { @@ -2025,6 +2026,7 @@ function show_actions_done($conf, $langs, $db, $filterobj, $objcon = null, $nopr $sql .= $hookmanager->resPrint; } + // Now add events of emailing module if (is_array($actioncode)) { foreach ($actioncode as $code) { $sql2 = addMailingEventTypeSQL($code, $objcon, $filterobj); @@ -2106,6 +2108,9 @@ function show_actions_done($conf, $langs, $db, $filterobj, $objcon = null, $nopr 'firstname' => empty($obj->firstname) ? '' : $obj->firstname, 'fk_element' => (int) $obj->fk_element, 'elementtype' => $obj->elementtype, + + 'code' => $obj->code, + // Type of event 'acode' => $obj->acode, 'alabel' => $obj->alabel, @@ -2121,6 +2126,10 @@ function show_actions_done($conf, $langs, $db, $filterobj, $objcon = null, $nopr 'dateend' => $db->jdate($obj->dp2), 'note' => $obj->label, 'percent' => (int) $obj->percent, + + 'code' => $obj->code, + + // Type of event 'acode' => $obj->acode, 'userid' => (int) $obj->user_id, @@ -2257,11 +2266,14 @@ function show_actions_done($conf, $langs, $db, $filterobj, $objcon = null, $nopr require_once DOL_DOCUMENT_ROOT.'/comm/action/class/cactioncomm.class.php'; $caction = new CActionComm($db); - $arraylist = $caction->liste_array(1, 'code', '', (!getDolGlobalString('AGENDA_USE_EVENT_TYPE') ? 1 : 0), '', 1); + $arraylist = $caction->liste_array(1, 'code', '', (getDolGlobalString('AGENDA_USE_EVENT_TYPE') ? 0 : 1), '', 1); foreach ($histo as $key => $value) { $actionstatic->fetch($histo[$key]['id']); // TODO Do we need this, we already have a lot of data of line into $histo + if (empty($actionstatic->code)) { + $actionstatic->code = $histo[$key]['acode']; + } $actionstatic->type_picto = $histo[$key]['apicto'] ?? ''; $actionstatic->type_code = $histo[$key]['acode']; @@ -2333,25 +2345,38 @@ function show_actions_done($conf, $langs, $db, $filterobj, $objcon = null, $nopr } $out .= ''; + // type_code // column "acode" in the sql = code in type of actioncomm, example: AC_OTH_AUTO, AC_EMAILING + // code // columne code in the sql (not yet added), can be AC_CONTACT_SENTBYMAIL, ... + // Type - $labeltype = $actionstatic->type_code; - if (!getDolGlobalString('AGENDA_USE_EVENT_TYPE') && empty($arraylist[$labeltype])) { - $labeltype = 'AC_OTH'; + $labelOfTypeToShow = $actionstatic->type_code; + //$typelabel = $actionstatic->type_label; + $code = $actionstatic->code; + if (!getDolGlobalString('AGENDA_USE_EVENT_TYPE') && empty($arraylist[$labelOfTypeToShow])) { + $labelOfTypeToShow = 'AC_OTH'; } if (!empty($actionstatic->code) && preg_match('/^TICKET_MSG/', $actionstatic->code)) { - $labeltype = $langs->trans("Message"); + $labelOfTypeToShow = $langs->trans("Message"); } else { - if (!empty($arraylist[$labeltype])) { - $labeltype = $arraylist[$labeltype]; + if (!empty($arraylist[$labelOfTypeToShow])) { + $labelOfTypeToShow = $arraylist[$labelOfTypeToShow]; + } elseif ($actionstatic->type_code == 'AC_EMAILING') { + $labelOfTypeToShow = $langs->trans("Emailing"); } - if ($actionstatic->type_code == 'AC_OTH_AUTO' && ($actionstatic->type_code != $actionstatic->code) && $labeltype && !empty($arraylist[$actionstatic->code])) { - $labeltype .= ' - '.$arraylist[$actionstatic->code]; // Use code in priority on type_code + if ($actionstatic->type_code == 'AC_OTH_AUTO' && ($actionstatic->type_code != $actionstatic->code) && $labelOfTypeToShow && !empty($arraylist[$actionstatic->code])) { + $labelOfTypeToShow .= ' - '.$arraylist[$actionstatic->code]; // Show also detailed code } } - $out .= ''; + + $labelOfTypeToShowLong = $labelOfTypeToShow; + if ($actionstatic->type_code == 'AC_OTH_AUTO') { + $labelOfTypeToShowLong .= ' (auto)'; + } + + $out .= ''; $out .= $actionstatic->getTypePicto(); //if (empty($conf->dol_optimize_smallscreen)) { - $out .= $labeltype; + $out .= $labelOfTypeToShow; //} $out .= ''; @@ -2360,18 +2385,19 @@ function show_actions_done($conf, $langs, $db, $filterobj, $objcon = null, $nopr if (isset($histo[$key]['type']) && $histo[$key]['type'] == 'action') { $transcode = $langs->trans("Action".$histo[$key]['acode']); //$libelle = ($transcode != "Action".$histo[$key]['acode'] ? $transcode : $histo[$key]['alabel']); - $libelle = $histo[$key]['note']; + $label = $histo[$key]['note']; $actionstatic->id = $histo[$key]['id']; - $out .= ' title="'.dol_escape_htmltag($libelle).'">'; - $out .= dol_trunc($libelle, 120); + $out .= ' title="'.dol_escape_htmltag($label).'">'; + $out .= dol_trunc($label, 120); } if (isset($histo[$key]['type']) && $histo[$key]['type'] == 'mailing') { $transcode = $langs->trans("Action".$histo[$key]['acode']); - $libelle = ($transcode != "Action".$histo[$key]['acode'] ? $transcode : 'Send mass mailing'); + $label = ($transcode != "Action".$histo[$key]['acode'] ? $transcode : 'Send mass mailing'); + $label .= ' - '.$histo[$key]['note']; $out .= ''; - $out .= img_object($langs->trans("ShowEMailing"), "email").' '; - $out .= dol_trunc($libelle, 120); + $out .= ' title="'.dol_escape_htmltag($label).'">'; + //$out .= img_object($langs->trans("EMailing").'
'.$histo[$key]['note'], "email").' '; + $out .= dol_trunc($label, 120); $out .= '
'; } $out .= ''; @@ -2625,7 +2651,7 @@ function addMailingEventTypeSQL($actioncode, $objcon, $filterobj) if (isModEnabled('mailing') && !empty($objcon->email) && (empty($actioncode) || $actioncode == 'AC_OTH_AUTO' || $actioncode == 'AC_EMAILING')) { $sql2 = "SELECT m.rowid as id, m.titre as label, mc.date_envoi as dp, mc.date_envoi as dp2, '100' as percent, 'mailing' as type"; $sql2 .= ", null as fk_element, '' as elementtype, null as contact_id"; - $sql2 .= ", 'AC_EMAILING' as acode, '' as alabel, '' as apicto"; + $sql2 .= ", 'AC_EMAILING' as code, 'AC_EMAILING' as acode, '' as alabel, '' as apicto"; $sql2 .= ", u.rowid as user_id, u.login as user_login, u.photo as user_photo, u.firstname as user_firstname, u.lastname as user_lastname"; // User that valid action if (is_object($filterobj) && get_class($filterobj) == 'Societe') { $sql2 .= ", '' as lastname, '' as firstname"; diff --git a/htdocs/core/lib/files.lib.php b/htdocs/core/lib/files.lib.php index a5e89c8e988..6795862ee26 100644 --- a/htdocs/core/lib/files.lib.php +++ b/htdocs/core/lib/files.lib.php @@ -3174,7 +3174,7 @@ function dol_check_secure_access_document($modulepart, $original_file, $entity, $original_file = $conf->fckeditor->dir_output.'/'.$original_file; } elseif ($modulepart == 'user' && !empty($conf->user->dir_output)) { // Wrapping for users - $canreaduser = (!empty($fuser->admin) || $fuser->rights->user->user->{$lire}); + $canreaduser = (!empty($fuser->admin) || $fuser->hasRight('user', 'user', 'lire')); if ($fuser->id == (int) $refname) { $canreaduser = 1; } // A user can always read its own card diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php index b4540e15941..8b79a7f4a63 100644 --- a/htdocs/core/lib/functions.lib.php +++ b/htdocs/core/lib/functions.lib.php @@ -10513,10 +10513,11 @@ function dol_osencode($str) * @param string $fieldid Field to get * @param int $entityfilter Filter by entity * @param string $filters Filters to add. WARNING: string must be escaped for SQL and not coming from user input. + * @param bool $useCache If true (default), cache will be queried and updated. * @return int<-1,max>|string ID of code if OK, 0 if key empty, -1 if KO * @see $langs->getLabelFromKey */ -function dol_getIdFromCode($db, $key, $tablename, $fieldkey = 'code', $fieldid = 'id', $entityfilter = 0, $filters = '') +function dol_getIdFromCode($db, $key, $tablename, $fieldkey = 'code', $fieldid = 'id', $entityfilter = 0, $filters = '', $useCache = true) { global $conf; @@ -10526,7 +10527,7 @@ function dol_getIdFromCode($db, $key, $tablename, $fieldkey = 'code', $fieldid = } // Check in cache - if (isset($conf->cache['codeid'][$tablename][$key][$fieldid])) { // Can be defined to 0 or '' + if ($useCache && isset($conf->cache['codeid'][$tablename][$key][$fieldid])) { // Can be defined to 0 or '' return $conf->cache['codeid'][$tablename][$key][$fieldid]; // Found in cache } @@ -10549,14 +10550,16 @@ function dol_getIdFromCode($db, $key, $tablename, $fieldkey = 'code', $fieldid = $resql = $db->query($sql); if ($resql) { $obj = $db->fetch_object($resql); + $valuetoget = ''; if ($obj) { - $conf->cache['codeid'][$tablename][$key][$fieldid] = $obj->valuetoget; + $valuetoget = $obj->valuetoget; + $conf->cache['codeid'][$tablename][$key][$fieldid] = $valuetoget; } else { $conf->cache['codeid'][$tablename][$key][$fieldid] = ''; } $db->free($resql); - return $conf->cache['codeid'][$tablename][$key][$fieldid]; + return $valuetoget; } else { return -1; } diff --git a/htdocs/core/lib/pdf.lib.php b/htdocs/core/lib/pdf.lib.php index 23c1a643b54..fd51a9a474b 100644 --- a/htdocs/core/lib/pdf.lib.php +++ b/htdocs/core/lib/pdf.lib.php @@ -1506,6 +1506,8 @@ function pdf_getlinedesc($object, $i, $outputlangs, $hideref = 0, $hidedesc = 0, $note = (!empty($object->lines[$i]->note) ? $object->lines[$i]->note : ''); $dbatch = (!empty($object->lines[$i]->detail_batch) ? $object->lines[$i]->detail_batch : false); + $multilangsactive = getDolGlobalInt('MAIN_MULTILANGS'); + if ($issupplierline) { include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php'; $prodser = new ProductFournisseur($db); @@ -1518,11 +1520,55 @@ function pdf_getlinedesc($object, $i, $outputlangs, $hideref = 0, $hidedesc = 0, } } + //id + $idprod = (!empty($object->lines[$i]->fk_product) ? $object->lines[$i]->fk_product : false); if ($idprod) { $prodser->fetch($idprod); + //load multilangs + if ($multilangsactive) { + $prodser->getMultiLangs(); + $object->lines[$i]->multilangs = $prodser->multilangs; + } + } + //label + if (!empty($object->lines[$i]->label)) { + $label = $object->lines[$i]->label; + } else { + if (!empty($object->lines[$i]->multilangs[$outputlangs->defaultlang]['label']) && $multilangsactive) { + $label = $object->lines[$i]->multilangs[$outputlangs->defaultlang]['label']; + } else { + if (!empty($object->lines[$i]->product_label)) { + $label = $object->lines[$i]->product_label; + } else { + $label = ''; + } + } + } + //description + if (!empty($object->lines[$i]->desc)) { + $desc = $object->lines[$i]->desc; + } else { + if (!empty($object->lines[$i]->multilangs[$outputlangs->defaultlang]['description']) && $multilangsactive) { + $desc = $object->lines[$i]->multilangs[$outputlangs->defaultlang]['description']; + } else { + if (!empty($object->lines[$i]->description)) { + $desc = $object->lines[$i]->description; + } else { + $desc = ''; + } + } + } + //ref supplier + $ref_supplier = (!empty($object->lines[$i]->ref_supplier) ? $object->lines[$i]->ref_supplier : (!empty($object->lines[$i]->ref_fourn) ? $object->lines[$i]->ref_fourn : '')); // TODO Not yet saved for supplier invoices, only supplier orders + //note + $note = (!empty($object->lines[$i]->note) ? $object->lines[$i]->note : ''); + //dbatch + $dbatch = (!empty($object->lines[$i]->detail_batch) ? $object->lines[$i]->detail_batch : false); + + if ($idprod) { // If a predefined product and multilang and on other lang, we renamed label with label translated - if (getDolGlobalInt('MAIN_MULTILANGS') && ($outputlangs->defaultlang != $langs->defaultlang)) { - $translatealsoifmodified = (getDolGlobalString('MAIN_MULTILANG_TRANSLATE_EVEN_IF_MODIFIED')); // By default if value was modified manually, we keep it (no translation because we don't have it) + if ($multilangsactive && ($outputlangs->defaultlang != $langs->defaultlang)) { + $translatealsoifmodified = getDolGlobalString('MAIN_MULTILANG_TRANSLATE_EVEN_IF_MODIFIED'); // By default if value was modified manually, we keep it (no translation because we don't have it) // TODO Instead of making a compare to see if param was modified, check that content contains reference translation. If yes, add the added part to the new translation // ($textwasnotmodified is replaced with $textwasmodifiedorcompleted and we add completion). diff --git a/htdocs/core/triggers/interface_50_modTicket_TicketEmail.class.php b/htdocs/core/triggers/interface_50_modTicket_TicketEmail.class.php index 9661cdb6416..40cd8f49827 100644 --- a/htdocs/core/triggers/interface_50_modTicket_TicketEmail.class.php +++ b/htdocs/core/triggers/interface_50_modTicket_TicketEmail.class.php @@ -197,6 +197,34 @@ class InterfaceTicketEmail extends DolibarrTriggers } } + // Send email to assignee if an assignee was set at creation + if ($object->fk_user_assign > 0 && $object->fk_user_assign != $user->id && empty($object->context['disableticketemail'])) { + $userstat = new User($this->db); + $res = $userstat->fetch($object->fk_user_assign); + if ($res > 0) { + // Send email to notification email + if (!getDolGlobalString('TICKET_DISABLE_ALL_MAILS')) { + // Send email to assigned user + $sendto = $userstat->email; + if (!getDolGlobalString('TICKET_DISABLE_MAIL_AUTOCOPY_TO')) { + $old_MAIN_MAIL_AUTOCOPY_TO = $conf->global->MAIN_MAIL_AUTOCOPY_TO; + $conf->global->MAIN_MAIL_AUTOCOPY_TO = ''; + } + + if (!empty($sendto)) { + $this->composeAndSendAssigneeMessage($sendto, $subject_assignee, $body_assignee, $see_ticket_assignee, $object, $langs); + } + + if (!getDolUserString('TICKET_DISABLE_MAIL_AUTOCOPY_TO')) { + $conf->global->MAIN_MAIL_AUTOCOPY_TO = $old_MAIN_MAIL_AUTOCOPY_TO; + } + } + } else { + $this->error = $userstat->error; + $this->errors = $userstat->errors; + } + } + // Send email to customer // Note: $object->context['disableticketemail'] is set to 1 by public interface at creation because email sending is already managed by page // $object->context['createdfrompublicinterface'] may also be defined when creation done from public interface diff --git a/htdocs/don/class/api_donations.class.php b/htdocs/don/class/api_donations.class.php index 45e9247710d..6eb89871470 100644 --- a/htdocs/don/class/api_donations.class.php +++ b/htdocs/don/class/api_donations.class.php @@ -256,6 +256,13 @@ class Donations extends DolibarrApi continue; } + if ($field == 'array_options' && is_array($value)) { + foreach ($value as $index => $val) { + $this->don->array_options[$index] = $this->_checkValForAPI($field, $val, $this->don); + } + continue; + } + $this->don->$field = $this->_checkValForAPI($field, $value, $this->don); } diff --git a/htdocs/expedition/class/api_shipments.class.php b/htdocs/expedition/class/api_shipments.class.php index 4d67ffe024f..1bd9b78a711 100644 --- a/htdocs/expedition/class/api_shipments.class.php +++ b/htdocs/expedition/class/api_shipments.class.php @@ -483,6 +483,7 @@ class Shipments extends DolibarrApi $this->shipment->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09'); continue; } + if ($field == 'array_options' && is_array($value)) { foreach ($value as $index => $val) { $this->shipment->array_options[$index] = $this->_checkValForAPI($field, $val, $this->shipment); diff --git a/htdocs/expensereport/class/api_expensereports.class.php b/htdocs/expensereport/class/api_expensereports.class.php index 5d5ec2ad316..d4d20084e2e 100644 --- a/htdocs/expensereport/class/api_expensereports.class.php +++ b/htdocs/expensereport/class/api_expensereports.class.php @@ -460,6 +460,13 @@ class ExpenseReports extends DolibarrApi continue; } + if ($field == 'array_options' && is_array($value)) { + foreach ($value as $index => $val) { + $this->expensereport->array_options[$index] = $this->_checkValForAPI($field, $val, $this->expensereport); + } + continue; + } + $this->expensereport->$field = $this->_checkValForAPI($field, $value, $this->expensereport); } diff --git a/htdocs/expensereport/class/expensereport.class.php b/htdocs/expensereport/class/expensereport.class.php index afd28c3d3c6..0ce56691fcb 100644 --- a/htdocs/expensereport/class/expensereport.class.php +++ b/htdocs/expensereport/class/expensereport.class.php @@ -694,6 +694,7 @@ class ExpenseReport extends CommonObject $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as d"; if ($ref) { $sql .= " WHERE d.ref = '".$this->db->escape($ref)."'"; + $sql .= " AND d.entity IN (".getEntity('expensereport').")"; } else { $sql .= " WHERE d.rowid = ".((int) $id); } diff --git a/htdocs/fourn/class/api_supplier_invoices.class.php b/htdocs/fourn/class/api_supplier_invoices.class.php index 06d9efaee2a..ae52a4705fa 100644 --- a/htdocs/fourn/class/api_supplier_invoices.class.php +++ b/htdocs/fourn/class/api_supplier_invoices.class.php @@ -289,6 +289,7 @@ class SupplierInvoices extends DolibarrApi } continue; } + $this->invoice->$field = $this->_checkValForAPI($field, $value, $this->invoice); } diff --git a/htdocs/fourn/class/api_supplier_orders.class.php b/htdocs/fourn/class/api_supplier_orders.class.php index a8a42ce1457..972c81da5ff 100644 --- a/htdocs/fourn/class/api_supplier_orders.class.php +++ b/htdocs/fourn/class/api_supplier_orders.class.php @@ -312,6 +312,7 @@ class SupplierOrders extends DolibarrApi } continue; } + $this->order->$field = $this->_checkValForAPI($field, $value, $this->order); } diff --git a/htdocs/includes/odtphp/odf.php b/htdocs/includes/odtphp/odf.php index 1198d667c61..5b3593f29a3 100644 --- a/htdocs/includes/odtphp/odf.php +++ b/htdocs/includes/odtphp/odf.php @@ -91,8 +91,8 @@ class Odf public $userdefined = array(); const PIXEL_TO_CM = 0.026458333; - const FIND_TAGS_REGEX = '/<([A-Za-z0-9]+)(?:\s([A-Za-z]+(?:\-[A-Za-z]+)?(?:=(?:".*?")|(?:[0-9]+))))*(?:(?:\s\/>)|(?:>(.*)<\/\1>))/s'; - const FIND_ENCODED_TAGS_REGEX = '/<([A-Za-z]+)(?:\s([A-Za-z]+(?:\-[A-Za-z]+)?(?:=(?:".*?")|(?:[0-9]+))))*(?:(?:\s\/>)|(?:>(.*)<\/\1>))/'; + const FIND_TAGS_REGEX = '/<([A-Za-z0-9]+)(?:\s([A-Za-z]+(?:\-[A-Za-z]+)?(?:=(?:".*?")|(?:[0-9]+))))*(?:(?:\s\/>)|(?:>(((?!<\1(\s.*)?>).)*)<\/\1>))/s'; + const FIND_ENCODED_TAGS_REGEX = '/<([A-Za-z]+)(?:\s([A-Za-z]+(?:\-[A-Za-z]+)?(?:=(?:".*?")|(?:[0-9]+))))*(?:(?:\s\/>)|(?:>(((?!<\1(\s.*)?>).)*)<\/\1>))/'; /** diff --git a/htdocs/install/mysql/tables/llx_blockedlog.key.sql b/htdocs/install/mysql/tables/llx_blockedlog.key.sql index 065be82aba1..e1e2c956609 100644 --- a/htdocs/install/mysql/tables/llx_blockedlog.key.sql +++ b/htdocs/install/mysql/tables/llx_blockedlog.key.sql @@ -22,4 +22,5 @@ ALTER TABLE llx_blockedlog ADD INDEX entity (entity); ALTER TABLE llx_blockedlog ADD INDEX fk_user (fk_user); ALTER TABLE llx_blockedlog ADD INDEX entity_action_certified (entity,action,certified); -ALTER TABLE llx_blockedlog ADD INDEX entity_rowid (entity, rowid); -- for the "SELECT rowid, signature FROM llx_blockedlog FORCE INDEX entity_rowid WHERE entity = x AND rowid < z ORDER BY rowid DESC" +-- We add this index for the "SELECT rowid, signature FROM llx_blockedlog FORCE INDEX entity_rowid WHERE entity = x AND rowid < z ORDER BY rowid DESC" +ALTER TABLE llx_blockedlog ADD INDEX entity_rowid (entity, rowid); diff --git a/htdocs/knowledgemanagement/class/api_knowledgemanagement.class.php b/htdocs/knowledgemanagement/class/api_knowledgemanagement.class.php index 9431c091d60..21c0fe2a59b 100644 --- a/htdocs/knowledgemanagement/class/api_knowledgemanagement.class.php +++ b/htdocs/knowledgemanagement/class/api_knowledgemanagement.class.php @@ -311,6 +311,13 @@ class KnowledgeManagement extends DolibarrApi continue; } + if ($field == 'array_options' && is_array($value)) { + foreach ($value as $index => $val) { + $this->knowledgerecord->array_options[$index] = $this->_checkValForAPI($field, $val, $this->knowledgerecord); + } + continue; + } + $this->knowledgerecord->$field = $this->_checkValForAPI($field, $value, $this->knowledgerecord); } diff --git a/htdocs/langs/en_US/stripe.lang b/htdocs/langs/en_US/stripe.lang index 2e244858d28..a4a8bab2bf3 100644 --- a/htdocs/langs/en_US/stripe.lang +++ b/htdocs/langs/en_US/stripe.lang @@ -12,6 +12,7 @@ YourEMail=Email to receive payment confirmation STRIPE_PAYONLINE_SENDEMAIL=Email notification after a payment attempt (success or fail) Creditor=Creditor PaymentCode=Payment code +StripeAutoRecordPayout=Enable the auto recording of payout (when Stripe do a payout and call the webhook IPN payout.create/payout.paid) StripeDoPayment=Pay with Stripe YouWillBeRedirectedOnStripe=You will be redirected on secured Stripe page to input you credit card information Continue=Next diff --git a/htdocs/loan/class/loan.class.php b/htdocs/loan/class/loan.class.php index ec13a9ec94b..abcfd857a84 100644 --- a/htdocs/loan/class/loan.class.php +++ b/htdocs/loan/class/loan.class.php @@ -359,6 +359,7 @@ class Loan extends CommonObject $accountline->fetch($line_url['fk_bank']); $result = $accountline->delete_urls($user); if ($result < 0) { + $this->errors = array_merge($this->errors, [$accountline->error], $accountline->errors); $error++; } } diff --git a/htdocs/modulebuilder/template/class/api_mymodule.class.php b/htdocs/modulebuilder/template/class/api_mymodule.class.php index e67def67c0c..a57d497ea30 100644 --- a/htdocs/modulebuilder/template/class/api_mymodule.class.php +++ b/htdocs/modulebuilder/template/class/api_mymodule.class.php @@ -288,6 +288,13 @@ class MyModuleApi extends DolibarrApi continue; } + if ($field == 'array_options' && is_array($value)) { + foreach ($value as $index => $val) { + $this->myobject->array_options[$index] = $this->_checkValForAPI($field, $val, $this->myobject); + } + continue; + } + $this->myobject->$field = $this->_checkValForAPI($field, $value, $this->myobject); } diff --git a/htdocs/mrp/class/api_mos.class.php b/htdocs/mrp/class/api_mos.class.php index d4e23183018..7b267902f66 100644 --- a/htdocs/mrp/class/api_mos.class.php +++ b/htdocs/mrp/class/api_mos.class.php @@ -235,6 +235,13 @@ class Mos extends DolibarrApi continue; } + if ($field == 'array_options' && is_array($value)) { + foreach ($value as $index => $val) { + $this->mo->array_options[$index] = $this->_checkValForAPI($field, $val, $this->mo); + } + continue; + } + $this->mo->$field = $this->_checkValForAPI($field, $value, $this->mo); } diff --git a/htdocs/partnership/class/api_partnerships.class.php b/htdocs/partnership/class/api_partnerships.class.php index 42c11b23529..5a72c67ea5d 100644 --- a/htdocs/partnership/class/api_partnerships.class.php +++ b/htdocs/partnership/class/api_partnerships.class.php @@ -249,6 +249,12 @@ class Partnerships extends DolibarrApi $this->partnership->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09'); continue; } + if ($field == 'array_options' && is_array($value)) { + foreach ($value as $index => $val) { + $this->partnership->array_options[$index] = $this->_checkValForAPI($field, $val, $this->partnership); + } + continue; + } $this->partnership->$field = $this->_checkValForAPI($field, $value, $this->partnership); } diff --git a/htdocs/product/class/api_products.class.php b/htdocs/product/class/api_products.class.php index a66c84c55d7..8e1bb74e0d7 100644 --- a/htdocs/product/class/api_products.class.php +++ b/htdocs/product/class/api_products.class.php @@ -400,6 +400,7 @@ class Products extends DolibarrApi } continue; } + $this->product->$field = $this->_checkValForAPI($field, $value, $this->product); } diff --git a/htdocs/product/stock/class/api_warehouses.class.php b/htdocs/product/stock/class/api_warehouses.class.php index 0f533c39bbe..bca43d80a40 100644 --- a/htdocs/product/stock/class/api_warehouses.class.php +++ b/htdocs/product/stock/class/api_warehouses.class.php @@ -217,6 +217,13 @@ class Warehouses extends DolibarrApi continue; } + if ($field == 'array_options' && is_array($value)) { + foreach ($value as $index => $val) { + $this->warehouse->array_options[$index] = $this->_checkValForAPI($field, $val, $this->warehouse); + } + continue; + } + $this->warehouse->$field = $this->_checkValForAPI($field, $value, $this->warehouse); } diff --git a/htdocs/public/payment/paymentko.php b/htdocs/public/payment/paymentko.php index 4e3cc764659..db16c74ab21 100644 --- a/htdocs/public/payment/paymentko.php +++ b/htdocs/public/payment/paymentko.php @@ -194,8 +194,16 @@ if (!empty($_SESSION['ipaddress'])) { // To avoid to make action twice // Send warning of error to administrator if ($sendemail) { + // Get default language to use for the company for supervision emails + $myCompanyDefaultLang = $mysoc->default_lang; + if (empty($myCompanyDefaultLang) || $myCompanyDefaultLang === 'auto') { + // We must guess the language from the company country. We must not use the language of the visitor. This is a technical email for supervision + // so it must always be into the same language. + $myCompanyDefaultLang = getLanguageCodeFromCountryCode($mysoc->country_code); + } + $companylangs = new Translate('', $conf); - $companylangs->setDefaultLang($mysoc->default_lang); + $companylangs->setDefaultLang($myCompanyDefaultLang); $companylangs->loadLangs(array('main', 'members', 'bills', 'paypal', 'paybox', 'stripe')); $from = getDolGlobalString("MAIN_MAIL_EMAIL_FROM"); diff --git a/htdocs/public/payment/paymentok.php b/htdocs/public/payment/paymentok.php index cbfaae76079..aa46e7ccf0b 100644 --- a/htdocs/public/payment/paymentok.php +++ b/htdocs/public/payment/paymentok.php @@ -1957,8 +1957,16 @@ if ($ispaymentok) { // Send an email to the admins if ($sendemail) { + // Get default language to use for the company for supervision emails + $myCompanyDefaultLang = $mysoc->default_lang; + if (empty($myCompanyDefaultLang) || $myCompanyDefaultLang === 'auto') { + // We must guess the language from the company country. We must not use the language of the visitor. This is a technical email for supervision + // so it must always be into the same language. + $myCompanyDefaultLang = getLanguageCodeFromCountryCode($mysoc->country_code); + } + $companylangs = new Translate('', $conf); - $companylangs->setDefaultLang($mysoc->default_lang); + $companylangs->setDefaultLang($myCompanyDefaultLang); $companylangs->loadLangs(array('main', 'members', 'bills', 'paypal', 'paybox', 'stripe')); $sendto = $sendemail; diff --git a/htdocs/public/stripe/ipn.php b/htdocs/public/stripe/ipn.php index 0a2c982258d..e04053634d7 100644 --- a/htdocs/public/stripe/ipn.php +++ b/htdocs/public/stripe/ipn.php @@ -178,14 +178,14 @@ top_httphead(); dol_syslog("***** Stripe IPN was called with event->type=".$event->type." service=".$service); -if ($event->type == 'payout.created') { - // When a payout is create by Stripe to transfer money to your account +if ($event->type == 'payout.created' && getDolGlobalString('STRIPE_AUTO_RECORD_PAYOUT')) { + // When a payout is created by Stripe to transfer money to your account $error = 0; $result = dolibarr_set_const($db, $service."_NEXTPAYOUT", date('Y-m-d H:i:s', $event->data->object->arrival_date), 'chaine', 0, '', $conf->entity); if ($result > 0) { - $subject = $societeName.' - [NOTIFICATION] Stripe payout scheduled'; + $subject = '['.$societeName.'] Notification - Stripe payout scheduled'; if (!empty($user->email)) { $sendto = dolGetFirstLastname($user->firstname, $user->lastname)." <".$user->email.">"; } else { @@ -221,7 +221,7 @@ if ($event->type == 'payout.created') { http_response_code(500); return -1; } -} elseif ($event->type == 'payout.paid') { +} elseif ($event->type == 'payout.paid' && getDolGlobalString('STRIPE_AUTO_RECORD_PAYOUT')) { // When a payout to transfer money to your account is completely done $error = 0; $result = dolibarr_set_const($db, $service."_NEXTPAYOUT", 0, 'chaine', 0, '', $conf->entity); @@ -249,6 +249,8 @@ if ($event->type == 'payout.created') { $typefrom = 'PRE'; $typeto = 'VIR'; + $db->begin(); + if (!$error) { $bank_line_id_from = $accountfrom->addline($dateo, $typefrom, $label, -1 * (float) price2num($amount), '', '', $user); } @@ -274,38 +276,47 @@ if ($event->type == 'payout.created') { if (!($result > 0)) { $error++; } + + if (!$error) { + $db->commit(); + } else { + $db->rollback(); + } + + // Send email + if (!$error) { + $subject = '['.$societeName.'] - NotificationOTIFICATION] Stripe payout done'; + if (!empty($user->email)) { + $sendto = dolGetFirstLastname($user->firstname, $user->lastname)." <".$user->email.">"; + } else { + $sendto = getDolGlobalString('MAIN_INFO_SOCIETE_MAIL') . '" <' . getDolGlobalString('MAIN_INFO_SOCIETE_MAIL').'>'; + } + $replyto = $sendto; + $sendtocc = ''; + if (getDolGlobalString('ONLINE_PAYMENT_SENDEMAIL')) { + $sendtocc = getDolGlobalString('ONLINE_PAYMENT_SENDEMAIL') . '" <' . getDolGlobalString('ONLINE_PAYMENT_SENDEMAIL').'>'; + } + + $message = "A bank transfer of ".price2num($event->data->object->amount / 100)." ".$event->data->object->currency." has been done to your account the ".dol_print_date($event->data->object->arrival_date, 'dayhour'); + + $mailfile = new CMailFile( + $subject, + $sendto, + $replyto, + $message, + array(), + array(), + array(), + $sendtocc, + '', + 0, + -1 + ); + + $ret = $mailfile->sendfile(); + } } - $subject = $societeName.' - [NOTIFICATION] Stripe payout done'; - if (!empty($user->email)) { - $sendto = dolGetFirstLastname($user->firstname, $user->lastname)." <".$user->email.">"; - } else { - $sendto = getDolGlobalString('MAIN_INFO_SOCIETE_MAIL') . '" <' . getDolGlobalString('MAIN_INFO_SOCIETE_MAIL').'>'; - } - $replyto = $sendto; - $sendtocc = ''; - if (getDolGlobalString('ONLINE_PAYMENT_SENDEMAIL')) { - $sendtocc = getDolGlobalString('ONLINE_PAYMENT_SENDEMAIL') . '" <' . getDolGlobalString('ONLINE_PAYMENT_SENDEMAIL').'>'; - } - - $message = "A bank transfer of ".price2num($event->data->object->amount / 100)." ".$event->data->object->currency." has been done to your account the ".dol_print_date($event->data->object->arrival_date, 'dayhour'); - - $mailfile = new CMailFile( - $subject, - $sendto, - $replyto, - $message, - array(), - array(), - array(), - $sendtocc, - '', - 0, - -1 - ); - - $ret = $mailfile->sendfile(); - return 1; } else { $error++; diff --git a/htdocs/reception/class/api_receptions.class.php b/htdocs/reception/class/api_receptions.class.php index 11f869d4df0..564c21bbb28 100644 --- a/htdocs/reception/class/api_receptions.class.php +++ b/htdocs/reception/class/api_receptions.class.php @@ -496,6 +496,13 @@ class Receptions extends DolibarrApi continue; } + if ($field == 'array_options' && is_array($value)) { + foreach ($value as $index => $val) { + $this->reception->array_options[$index] = $this->_checkValForAPI($field, $val, $this->reception); + } + continue; + } + $this->reception->$field = $this->_checkValForAPI($field, $value, $this->reception); } diff --git a/htdocs/societe/class/api_contacts.class.php b/htdocs/societe/class/api_contacts.class.php index 365d2968f0b..d6bbee5a815 100644 --- a/htdocs/societe/class/api_contacts.class.php +++ b/htdocs/societe/class/api_contacts.class.php @@ -395,7 +395,7 @@ class Contacts extends DolibarrApi } if ($field == 'array_options' && is_array($value)) { foreach ($value as $index => $val) { - $this->contact->array_options[$index] = $this->_checkValForAPI('extrafields', $val, $this->contact); + $this->contact->array_options[$index] = $this->_checkValForAPI($field, $val, $this->contact); } continue; } diff --git a/htdocs/societe/class/api_thirdparties.class.php b/htdocs/societe/class/api_thirdparties.class.php index 9f1049544cf..d42054361b6 100644 --- a/htdocs/societe/class/api_thirdparties.class.php +++ b/htdocs/societe/class/api_thirdparties.class.php @@ -331,7 +331,7 @@ class Thirdparties extends DolibarrApi } if ($field == 'array_options' && is_array($value)) { foreach ($value as $index => $val) { - $this->company->array_options[$index] = $val; + $this->company->array_options[$index] = $this->_checkValForAPI($field, $val, $this->company); } continue; } diff --git a/htdocs/stripe/admin/stripe.php b/htdocs/stripe/admin/stripe.php index a017742d722..eb08aac811d 100644 --- a/htdocs/stripe/admin/stripe.php +++ b/htdocs/stripe/admin/stripe.php @@ -379,18 +379,30 @@ print ''.$langs->trans("Example").': '.$mysoc->name.''; print ''; -print ''; -print $langs->trans("StripeUserAccountForActions").''; -print img_picto('', 'user', 'class="pictofixedwidth"').$form->select_dolusers(getDolGlobalString('STRIPE_USER_ACCOUNT_FOR_ACTIONS'), 'STRIPE_USER_ACCOUNT_FOR_ACTIONS', 0); -print ''; - print ''; print $langs->trans("BankAccount").''; print img_picto('', 'bank_account', 'class="pictofixedwidth"'); $form->select_comptes(getDolGlobalString('STRIPE_BANK_ACCOUNT_FOR_PAYMENTS'), 'STRIPE_BANK_ACCOUNT_FOR_PAYMENTS', 0, '', 1); print ''; -if (getDolGlobalInt('MAIN_FEATURES_LEVEL') >= 2) { // What is this for ? + +// Param to record automatically payouts (received from IPN payout.payed and payout.created) +print ''; +print $langs->trans("StripeAutoRecordPayout").''; +if ($conf->use_javascript_ajax) { + print ajax_constantonoff('STRIPE_AUTO_RECORD_PAYOUT', array(), null, 0, 0, 1); +} else { + $arrval = array('0' => $langs->trans("No"), '1' => $langs->trans("Yes")); + print $form->selectarray("STRIPE_AUTO_RECORD_PAYOUT", $arrval, getDolGlobalInt('STRIPE_AUTO_RECORD_PAYOUT')); +} +print ''; + +if (getDolGlobalInt('STRIPE_AUTO_RECORD_PAYOUT')) { + print ''; + print $langs->trans("StripeUserAccountForActions").''; + print img_picto('', 'user', 'class="pictofixedwidth"').$form->select_dolusers(getDolGlobalString('STRIPE_USER_ACCOUNT_FOR_ACTIONS'), 'STRIPE_USER_ACCOUNT_FOR_ACTIONS', 0); + print ''; + print ''; print $langs->trans("BankAccountForBankTransfer").''; print img_picto('', 'bank_account', 'class="pictofixedwidth"'); diff --git a/htdocs/ticket/class/api_tickets.class.php b/htdocs/ticket/class/api_tickets.class.php index 5aa7bd65f7e..c3206e04120 100644 --- a/htdocs/ticket/class/api_tickets.class.php +++ b/htdocs/ticket/class/api_tickets.class.php @@ -402,6 +402,16 @@ class Tickets extends DolibarrApi continue; } + if ($field == 'id') { + continue; + } + if ($field == 'array_options' && is_array($value)) { + foreach ($value as $index => $val) { + $this->ticket->array_options[$index] = $this->_checkValForAPI($field, $val, $this->ticket); + } + continue; + } + $this->ticket->$field = $this->_checkValForAPI($field, $value, $this->ticket); } diff --git a/htdocs/user/class/api_users.class.php b/htdocs/user/class/api_users.class.php index b703b58ab9a..7038d521180 100644 --- a/htdocs/user/class/api_users.class.php +++ b/htdocs/user/class/api_users.class.php @@ -407,6 +407,12 @@ class Users extends DolibarrApi $this->useraccount->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09'); continue; } + if ($field == 'array_options' && is_array($value)) { + foreach ($value as $index => $val) { + $this->useraccount->array_options[$index] = $this->_checkValForAPI($field, $val, $this->useraccount); + } + continue; + } if (DolibarrApiAccess::$user->admin) { // If user for API is admin if ($field == 'admin' && $value != $this->useraccount->admin && empty($value)) { diff --git a/htdocs/variants/class/ProductCombination.class.php b/htdocs/variants/class/ProductCombination.class.php index 27829353182..db82c86747a 100644 --- a/htdocs/variants/class/ProductCombination.class.php +++ b/htdocs/variants/class/ProductCombination.class.php @@ -795,8 +795,8 @@ class ProductCombination $price_impact = $forced_pricevar; } - if (!array($price_var_percent)) { - $price_var_percent[1] = (float) $price_var_percent; + if (!is_array($price_var_percent)) { + $price_var_percent = array(1 => (bool) $price_var_percent); } $newcomb = new ProductCombination($this->db); @@ -998,13 +998,26 @@ class ProductCombination $variations[$tmp_pc2v->fk_prod_attr] = $tmp_pc2v->fk_prod_attr_val; } + $variation_price_percentage = $combination->variation_price_percentage; + $variation_price = $combination->variation_price; + + if (getDolGlobalInt('PRODUIT_MULTIPRICES') && getDolGlobalInt('PRODUIT_MULTIPRICES_LIMIT') > 1) { + $variation_price_percentage = [ ]; + $variation_price = [ ]; + + foreach ($combination->combination_price_levels as $productCombinationLevel) { + $variation_price_percentage[$productCombinationLevel->fk_price_level] = $productCombinationLevel->variation_price_percentage; + $variation_price[$productCombinationLevel->fk_price_level] = $productCombinationLevel->variation_price; + } + } + if ($this->createProductCombination( $user, $destProduct, $variations, array(), - $combination->variation_price_percentage, - $combination->variation_price, + $variation_price_percentage, + $variation_price, $combination->variation_weight ) < 0) { return -1; diff --git a/test/phpunit/ODFTest.php b/test/phpunit/ODFTest.php index 8ddae208e19..8700f0a52a6 100644 --- a/test/phpunit/ODFTest.php +++ b/test/phpunit/ODFTest.php @@ -218,33 +218,45 @@ class ODFTest extends CommonClassTest 'charset' => null, 'expected' => mb_convert_encoding('text with intricatedtags', 'UTF-8', 'ISO-8859-1'), ], + 24 => [ + 'to_convert' => "text with two (strong) tags", + 'encode' => true, + 'charset' => null, + 'expected' => utf8_encode('text with two (strong) tags'), + ], + 25 => [ + 'to_convert' => "text with two (strong) tags and intricated underline ", + 'encode' => true, + 'charset' => null, + 'expected' => utf8_encode('text with two (strong) tags and intricated underline '), + ], // One can also pass html-encoded string to the method - 24 => [ + 26 => [ 'to_convert' => 'One&two', 'encode' => true, 'charset' => null, 'expected' => 'One&two', ], - 25 => [ + 27 => [ 'to_convert' => "text with <strong>strong, </strong><em>emphasis</em> and <u>underlined</u> words with <i>it@lic sp&ciàlchärs éè l'</i>", 'encode' => false, 'charset' => 'UTF-8', 'expected' => 'text with strong, emphasis and underlined words with it@lic sp&ciàlchärs éè l\'', ], - 26 => [ + 28 => [ 'to_convert' => "text with <strong>strong, </strong><em>emphasis</em> and <u>underlined</u> words with <i>it@lic sp&ciàlchärs éè l'</i>", 'encode' => true, 'charset' => 'UTF-8', 'expected' => 'text with strong, emphasis and underlined words with it@lic sp&ciàlchärs éè l'', ], - 27 => [ + 29 => [ 'to_convert' => "text with <strong>strong, </strong><em>emphasis</em> and <u>underlined</u> words with <i>it@lic sp&ciàlchärs éè l'</i>", 'encode' => false, 'charset' => null, 'expected' => mb_convert_encoding('text with strong, emphasis and underlined words with it@lic sp&ciàlchärs éè l\'', 'UTF-8', 'ISO-8859-1'), ], - 28 => [ + 30 => [ 'to_convert' => "text with <strong>strong, </strong><em>emphasis</em> and <u>underlined</u> words with <i>it@lic sp&ciàlchärs éè l'</i>", 'encode' => true, 'charset' => null, @@ -263,20 +275,20 @@ class ODFTest extends CommonClassTest // Following tests reflect the current behavior. They may evolve if the method behavior changes. // The method removes hyperlinks and tags that are not dealt with. - 29 => [ + 31 => [ 'to_convert' => '123 trucmachin > truc < troc > tracbla bla', 'encode' => true, 'charset' => null, 'expected' => "123 trucmachin > truc < troc > tracbla bla", ], - 30 => [ + 32 => [ 'to_convert' => '123

Title

bla', 'encode' => true, 'charset' => null, 'expected' => "123 Title bla", ], // HTML should not take \n into account, but only
. - 31 => [ + 33 => [ 'to_convert' => "text with strong text , a line\nbreak and underlined words with it@lic sp&ciàlchärs éè l'", 'encode' => false, 'charset' => 'UTF-8', @@ -295,7 +307,7 @@ class ODFTest extends CommonClassTest } else { $res = $odf->convertVarToOdf($case['to_convert'], $case['encode']); } - $this->assertEquals($res, $case['expected']); + $this->assertEquals($case['expected'], $res); } print __METHOD__." result=".$result."\n";