diff --git a/admin/setup.php b/admin/setup.php index afa4118..5835354 100644 --- a/admin/setup.php +++ b/admin/setup.php @@ -33,18 +33,19 @@ $res = require_once __DIR__.'/../../../main.inc.php'; } if (!$res) { - die('Include of main fails'); +die('Include of main fails'); } require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/pdf.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php'; -// EN: Load email template class with backward compatibility for older Dolibarr versions. +require_once DOL_DOCUMENT_ROOT.'/cron/class/cronjob.class.php'; +// EN: Load email template class with version-aware fallback. -if (floatval(DOL_VERSION) < 23) { - dol_include_once('/timesheetweek/core/class/cemailtemplate.class.php'); +if (version_compare(DOL_VERSION, '23.0.0', '>=') || file_exists(DOL_DOCUMENT_ROOT.'/core/class/cemailtemplate.class.php')) { +require_once DOL_DOCUMENT_ROOT.'/core/class/cemailtemplate.class.php'; } else { - require_once DOL_DOCUMENT_ROOT.'/core/class/cemailtemplate.class.php'; +require_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php'; } require_once DOL_DOCUMENT_ROOT.'/core/lib/doc.lib.php'; @@ -354,30 +355,41 @@ function timesheetweekListDocumentModels(array $directories, Translate $langs, a } if ($action === 'savereminder') { -$reminderEnabledValue = (int) GETPOST('TIMESHEETWEEK_REMINDER_ENABLED', 'int'); -$reminderWeekdayValue = (int) GETPOST('TIMESHEETWEEK_REMINDER_WEEKDAY', 'int'); -$reminderHourValue = trim(GETPOST('TIMESHEETWEEK_REMINDER_HOUR', 'alphanohtml')); -$reminderTemplateValue = (int) GETPOST('TIMESHEETWEEK_REMINDER_EMAIL_TEMPLATE', 'int'); - - $error = 0; - - if ($reminderWeekdayValue < 1 || $reminderWeekdayValue > 7) { - setEventMessages($langs->trans('TimesheetWeekReminderWeekdayInvalid'), null, 'errors'); - $error++; + $reminderEnabledValue = (int) GETPOST('TIMESHEETWEEK_REMINDER_ENABLED', 'int'); + $reminderStartYear = (int) GETPOST('TIMESHEETWEEK_REMINDER_STARTTIMEyear', 'int'); + $reminderStartMonth = (int) GETPOST('TIMESHEETWEEK_REMINDER_STARTTIMEmonth', 'int'); + $reminderStartDay = (int) GETPOST('TIMESHEETWEEK_REMINDER_STARTTIMEday', 'int'); + $reminderStartHour = (int) GETPOST('TIMESHEETWEEK_REMINDER_STARTTIMEhour', 'int'); + $reminderStartMinute = (int) GETPOST('TIMESHEETWEEK_REMINDER_STARTTIMEmin', 'int'); + $reminderTemplateValue = (int) GETPOST('TIMESHEETWEEK_REMINDER_EMAIL_TEMPLATE', 'int'); + $reminderExcludedUsersValue = GETPOST('TIMESHEETWEEK_REMINDER_EXCLUDED_USERS', 'array'); + $reminderExcludedUsersList = array(); + if (is_array($reminderExcludedUsersValue)) { + foreach ($reminderExcludedUsersValue as $reminderExcludedUserId) { + $reminderExcludedUserId = (int) $reminderExcludedUserId; + if ($reminderExcludedUserId > 0) { + $reminderExcludedUsersList[] = $reminderExcludedUserId; + } + } } - - if (!preg_match('/^(?:[01]\\d|2[0-3]):[0-5]\\d$/', $reminderHourValue)) { - setEventMessages($langs->trans('TimesheetWeekReminderHourInvalid'), null, 'errors'); + + + + $error = 0; + + $reminderStartTimestamp = dol_mktime($reminderStartHour, $reminderStartMinute, 0, $reminderStartMonth, $reminderStartDay, $reminderStartYear); + if (empty($reminderStartTimestamp)) { + setEventMessages($langs->trans('TimesheetWeekReminderStartTimeInvalid'), null, 'errors'); $error++; } - + if (!$error) { $results = array(); $results[] = dolibarr_set_const($db, 'TIMESHEETWEEK_REMINDER_ENABLED', ($reminderEnabledValue ? 1 : 0), 'chaine', 0, '', $conf->entity); - $results[] = dolibarr_set_const($db, 'TIMESHEETWEEK_REMINDER_WEEKDAY', $reminderWeekdayValue, 'chaine', 0, '', $conf->entity); - $results[] = dolibarr_set_const($db, 'TIMESHEETWEEK_REMINDER_HOUR', $reminderHourValue, 'chaine', 0, '', $conf->entity); + $results[] = dolibarr_set_const($db, 'TIMESHEETWEEK_REMINDER_STARTTIME', (string) $reminderStartTimestamp, 'chaine', 0, '', $conf->entity); $results[] = dolibarr_set_const($db, 'TIMESHEETWEEK_REMINDER_EMAIL_TEMPLATE', $reminderTemplateValue, 'chaine', 0, '', $conf->entity); - + $results[] = dolibarr_set_const($db, 'TIMESHEETWEEK_REMINDER_EXCLUDED_USERS', implode(',', $reminderExcludedUsersList), 'chaine', 0, '', $conf->entity); + $hasError = false; foreach ($results as $resultValue) { if ($resultValue <= 0) { @@ -385,23 +397,28 @@ function timesheetweekListDocumentModels(array $directories, Translate $langs, a break; } } - + if ($hasError) { setEventMessages($langs->trans('Error'), null, 'errors'); } else { + $cronUpdate = TimesheetweekReminder::updateCronStartTime($db, $reminderStartTimestamp, $user); + if ($cronUpdate < 0) { + setEventMessages($langs->trans('TimesheetWeekReminderCronUpdateFailed'), null, 'errors'); + } elseif ($cronUpdate === 0) { + setEventMessages($langs->trans('TimesheetWeekReminderCronMissing'), null, 'warnings'); + } setEventMessages($langs->trans('SetupSaved'), null, 'mesgs'); } } } - if ($action === 'testreminder') { +if ($action === 'testreminder') { $reminder = new TimesheetweekReminder($db); - $resultTest = $reminder->sendTest($db, $user); //$resultTest = TimesheetweekReminder::sendTest($db, $user); - //var_dump($resultTest); - if ($resultTest == 0) { - setEventMessages($langs->trans('TimesheetWeekReminderTestSuccess'), null, 'mesgs'); + $resultTest = $reminder->sendTest($user); + if ($resultTest >= 0) { + setEventMessages($langs->trans('TimesheetWeekReminderTestSuccess'), null, 'mesgs'); } else { - setEventMessages($langs->trans('TimesheetWeekReminderTestError'), null, 'errors'); + setEventMessages($langs->trans('TimesheetWeekReminderTestError'), null, 'errors'); } } @@ -410,9 +427,43 @@ function timesheetweekListDocumentModels(array $directories, Translate $langs, a $defaultPdf = getDolGlobalString('TIMESHEETWEEK_ADDON_PDF', 'standard_timesheetweek'); $useQuarterDaySelector = getDolGlobalInt('TIMESHEETWEEK_QUARTERDAYFORDAILYCONTRACT', 0); $reminderEnabled = getDolGlobalInt('TIMESHEETWEEK_REMINDER_ENABLED', 0); -$reminderWeekday = getDolGlobalInt('TIMESHEETWEEK_REMINDER_WEEKDAY', 1); -$reminderHour = getDolGlobalString('TIMESHEETWEEK_REMINDER_HOUR', '18:00'); +$reminderStartValue = getDolGlobalString('TIMESHEETWEEK_REMINDER_STARTTIME', ''); +$reminderStartTimestamp = ''; +if ($reminderStartValue !== '') { + if (is_numeric($reminderStartValue)) { + $reminderStartTimestamp = (int) $reminderStartValue; + } else { + $reminderStartTimestamp = dol_stringtotime($reminderStartValue, 0); + } +} +if (empty($reminderStartTimestamp)) { + $reminderStartTimestamp = dol_now(); +} +$reminderCurrentHour = (int) dol_print_date($reminderStartTimestamp, '%H'); +$reminderCurrentMinute = (int) dol_print_date($reminderStartTimestamp, '%M'); +$reminderWeekday = (int) dol_print_date($reminderStartTimestamp, '%w'); +$now = dol_now(); +$currentWeekday = (int) dol_print_date($now, '%w'); +$daysAhead = ($reminderWeekday - $currentWeekday + 7) % 7; +$referenceDate = dol_time_plus_duree($now, $daysAhead, 'd'); +$referenceYear = (int) dol_print_date($referenceDate, '%Y'); +$referenceMonth = (int) dol_print_date($referenceDate, '%m'); +$referenceDay = (int) dol_print_date($referenceDate, '%d'); +$nextReminderTimestamp = dol_mktime($reminderCurrentHour, $reminderCurrentMinute, 0, $referenceMonth, $referenceDay, $referenceYear); +if ($nextReminderTimestamp <= $now) { + $nextReminderTimestamp = dol_time_plus_duree($nextReminderTimestamp, 7, 'd'); +} +if ($nextReminderTimestamp !== $reminderStartTimestamp) { + dolibarr_set_const($db, 'TIMESHEETWEEK_REMINDER_STARTTIME', (string) $nextReminderTimestamp, 'chaine', 0, '', $conf->entity); + TimesheetweekReminder::updateCronStartTime($db, $nextReminderTimestamp, $user); +} +$reminderStartTimestamp = $nextReminderTimestamp; $reminderTemplateId = getDolGlobalInt('TIMESHEETWEEK_REMINDER_EMAIL_TEMPLATE', 0); +$reminderExcludedUsersString = getDolGlobalString('TIMESHEETWEEK_REMINDER_EXCLUDED_USERS', ''); +$reminderExcludedUsers = array(); +if ($reminderExcludedUsersString !== '') { + $reminderExcludedUsers = array_filter(array_map('intval', explode(',', $reminderExcludedUsersString))); +} $directories = array_merge(array('/'), (array) $conf->modules_parts['models']); // EN: Prepare a lightweight object to test numbering module activation. @@ -582,32 +633,44 @@ function timesheetweekListDocumentModels(array $directories, Translate $langs, a print ''; print ''; -$weekdayOptions = array( -1 => $langs->trans('Monday'), -2 => $langs->trans('Tuesday'), -3 => $langs->trans('Wednesday'), -4 => $langs->trans('Thursday'), -5 => $langs->trans('Friday'), -6 => $langs->trans('Saturday'), -7 => $langs->trans('Sunday'), -); +$selectStartTime = $form->select_date($reminderStartTimestamp, 'TIMESHEETWEEK_REMINDER_STARTTIME', 1, 1, 0, '', 1, 0, 1, '', 0, 0, '', '', '', ''); print ''; -print ''.$langs->trans('TimesheetWeekReminderWeekday').''; -print ''.$langs->trans('TimesheetWeekReminderWeekdayHelp').''; -print ''.$form->selectarray('TIMESHEETWEEK_REMINDER_WEEKDAY', $weekdayOptions, $reminderWeekday, 0, 0, 0, '', 0, 0, 0, '', '', 1).''; +print ''.$langs->trans('TimesheetWeekReminderStartTime').''; +print ''.$langs->trans('TimesheetWeekReminderStartTimeHelp').''; +print ''.$selectStartTime.''; print ''; print ''; -print ''.$langs->trans('TimesheetWeekReminderHour').''; -print ''.$langs->trans('TimesheetWeekReminderHourHelp').''; -print ''; +print ''.$langs->trans('TimesheetWeekReminderEmailTemplate').''; +print ''.$langs->trans('TimesheetWeekReminderEmailTemplateHelp').''; +print ''.$form->selectarray('TIMESHEETWEEK_REMINDER_EMAIL_TEMPLATE', $templateOptions, $reminderTemplateId, 0, 0, 0, '', 0, 0, 0, '', '', $conf->entity).''; print ''; +$selectExcludedUsers = $form->select_dolusers( +$reminderExcludedUsers, +'TIMESHEETWEEK_REMINDER_EXCLUDED_USERS', +0, +'', +0, +'', +'', +0, +0, +0, +'', +0, +'', +'minwidth300', +0, +0, +1 +); + print ''; -print ''.$langs->trans('TimesheetWeekReminderEmailTemplate').''; -print ''.$langs->trans('TimesheetWeekReminderEmailTemplateHelp').''; -print ''.$form->selectarray('TIMESHEETWEEK_REMINDER_EMAIL_TEMPLATE', $templateOptions, $reminderTemplateId, 0, 0, 0, '', 0, 0, 0, '', '', 1).''; +print ''.$langs->trans('TimesheetWeekReminderExcludedUsers').''; +print ''.$langs->trans('TimesheetWeekReminderExcludedUsersHelp').''; +print ''.$selectExcludedUsers.''; print ''; print ''; diff --git a/class/timesheetweek_reminder.class.php b/class/timesheetweek_reminder.class.php index 9c9d47e..7f3e984 100644 --- a/class/timesheetweek_reminder.class.php +++ b/class/timesheetweek_reminder.class.php @@ -37,8 +37,10 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/functions.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php'; require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php'; +require_once DOL_DOCUMENT_ROOT.'/cron/class/cronjob.class.php'; dol_include_once('/timesheetweek/class/timesheetweek.class.php'); @@ -49,43 +51,47 @@ class TimesheetweekReminder extends CommonObject { public $db; - public $error; - public $errors = array(); - public $output; - - public function __construct(DoliDB $db) - { - $this->db = $db; - } + public $error; + public $errors = array(); + public $output; + + public function __construct(DoliDB $db) + { + $this->db = $db; + } /** - * Run cron job to send weekly reminder emails. - * - * @param DoliDB $db Database handler - * @param int $limit Optional limit for recipients - * @param int $forcerun Force execution (1) or use normal scheduling (0) - * @param array $targetUserIds Limit execution to specific user ids when provided - * @return int <0 if KO, >=0 if OK (number of emails sent) - */ - public function run($dbInstance = null, $limit = 0, $forcerun = 0, array $targetUserIds = array()) //$dbInstance = null, + * Run cron job to send weekly reminder emails. + * + * @param DoliDB|null $dbInstance Optional database handler override + * @param int $limit Optional limit for recipients + * @param int $forcerun Force execution (1) or use normal scheduling (0) + * @param array $targetUserIds Limit execution to specific user ids when provided + * @return int <0 if KO, >=0 if OK (number of emails sent) + */ + public function run($dbInstance = null, $limit = 0, $forcerun = 0, array $targetUserIds = array()) { global $db, $conf, $user, $langs; - /* - $db = $dbInstance; - if (empty($db) && !empty($GLOBALS['db'])) { - $db = $GLOBALS['db']; - } - */ - if (empty($db)) { - dol_syslog($langs->transnoentitiesnoconv('ErrorNoDatabase'), LOG_ERR); - return -1; + if ($dbInstance instanceof DoliDB) { + $this->db = $dbInstance; + } elseif (!empty($db) && $db instanceof DoliDB) { + $this->db = $db; } - + + if (empty($this->db)) { + $this->error = $langs->trans('ErrorNoDatabase'); + $this->output = $this->error; + dol_syslog($this->error, LOG_ERR); + return 0; + } + + $db = $this->db; + $langs->loadLangs(array('timesheetweek@timesheetweek')); $forceExecution = !empty($forcerun); if (!$forceExecution) { - $forceExecution = ((int) GETPOST('forcerun', 'int') > 0); + $forceExecution = ((int) GETPOST('forcerun', 'int') > 0); } if (!$forceExecution) { $action = GETPOST('action', 'aZ09'); @@ -95,88 +101,71 @@ public function run($dbInstance = null, $limit = 0, $forcerun = 0, array $target } } - if (floatval(DOL_VERSION) < 23) { - dol_include_once('/timesheetweek/core/class/cemailtemplate.class.php'); - } else { + $useCoreTemplateClass = (version_compare(DOL_VERSION, '23.0.0', '>=') || file_exists(DOL_DOCUMENT_ROOT.'/core/class/cemailtemplate.class.php')) ? true : false; + if ($useCoreTemplateClass) { require_once DOL_DOCUMENT_ROOT.'/core/class/cemailtemplate.class.php'; + dol_syslog(__METHOD__.' use core CEmailTemplate', LOG_DEBUG); + } else { + require_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php'; + dol_syslog(__METHOD__.' use ModelMail', LOG_DEBUG); } - if (!class_exists('CEmailTemplate') && !class_exists('EmailTemplate')) { - dol_syslog($langs->trans('ErrorFailedToLoadEmailTemplateClass'), LOG_ERR); - return -1; - } - - dol_syslog(__METHOD__, LOG_DEBUG); - $reminderEnabled = getDolGlobalInt('TIMESHEETWEEK_REMINDER_ENABLED', 0, $conf->entity); if (empty($reminderEnabled) && empty($forceExecution)) { dol_syslog('TimesheetweekReminder: reminder disabled', LOG_INFO); + $this->output = $langs->trans('TimesheetWeekReminderDisabled'); return 0; } - $reminderWeekday = getDolGlobalInt('TIMESHEETWEEK_REMINDER_WEEKDAY', 1, $conf->entity); - if ($reminderWeekday < 1 || $reminderWeekday > 7) { - dol_syslog($langs->trans('TimesheetWeekReminderWeekdayInvalid'), LOG_ERR); - return -1; + $reminderStartValue = getDolGlobalString('TIMESHEETWEEK_REMINDER_STARTTIME', '', $conf->entity); + $reminderStartTimestamp = 0; + if ($reminderStartValue !== '') { + if (is_numeric($reminderStartValue)) { + $reminderStartTimestamp = (int) $reminderStartValue; + } else { + $reminderStartTimestamp = dol_stringtotime($reminderStartValue, 0); + } } - - $reminderHour = getDolGlobalString('TIMESHEETWEEK_REMINDER_HOUR', '18:00', $conf->entity); - if (!preg_match('/^(?:[01]\\d|2[0-3]):[0-5]\\d$/', $reminderHour)) { - dol_syslog($langs->trans('TimesheetWeekReminderHourInvalid'), LOG_ERR); - return -1; + if (empty($reminderStartTimestamp)) { + $this->error = $langs->trans('TimesheetWeekReminderStartTimeInvalid'); + $this->output = $this->error; + dol_syslog($this->error, LOG_ERR); + return 0; } $templateId = getDolGlobalInt('TIMESHEETWEEK_REMINDER_EMAIL_TEMPLATE', 0, $conf->entity); if (empty($templateId)) { - dol_syslog($langs->trans('TimesheetWeekReminderTemplateMissing'), LOG_WARNING); + $this->error = $langs->trans('TimesheetWeekReminderTemplateMissing'); + $this->output = $this->error; + dol_syslog($this->error, LOG_WARNING); return 0; } - $timezoneCode = !empty($conf->timezone) ? $conf->timezone : 'UTC'; - $now = dol_now(); - $nowArray = dol_getdate($now, true, $timezoneCode); - $currentWeekday = (int) $nowArray['wday']; - $currentWeekdayIso = ($currentWeekday === 0) ? 7 : $currentWeekday; - $currentMinutes = ((int) $nowArray['hours'] * 60) + (int) $nowArray['minutes']; - - list($targetHour, $targetMinute) = explode(':', $reminderHour); - $targetMinutes = ((int) $targetHour * 60) + (int) $targetMinute; - $windowMinutes = 5; - $lowerBound = max(0, $targetMinutes - $windowMinutes); - $upperBound = min(120, $targetMinutes + $windowMinutes); - - if (empty($forceExecution)) { - if ($currentWeekdayIso !== $reminderWeekday) { - dol_syslog('TimesheetweekReminder: not the configured day, skipping execution', LOG_DEBUG); - return 0; - } - if ($currentMinutes < $lowerBound || $currentMinutes > $upperBound) { - dol_syslog('TimesheetweekReminder: outside configured time window, skipping execution', LOG_DEBUG); - return 0; - } - } - $emailTemplate = null; $templateFetch = 0; - if (class_exists('CEmailTemplate')) { + if ($useCoreTemplateClass && class_exists('CEmailTemplate')) { $emailTemplate = new CEmailTemplate($db); if (method_exists($emailTemplate, 'apifetch')) { $templateFetch = $emailTemplate->apifetch($templateId); } else { $templateFetch = $emailTemplate->fetch($templateId); } - } elseif (class_exists('EmailTemplate')) { - $emailTemplate = new EmailTemplate($db); - $templateFetch = $emailTemplate->fetch($templateId); - } - - if (empty($emailTemplate)) { - dol_syslog($langs->trans('TimesheetWeekReminderTemplateMissing'), LOG_ERR); - return -1; + if ($templateFetch > 0) { + dol_syslog(__METHOD__.' template fetched via CEmailTemplate id='.$templateId, LOG_DEBUG); + } + } elseif (!$useCoreTemplateClass && class_exists('FormMail')) { + $formMail = new FormMail($db); + $emailTemplate = $formMail->getEMailTemplate($db, 'actioncomm_send', $user, $langs, $templateId, 1); + if ($emailTemplate instanceof ModelMail && $emailTemplate->id > 0) { + $templateFetch = 1; + dol_syslog(__METHOD__.' template fetched via ModelMail id='.$templateId, LOG_DEBUG); + } } - if ($templateFetch <= 0) { - dol_syslog($langs->trans('TimesheetWeekReminderTemplateMissing'), LOG_WARNING); + if (empty($emailTemplate) || $templateFetch <= 0) { + $this->error = $langs->trans('TimesheetWeekReminderTemplateMissing'); + $this->output = $this->error; + dol_syslog($this->error, LOG_ERR); return 0; } @@ -184,11 +173,16 @@ public function run($dbInstance = null, $limit = 0, $forcerun = 0, array $target $body = !empty($emailTemplate->content) ? $emailTemplate->content : ''; if (empty($subject) || empty($body)) { - dol_syslog($langs->trans('TimesheetWeekReminderTemplateMissing'), LOG_WARNING); + $this->error = $langs->trans('TimesheetWeekReminderTemplateMissing'); + $this->output = $this->error; + dol_syslog($this->error, LOG_WARNING); return 0; } - $from = getDolGlobalString('MAIN_MAIL_EMAIL_FROM', ''); + $from = (!empty($emailTemplate->email_from) ? $emailTemplate->email_from : ''); + if (empty($from)) { + $from = getDolGlobalString('MAIN_MAIL_EMAIL_FROM', ''); + } if (empty($from)) { $from = getDolGlobalString('MAIN_INFO_SOCIETE_MAIL', ''); } @@ -196,28 +190,27 @@ public function run($dbInstance = null, $limit = 0, $forcerun = 0, array $target $substitutions = getCommonSubstitutionArray($langs, 0, null, null, null); complete_substitutions_array($substitutions, $langs, null); - $eligibleRights = array( - 45000301, // read own - 45000302, // read child - 45000303, // read all - 45000304, // write own - 45000305, // write child - 45000306, // write all - 45000310, // validate generic - 45000311, // validate own - 45000312, // validate child - 45000313, // validate all - 45000314, // seal - 45000315, // unseal - ); - - $entityFilter = getEntity('user'); + $excludedUsersString = getDolGlobalString('TIMESHEETWEEK_REMINDER_EXCLUDED_USERS', '', $conf->entity); + $excludedUsers = array(); + if ($excludedUsersString !== '') { + $excludedUsers = array_filter(array_map('intval', explode(',', $excludedUsersString))); + } + $sql = 'SELECT DISTINCT u.rowid, u.lastname, u.firstname, u.email'; $sql .= ' FROM '.MAIN_DB_PREFIX."user AS u"; - $sql .= ' INNER JOIN '.MAIN_DB_PREFIX."user_rights AS ur ON ur.fk_user = u.rowid AND ur.entity IN (".$entityFilter.')'; + if (isModEnabled('multicompany') && getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE')) { + $sql .= " INNER JOIN ".MAIN_DB_PREFIX."usergroup_user as ug"; + $sql .= " ON ((ug.fk_user = u.rowid"; + $sql .= " AND ug.entity IN (".getEntity('usergroup')."))"; + $sql .= " OR u.entity = 0)"; + } $sql .= " WHERE u.statut = 1 AND u.email IS NOT NULL AND u.email <> ''"; - $sql .= ' AND u.entity IN ('.$entityFilter.')'; - $sql .= ' AND ur.fk_id IN ('.implode(',', array_map('intval', $eligibleRights)).')'; + if (isModEnabled('multicompany') && !getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE')) { + $sql .= ' AND u.entity IN ('.getEntity("user").')'; + } + if (!empty($excludedUsers)) { + $sql .= ' AND u.rowid NOT IN ('.implode(',', $excludedUsers).')'; + } if (!empty($targetUserIds)) { $sql .= ' AND u.rowid IN ('.implode(',', array_map('intval', $targetUserIds)).')'; } @@ -228,8 +221,10 @@ public function run($dbInstance = null, $limit = 0, $forcerun = 0, array $target $resql = $db->query($sql); if (!$resql) { - dol_syslog($db->lasterror(), LOG_ERR); - return -1; + $this->error = $db->lasterror(); + $this->output = $langs->trans('TimesheetWeekReminderSendFailed', $this->error); + dol_syslog($this->error, LOG_ERR); + return 0; } $emailsSent = 0; @@ -277,32 +272,72 @@ public function run($dbInstance = null, $limit = 0, $forcerun = 0, array $target $db->free($resql); -/* if ($errors > 0) { + if ($errors > 0) { + $this->error = $langs->trans('TimesheetWeekReminderSendFailed', $errors); + $this->output = $this->error; + dol_syslog(__METHOD__.' errors='.$errors, LOG_ERR); return -$errors; - }*/ + } -// return $emailsSent; - - if ($errors) { - $this->error = $langs->trans('TimesheetWeekReminderSendFailed', $errors); - dol_syslog(__METHOD__." end - ".$this->error, LOG_ERR); - return 1; - }else{ - $this->output = $langs->trans('TimesheetWeekReminderSendSuccess', $emailsSent); - dol_syslog(__METHOD__." end - ".$this->output, LOG_INFO); - return 0; - } - } + $this->output = $langs->trans('TimesheetWeekReminderSendSuccess', $emailsSent); + dol_syslog(__METHOD__.' sent='.$emailsSent, LOG_DEBUG); - /** - * Send a reminder test email to the current user using the configured template. - * - * @param DoliDB $db Database handler - * @param User $user Current user - * @return int <0 if KO, >=0 if OK (number of emails sent) - */ - public function sendTest($db, User $user) - { - return self::run($db, 1, 1, array((int) $user->id)); + return $emailsSent; + } + /** + * Send a reminder test email to the current user using the configured template. + * + * @param User $user Current user + * @return int <0 if KO, >=0 if OK (number of emails sent) + */ + public function sendTest(User $user) + { + return $this->run($this->db, 1, 1, array((int) $user->id)); + } + + /** + * Update the cron job start date using the provided timestamp. + * + * @param DoliDB $db Database handler + * @param int $startTimestamp Next launch timestamp + * @param User|null $currentUser User performing the update + * @return int >0 if updated, 0 if cron missing, <0 on error + */ + public static function updateCronStartTime(DoliDB $db, int $startTimestamp, $currentUser = null) + { + global $user; + + $cronJob = new Cronjob($db); + $fetchResult = $cronJob->fetch(0, 'TimesheetweekReminder', 'run'); + if ($fetchResult <= 0 || empty($cronJob->id)) { + return 0; + } + + $cronJob->datestart = $startTimestamp; + $cronJob->datenextrun = $startTimestamp; + + $userForUpdate = $currentUser; + if (empty($userForUpdate) && !empty($user) && $user instanceof User) { + $userForUpdate = $user; + } + + return $cronJob->update($userForUpdate); + } + + /** + * Store the next reminder start date based on the provided timestamp. + * + * @param int $currentStartTimestamp Current start timestamp + * @return int Next start timestamp + */ + private function scheduleNextStart($currentStartTimestamp) + { + global $db, $conf; + + $nextStartTimestamp = dol_time_plus_duree($currentStartTimestamp, 1, 'w'); + dolibarr_set_const($db, 'TIMESHEETWEEK_REMINDER_STARTTIME', (string) $nextStartTimestamp, 'chaine', 0, '', $conf->entity); + self::updateCronStartTime($db, $nextStartTimestamp); + + return $nextStartTimestamp; + } } -} diff --git a/core/modules/timesheetweek/mod_timesheetweek_advanced.php b/core/modules/timesheetweek/mod_timesheetweek_advanced.php index dd0deaa..8af2fe7 100644 --- a/core/modules/timesheetweek/mod_timesheetweek_advanced.php +++ b/core/modules/timesheetweek/mod_timesheetweek_advanced.php @@ -146,7 +146,7 @@ public function getNextValue($object) $date = $object->date_creation; - $numFinal = get_next_value($db, $mask, 'timesheetweek_timesheetweek', 'ref', '', '', $date); + $numFinal = get_next_value($db, $mask, 'timesheet_week', 'ref', '', '', $date); return $numFinal; } diff --git a/core/modules/timesheetweek/mod_timesheetweek_standard.php b/core/modules/timesheetweek/mod_timesheetweek_standard.php index 04e9bfb..baaf0a7 100644 --- a/core/modules/timesheetweek/mod_timesheetweek_standard.php +++ b/core/modules/timesheetweek/mod_timesheetweek_standard.php @@ -93,7 +93,7 @@ public function canBeActivated($object) $posindice = strlen($this->prefix) + 6; $sql = "SELECT MAX(CAST(SUBSTRING(ref FROM ".$posindice.") AS SIGNED)) as max"; - $sql .= " FROM ".$db->prefix()."timesheetweek_timesheetweek"; + $sql .= " FROM ".$db->prefix()."timesheet_week"; $sql .= " WHERE ref LIKE '".$db->escape($this->prefix)."____-%'"; $resql = $db->query($sql); @@ -126,7 +126,7 @@ public function getNextValue($object) // first we get the max value $posindice = strlen($this->prefix) + 6; $sql = "SELECT MAX(CAST(SUBSTRING(ref FROM ".$posindice.") AS SIGNED)) as max"; - $sql .= " FROM ".$db->prefix()."timesheetweek_timesheetweek"; + $sql .= " FROM ".$db->prefix()."timesheet_week"; $sql .= " WHERE ref LIKE '".$db->escape($this->prefix)."____-%'"; $resql = $db->query($sql); diff --git a/langs/en_US/timesheetweek.lang b/langs/en_US/timesheetweek.lang index 7c0ea4b..90c399f 100644 --- a/langs/en_US/timesheetweek.lang +++ b/langs/en_US/timesheetweek.lang @@ -31,14 +31,15 @@ TimesheetWeekReminderSectionTitle = Weekly email reminder TimesheetWeekReminderSectionHelp = Configure the automatic reminder asking users to fill, validate or send their weekly timesheets. TimesheetWeekReminderEnabled = Enable automatic reminder TimesheetWeekReminderEnabledHelp = Turn the weekly reminder on or off. -TimesheetWeekReminderWeekday = Reminder day of week -TimesheetWeekReminderWeekdayHelp = Select which day of the week the reminder should be sent. -TimesheetWeekReminderHour = Reminder time (HH:MM) -TimesheetWeekReminderHourHelp = Time of day when the reminder should be sent. -TimesheetWeekReminderHourInvalid = Please enter a valid time formatted as HH:MM. -TimesheetWeekReminderWeekdayInvalid = Please choose a valid day of the week for the reminder. +TimesheetWeekReminderStartTime = Reminder start date and time +TimesheetWeekReminderStartTimeHelp = Choose the exact date and time when the next reminder must start running. +TimesheetWeekReminderStartTimeInvalid = Please select a valid date and time for the reminder start. +TimesheetWeekReminderCronUpdateFailed = Unable to update the cron start date for the reminder task. +TimesheetWeekReminderCronMissing = Reminder cron task not found; please reactivate the module to recreate it. TimesheetWeekReminderEmailTemplate = Email template for reminder TimesheetWeekReminderEmailTemplateHelp = Choose the Dolibarr email template used to send the reminder. +TimesheetWeekReminderExcludedUsers = Users excluded from the reminder +TimesheetWeekReminderExcludedUsersHelp = Select users who must not receive the weekly reminder email. TimesheetWeekReminderCronLabel = Weekly timesheet reminder TimesheetWeekReminderCronComment = Send weekly reminder emails prompting users to complete their timesheets. TimesheetWeekReminderTemplateMissing = Reminder email template is not configured. diff --git a/langs/fr_FR/timesheetweek.lang b/langs/fr_FR/timesheetweek.lang index d5aa9f3..ed38a7d 100644 --- a/langs/fr_FR/timesheetweek.lang +++ b/langs/fr_FR/timesheetweek.lang @@ -31,14 +31,15 @@ TimesheetWeekReminderSectionTitle = Rappel hebdomadaire par email TimesheetWeekReminderSectionHelp = Configure le rappel automatique invitant les utilisateurs à saisir, valider ou envoyer leurs feuilles hebdomadaires. TimesheetWeekReminderEnabled = Activer le rappel automatique TimesheetWeekReminderEnabledHelp = Active ou désactive l'envoi hebdomadaire du rappel. -TimesheetWeekReminderWeekday = Jour d'envoi du rappel -TimesheetWeekReminderWeekdayHelp = Choisissez le jour de la semaine pour envoyer le rappel. -TimesheetWeekReminderHour = Heure d'envoi (HH:MM) -TimesheetWeekReminderHourHelp = Heure d'envoi du rappel, au format 24 h. -TimesheetWeekReminderHourInvalid = Merci de saisir une heure valide au format HH:MM. -TimesheetWeekReminderWeekdayInvalid = Merci de choisir un jour de semaine valide pour le rappel. +TimesheetWeekReminderStartTime = Date et heure de départ du rappel +TimesheetWeekReminderStartTimeHelp = Choisissez la date et l'heure exactes du prochain lancement du rappel. +TimesheetWeekReminderStartTimeInvalid = Merci de sélectionner une date et une heure de départ valides pour le rappel. +TimesheetWeekReminderCronUpdateFailed = Impossible de mettre à jour la date de départ de la tâche CRON du rappel. +TimesheetWeekReminderCronMissing = Tâche CRON de rappel introuvable ; merci de réactiver le module pour la recréer. TimesheetWeekReminderEmailTemplate = Modèle d'email pour le rappel TimesheetWeekReminderEmailTemplateHelp = Sélectionnez le modèle d'email Dolibarr utilisé pour envoyer le rappel. +TimesheetWeekReminderExcludedUsers = Utilisateurs exclus du rappel +TimesheetWeekReminderExcludedUsersHelp = Sélectionnez les utilisateurs qui ne doivent pas recevoir le rappel hebdomadaire. TimesheetWeekReminderCronLabel = Rappel hebdomadaire des feuilles d'heures TimesheetWeekReminderCronComment = Envoie un rappel hebdomadaire par email pour inviter les utilisateurs à compléter leurs feuilles d'heures. TimesheetWeekReminderTemplateMissing = Le modèle d'email de rappel n'est pas configuré.