Merge branch 'develop' of git@github.com:Dolibarr/dolibarr.git into develop

This commit is contained in:
Laurent Destailleur
2026-03-20 16:41:20 +01:00
7 changed files with 228 additions and 15 deletions

View File

@@ -10835,6 +10835,9 @@ function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null,
$substitutionarray['__AMOUNT_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, '', true) : '';
$substitutionarray['__AMOUNT_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, $conf->currency, true) : '';
$substitutionarray['__DEPOSIT_PERCENT__'] = is_object($object) ? $object->deposit_percent : '';
$substitutionarray['__DEPOSIT_AMOUNT__'] = is_object($object) ? price2num($object->total_ttc * ($object->deposit_percent / 100), 'MT') : '';
$substitutionarray['__AMOUNT_REMAIN__'] = is_object($object) ? price2num($object->total_ttc - $already_payed_all, 'MT') : '';
$substitutionarray['__AMOUNT_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';

View File

@@ -150,6 +150,21 @@ class modHoliday extends DolibarrModules
'status' => 1,
'test' => '$conf->holiday->enabled',
'datestart' => $datestart
),
1 => array(
'label' => 'SendPreviousMonthHRInformations:holiday',
'jobtype' => 'method',
'class' => 'holiday/class/holiday.class.php',
'objectname' => 'Holiday',
'method' => 'sendPreviousMonthHRInformations',
'parameters' => 'emailaddress, EmailTemplateCode',
'comment' => 'Send HR information to the defined email address. EmailTemplateCode can be id or label of emailtemplate to send',
'frequency' => 1,
'unitfrequency' => 3600 * 24 * 31,
'priority' => 50,
'status' => 0,
'test' => '$conf->holiday->enabled',
'datestart' => $datestart
)
);

View File

@@ -86,7 +86,7 @@ class mod_syslog_syslog extends LogHandler
{
global $langs;
$facility = constant(getDolGlobalString('SYSLOG_FACILITY'));
$facility = defined(getDolGlobalString('SYSLOG_FACILITY')) ? constant(getDolGlobalString('SYSLOG_FACILITY')) : null;
if ($facility) {
// Only LOG_USER supported on Windows
@@ -117,8 +117,8 @@ class mod_syslog_syslog extends LogHandler
return; // Global option to disable output of this handler
}
if (getDolGlobalString('SYSLOG_FACILITY')) { // Example LOG_USER
$facility = constant($conf->global->SYSLOG_FACILITY);
if (getDolGlobalString('SYSLOG_FACILITY') && defined(getDolGlobalString('SYSLOG_FACILITY'))) { // Example LOG_USER
$facility = constant(getDolGlobalString('SYSLOG_FACILITY'));
} else {
$facility = constant('LOG_USER');
}

View File

@@ -2624,4 +2624,195 @@ class Holiday extends CommonObject
$return .= '</div>';
return $return;
}
/**
* Send a mail with previous month Hr information
* CAN BE A CRON TASK
*
* @param string $mailto Email address to send to
* @param string $template Id or Label of mail template to use
* @param string $newlang Force a lang or empty for auto
*
* @return int 0 if OK, <> 0 if KO (this function is used also by cron so only 0 is OK)
*/
public function sendPreviousMonthHRInformations($mailto = "", $template = "", $newlang = "")
{
global $conf, $langs, $user;
$db = $this->db;
$outputlangs = $langs;
$error = 0;
$this->output='';
$this->error='';
$arrayfields = array(
'user' => 'Employee',
'type' => 'Type',
'date_start' => 'DateDebCP',
'date_end' => 'DateFinCP',
'used_days' => 'NbUseDaysCPShort',
);
if (!empty($newlang)) {
$outputlangs = new Translate("", $conf);
$outputlangs->setDefaultLang($newlang);
}
$outputlangs->loadLangs(array('main', 'holiday', 'hrm'));
if (empty($mailto) || !isValidEmail($mailto)) {
$this->errors[] = 'Bad value for parameter mailto. Must be a valid email address.';
return 1;
}
$typeleaves = $this->getTypes(1, -1);
$arraytypeleaves = array();
foreach ($typeleaves as $key => $val) {
$labeltoshow = ($outputlangs->trans($val['code']) != $val['code'] ? $outputlangs->trans($val['code']) : $val['label']);
$arraytypeleaves[$val['rowid']] = $labeltoshow;
}
$listhalfday = array('morning' => $outputlangs->trans("Morning"), "afternoon" => $outputlangs->trans("Afternoon"));
$datenow = dol_getdate(dol_now());
$prev_month = dol_get_prev_month($datenow["mon"], $datenow["year"]);
$year_month = sprintf("%04d", $prev_month["year"]).'-'.sprintf("%02d", $prev_month["month"]);
$arrayleaves = array();
$sql = "SELECT cp.rowid, cp.ref, cp.fk_user, cp.date_debut, cp.date_fin, cp.fk_type, cp.description, cp.halfday, cp.statut as status";
$sql .= " FROM ".MAIN_DB_PREFIX."holiday cp";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user u ON cp.fk_user = u.rowid";
$sql .= " WHERE cp.entity IN (".getEntity('holiday').") AND cp.rowid > 0";
$sql .= " AND cp.statut = ".Holiday::STATUS_APPROVED;
$sql .= " AND (";
$sql .= " (date_format(cp.date_debut, '%Y-%m') = '".$this->db->escape($year_month)."' OR date_format(cp.date_fin, '%Y-%m') = '".$this->db->escape($year_month)."')";
$sql .= " OR"; // For leave over several months
$sql .= " (date_format(cp.date_debut, '%Y-%m') < '".$this->db->escape($year_month)."' AND date_format(cp.date_fin, '%Y-%m') > '".$this->db->escape($year_month)."') ";
$sql .= " )";
$sql .= $this->db->order("cp.fk_user, cp.date_debut", "ASC");
$resql = $this->db->query($sql);
if (empty($resql)) {
$this->errors[] = $this->db->lasterror();
return 1;
}
$num = $this->db->num_rows($resql);
if ($num > 0) {
$tmpuser = new User($this->db);
while ($obj = $this->db->fetch_object($resql)) {
$tmpuser->fetch($obj->fk_user);
$date_start = $this->db->jdate($obj->date_debut, true);
$date_end = $this->db->jdate($obj->date_fin, true);
$tmpstart = dol_getdate($date_start);
$tmpend = dol_getdate($date_end);
$starthalfday = ($obj->halfday == -1 || $obj->halfday == 2) ? 'afternoon' : 'morning';
$endhalfday = ($obj->halfday == 1 || $obj->halfday == 2) ? 'morning' : 'afternoon';
$halfdayinmonth = $obj->halfday;
$starthalfdayinmonth = $starthalfday;
$endhalfdayinmonth = $endhalfday;
//0:Full days, 2:Start afternoon end morning, -1:Start afternoon end afternoon, 1:Start morning end morning
// Set date_start_gmt and date_end_gmt that are date to show for the selected month
$date_start_inmonth = $this->db->jdate($obj->date_debut, true);
$date_end_inmonth = $this->db->jdate($obj->date_fin, true);
if ($tmpstart['year'] < $prev_month["year"] || $tmpstart['mon'] < $prev_month["month"]) {
$date_start_inmonth = dol_get_first_day($prev_month["year"], $prev_month["month"], true);
$starthalfdayinmonth = 'morning';
if ($halfdayinmonth == 2) {
$halfdayinmonth = 1;
}
if ($halfdayinmonth == -1) {
$halfdayinmonth = 0;
}
}
if ($tmpend['year'] > $prev_month["year"] || $tmpend['mon'] > $prev_month["month"]) {
$date_end_inmonth = dol_get_last_day($prev_month["year"], $prev_month["month"], true) - ((24 * 3600) - 1);
$endhalfdayinmonth = 'afternoon';
if ($halfdayinmonth == 2) {
$halfdayinmonth = -1;
}
if ($halfdayinmonth == 1) {
$halfdayinmonth = 0;
}
}
$arrayleaves[] = array(
"user" => $tmpuser->getNomUrl(0, 'nolink', 0, 0, 24, 1),
"type" => $arraytypeleaves[$obj->fk_type],
"date_start" => dol_print_date($date_start_inmonth, 'day') . '<span class="opacitymedium">('.$outputlangs->trans($listhalfday[$starthalfdayinmonth]).')</span>',
"date_end" => dol_print_date($date_end_inmonth, 'day') . '<span class="opacitymedium">('.$outputlangs->trans($listhalfday[$endhalfdayinmonth]).')</span>',
"used_days" => num_open_day($date_start_inmonth, $date_end_inmonth, 0, 1, $halfdayinmonth, $tmpuser->country_id)
);
}
}
$outputarrayleaves = '<br><table style="width: 100%;border-collapse: separate !important;border-spacing: 0px;border-top: 1px solid #b6b6b6;border-left: 1px solid #b6b6b6;border-right: 1px solid #b6b6b6;margin: 0px 0px 20px 0px;">';
$outputarrayleaves .= '<tr>';
foreach ($arrayfields as $key => $label) {
$outputarrayleaves .= '<td style="border-bottom:1px solid #b6b6b6;padding: 6px 10px 6px 12px;">';
$outputarrayleaves .= $outputlangs->trans($label);
$outputarrayleaves .= '</td>';
}
$outputarrayleaves .= '</tr>';
if (!empty($arrayleaves)) {
foreach ($arrayleaves as $key => $fields) {
$outputarrayleaves .= '<tr>';
foreach ($fields as $field => $value) {
$outputarrayleaves .= '<td style="border-bottom:1px solid #b6b6b6;padding: 6px 10px 6px 12px;" id="'.$field.'">';
$outputarrayleaves .= $value;
$outputarrayleaves .= '</td>';
}
$outputarrayleaves .= '</tr>';
}
} else {
$outputarrayleaves .= '<tr>';
$outputarrayleaves .= '<td style="border-bottom:1px solid #b6b6b6;padding: 6px 10px 6px 12px;" colspan="5">';
$outputarrayleaves .= $outputlangs->trans("None");
$outputarrayleaves .= '</td>';
$outputarrayleaves .= '</tr>';
}
$outputarrayleaves .= '</table>';
include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
include_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
$formmail = new FormMail($this->db);
$templateId = 0;
$templateLabel = '';
if (empty($template) || $template == 'EmailTemplateCode') {
$templateLabel = '(HolidayHrInformationsPreviousMonth)';
} else {
if (is_numeric($template)) {
$templateId = $template;
} else {
$templateLabel = $template;
}
}
$mailtemplate = $formmail->getEMailTemplate($this->db, "holiday", $user, $outputlangs, $templateId, 1, $templateLabel);
$substitutionarray = getCommonSubstitutionArray($outputlangs, 0, null, $this);
complete_substitutions_array($substitutionarray, $outputlangs, $this);
$subject = make_substitutions($mailtemplate->topic, $substitutionarray, $outputlangs);
$msg = make_substitutions($mailtemplate->content, $substitutionarray, $outputlangs);
$from = dol_string_nospecial(getDolGlobalString('MAIN_INFO_SOCIETE_NOM'), ' ', array(",")).' <' . getDolGlobalString('MAIN_INFO_SOCIETE_MAIL').'>';
$msg = preg_replace('/__ARRAY_EMPLOYEE_STARTDAY_ENDDAY_DAYS__/', $outputarrayleaves, $msg);
$cmail = new CMailFile($subject, $mailto, $from, $msg, array(), array(), array(), '', '', 0, 1);
$result = $cmail->sendfile();
if (!$result || !empty($cmail->error) || !empty($cmail->errors)) {
$this->errors[] = $cmail->error;
if (is_array($cmail->errors) && count($cmail->errors) > 0) {
$this->errors = array_merge($this->errors, $cmail->errors);
$error++;
}
}
if (!empty($this->errors)) {
$this->output .= "\n";
// The $this->errors will be concatenated to the output by the function that call this method.
}
return ($error ? 1 : 0);
}
}

View File

@@ -189,6 +189,7 @@ ALTER TABLE llx_societe_remise_except ADD COLUMN localtax1_type varchar(10) NUL
ALTER TABLE llx_societe_remise_except ADD COLUMN localtax2_tx double(7,4) DEFAULT 0 NOT NULL AFTER localtax1_type;
ALTER TABLE llx_societe_remise_except ADD COLUMN localtax2_type varchar(10) NULL AFTER localtax2_tx;
INSERT INTO llx_c_email_templates (entity, module, type_template, lang, private, fk_user, datec, label, position, enabled, active, topic, content, content_lines, joinfiles) VALUES (0, 'holiday', 'holiday', '', 0, null, null, '(HolidayHrInformationsPreviousMonth)', 100,'isModEnabled("holiday")', 1, '__(HolidayHrInformationsPreviousMonthTopic)__', '__(HolidayHrInformationsPreviousMonthContent)__:<br>__ARRAY_EMPLOYEE_STARTDAY_ENDDAY_DAYS__', null, 0)
INSERT INTO llx_c_email_templates (entity, module, type_template, lang, private, fk_user, datec, label, position, enabled, active, topic, content, content_lines, joinfiles) VALUES (0, 'holiday', 'holiday', '', 0, null, null, '(HolidayHrInformationsPreviousMonth)', 100,'isModEnabled("holiday")', 1, '__(HolidayHrInformationsPreviousMonthTopic)__', '__(HolidayHrInformationsPreviousMonthContent)__:<br>__ARRAY_EMPLOYEE_STARTDAY_ENDDAY_DAYS__', null, 0);
ALTER TABLE llx_c_ticket_category ADD COLUMN fk_ticket_type integer NULL;
-- end of migration

View File

@@ -31,5 +31,6 @@ create table llx_c_ticket_category
force_severity varchar(32) NULL, -- To force the severity if we choosed this category
description varchar(255), -- A long description of ticket
pos integer DEFAULT 0 NOT NULL,
active integer DEFAULT 1
active integer DEFAULT 1,
fk_ticket_type integer NULL
)ENGINE=innodb;

View File

@@ -132,21 +132,25 @@ class Thirdparties extends DolibarrApi
* Get a list of third parties
*
* @since 3.8.0 Initial implementation
* @since 21.0.0 Data pagination
*
* @param string $sortfield S ort field
* @param string $sortfield Sort field
* @param string $sortorder Sort order
* @param int $limit List limit
* @param int $page Page number
* @param int $mode Set to 0 to show all third parties, Set to 1 to show only customers, 2 for prospects, 3 for neither customer or prospect, 4 for suppliers
* @param int $mode Set to 0 to show all third parties, Set to 1 to show only customers, 2 for prospects, 3 for neither customer nor prospect, 4 for suppliers
* @param int $category Use this param to filter the list by category
* @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "((t.nom:like:'TheCompany%') or (t.name_alias:like:'TheCompany%')) and (t.datec:<:'20160101')"
* @param string $properties Restrict the data returned to these properties. Ignored if empty. Comma separated list of properties names
* @param bool $pagination_data If this parameter is set to true the response will include pagination data. Default value is false. Page starts from 0*
* @return array Array of thirdparty objects
* @param bool $pagination_data If this parameter is set to true, the response will include pagination data. The default value is false. Page starts from 0*
* @return array Array of third party objects
* @phan-return Societe[]|array{data:Societe[],pagination:array{total:int,page:int,page_count:int,limit:int}}
* @phpstan-return Societe[]|array{data:Societe[],pagination:array{total:int,page:int,page_count:int,limit:int}}
*
* @throws RestException
* @throws RestException 400
* @throws RestException 403
* @throws RestException 404
* @throws RestException 503
*/
public function index($sortfield = "t.rowid", $sortorder = 'ASC', $limit = 100, $page = 0, $mode = 0, $category = 0, $sqlfilters = '', $properties = '', $pagination_data = false)
{
@@ -176,9 +180,7 @@ class Thirdparties extends DolibarrApi
$sql .= ", ".MAIN_DB_PREFIX."categorie_fournisseur as cc";
}
}
$sql .= ", ".MAIN_DB_PREFIX."c_stcomm as st";
$sql .= " WHERE t.entity IN (".getEntity('societe').")";
$sql .= " AND t.fk_stcomm = st.id";
if ($mode == 1) {
$sql .= " AND t.client IN (1, 3)";
} elseif ($mode == 2) {
@@ -188,7 +190,7 @@ class Thirdparties extends DolibarrApi
} elseif ($mode == 4) {
$sql .= " AND t.fournisseur IN (1)";
}
// Select thirdparties of given category
// Select third parties of a given category
if ($category > 0) {
if (!empty($mode) && $mode != 4) {
$sql .= " AND c.fk_categorie = ".((int) $category)." AND c.fk_soc = t.rowid";
@@ -248,7 +250,7 @@ class Thirdparties extends DolibarrApi
$i++;
}
} else {
throw new RestException(503, 'Error when retrieve thirdparties : '.$this->db->lasterror());
throw new RestException(503, 'Error when retrieve third parties : '.$this->db->lasterror());
}
if (!count($obj_ret)) {
$message = '';
@@ -271,7 +273,7 @@ class Thirdparties extends DolibarrApi
throw new RestException(404, $message);
}
//if $pagination_data is true the response will contain element data with all values and element pagination with pagination data(total,page,limit)
//if $pagination_data is true, the response will contain element data with all values and element pagination with pagination data(total,page,limit)
if ($pagination_data) {
$totalsResult = $this->db->query($sqlTotals);
$total = $this->db->fetch_object($totalsResult)->total;