Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# CHANGELOG MODULE TIMESHEETWEEK FOR [DOLIBARR ERP CRM](https://www.dolibarr.org)

## 1.4.0
- Ajoute un interrupteur d'administration pour activer le sélecteur quart de jour des salariés au forfait jour. / Adds an admin switch to enable the quarter-day selector for daily-rate employees.
- Affiche les durées forfait jour (0.25, 0.5 et 1 jour) dans les PDF standard et de synthèse lorsque le quart de jour est actif. / Displays the daily-rate durations (0.25, 0.5 and 1 day) inside the standard and summary PDFs whenever quarter-day mode is enabled.

## 1.3.0
- Active la génération de PDF depuis la fiche hebdomadaire avec le widget Documents Dolibarr et respecte les modèles configurés dans la page de setup. / Enables PDF generation from the weekly sheet using Dolibarr's Documents widget and honours the templates configured in the setup page.
- Introduit le modèle PDF « standard_timesheetweek » basé sur la synthèse partagée afin de produire les fichiers dans le répertoire documentaire Dolibarr. / Introduces the "standard_timesheetweek" PDF model powered by the shared summary engine so files land into Dolibarr's document directory.
Expand Down
76 changes: 63 additions & 13 deletions admin/setup.php
Original file line number Diff line number Diff line change
Expand Up @@ -276,12 +276,12 @@ function timesheetweekListDocumentModels(array $directories, Translate $langs, a

// EN: Verify CSRF token when the request changes the configuration.
// FR: Vérifie le jeton CSRF lorsque la requête modifie la configuration.
if (in_array($action, array('setmodule', 'setdoc', 'setdocmodel', 'delmodel'), true)) {
if (function_exists('dol_verify_token')) {
if (empty($token) || dol_verify_token($token) <= 0) {
accessforbidden();
}
if (in_array($action, array('setmodule', 'setdoc', 'setdocmodel', 'delmodel', 'setquarterday'), true)) {
if (function_exists('dol_verify_token')) {
if (empty($token) || dol_verify_token($token) <= 0) {
accessforbidden();
}
}
}

// EN: Persist the chosen numbering module.
Expand Down Expand Up @@ -323,21 +323,37 @@ function timesheetweekListDocumentModels(array $directories, Translate $langs, a
// EN: Disable a PDF model and remove the default flag if needed.
// FR: Désactive un modèle PDF et supprime le statut par défaut si nécessaire.
if ($action === 'delmodel' && !empty($value)) {
$res = timesheetweekDisableDocumentModel($value);
if ($res > 0) {
if ($value === getDolGlobalString('TIMESHEETWEEK_ADDON_PDF')) {
dolibarr_del_const($db, 'TIMESHEETWEEK_ADDON_PDF', $conf->entity);
}
setEventMessages($langs->trans('ModelDisabled', $value), null, 'mesgs');
} else {
setEventMessages($langs->trans('Error'), null, 'errors');
$res = timesheetweekDisableDocumentModel($value);
if ($res > 0) {
if ($value === getDolGlobalString('TIMESHEETWEEK_ADDON_PDF')) {
dolibarr_del_const($db, 'TIMESHEETWEEK_ADDON_PDF', $conf->entity);
}
setEventMessages($langs->trans('ModelDisabled', $value), null, 'mesgs');
} else {
setEventMessages($langs->trans('Error'), null, 'errors');
}
}

// EN: Enable or disable the quarter-day selector for daily rate contracts.
// FR: Active ou désactive le sélecteur quart de jour pour les contrats au forfait jour.
if ($action === 'setquarterday') {
$targetValue = (int) GETPOST('value', 'int');
if ($targetValue !== 0) {
$targetValue = 1;
}
$res = dolibarr_set_const($db, 'TIMESHEETWEEK_QUARTERDAYFORDAILYCONTRACT', $targetValue, 'chaine', 0, '', $conf->entity);
if ($res > 0) {
setEventMessages($langs->trans('SetupSaved'), null, 'mesgs');
} else {
setEventMessages($langs->trans('Error'), null, 'errors');
}
}

// EN: Read the selected options so we can highlight them in the UI.
// FR: Lit les options sélectionnées pour les mettre en avant dans l'interface.
$selectedNumbering = getDolGlobalString('TIMESHEETWEEK_ADDON', 'mod_timesheetweek_standard');
$defaultPdf = getDolGlobalString('TIMESHEETWEEK_ADDON_PDF', 'standard_timesheetweek');
$useQuarterDaySelector = getDolGlobalInt('TIMESHEETWEEK_QUARTERDAYFORDAILYCONTRACT', 0);
$directories = array_merge(array('/'), (array) $conf->modules_parts['models']);

// EN: Prepare a lightweight object to test numbering module activation.
Expand Down Expand Up @@ -438,6 +454,40 @@ function timesheetweekListDocumentModels(array $directories, Translate $langs, a

print '<br>';

// EN: Display the helper switches dedicated to the daily-rate contract workflows.
// FR: Affiche les commutateurs dédiés aux workflows des contrats au forfait jour.
print load_fiche_titre($langs->trans('TimesheetWeekDailyRateOptions'), '', 'setup');
print '<div class="underbanner opacitymedium">'.$langs->trans('TimesheetWeekDailyRateOptionsHelp').'</div>';

print '<div class="div-table-responsive-no-min">';
print '<table class="noborder centpercent">';
print '<tr class="liste_titre">';
print '<th>'.$langs->trans('Name').'</th>';
print '<th>'.$langs->trans('Description').'</th>';
print '<th class="center">'.$langs->trans('Status').'</th>';
print '</tr>';

// EN: Render the switch dedicated to the quarter-day declaration helper.
// FR: Affiche le commutateur dédié à l'aide de déclaration des quarts de jour.
print '<tr class="oddeven">';
print '<td class="nowraponall">'.$langs->trans('TimesheetWeekQuarterDayForDailyContract').'</td>';
print '<td class="small">'.$langs->trans('TimesheetWeekQuarterDayForDailyContractHelp').'</td>';
print '<td class="center">';
if (!empty($useQuarterDaySelector)) {
$url = $_SERVER['PHP_SELF'].'?action=setquarterday&value=0&token='.$pageToken;
print '<a class="reposition" href="'.dol_escape_htmltag($url).'">'.img_picto($langs->trans('Disable'), 'switch_on').'</a>';
} else {
$url = $_SERVER['PHP_SELF'].'?action=setquarterday&value=1&token='.$pageToken;
print '<a class="reposition" href="'.dol_escape_htmltag($url).'">'.img_picto($langs->trans('Activate'), 'switch_off').'</a>';
}
print '</td>';
print '</tr>';

print '</table>';
print '</div>';

print '<br>';

print load_fiche_titre($langs->trans('TimesheetWeekPDFModels'), '', 'pdf');
print '<div class="underbanner opacitymedium">'.$langs->trans('TimesheetWeekPDFModelsHelp').'</div>';

Expand Down
2 changes: 1 addition & 1 deletion core/modules/modTimesheetWeek.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ public function __construct($db)
}

// Possible values for version are: 'development', 'experimental', 'dolibarr', 'dolibarr_deprecated', 'experimental_deprecated' or a version string like 'x.y.z'
$this->version = '1.3.0'; // EN: Enables PDF generation with document model management like native Dolibarr cards.
$this->version = '1.4.0'; // EN: Enables PDF generation with document model management like native Dolibarr cards.
// FR: Active la génération PDF avec gestion des modèles de documents comme sur les fiches Dolibarr natives.
// Url to the file with your last numberversion of this module
//$this->url_last_version = 'http://www.example.com/versionmodule.txt';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,10 @@ public function write_file($object, $outputlangs, $srctemplatepath = '', $hidede
return -1;
}

// EN: Remember whether the quarter-day selector is enabled to adapt PDF hints and mappings.
// FR: Retient si le sélecteur quart de jour est actif afin d'adapter les aides et correspondances PDF.
$useQuarterDayDailyContract = (bool) getDolGlobalInt('TIMESHEETWEEK_QUARTERDAYFORDAILYCONTRACT', 0);

// EN: Collect employee details to reproduce the on-screen grid behaviour.
// FR: Récupère les informations salarié pour reproduire le comportement de la grille à l'écran.
$timesheetEmployee = null;
Expand Down Expand Up @@ -503,21 +507,45 @@ public function write_file($object, $outputlangs, $srctemplatepath = '', $hidede
$dailyRateOptions = array();
if ($isDailyRateEmployee) {
$dailyRateOptions = array(
1 => $outputlangs->trans('TimesheetWeekDailyRateFullDay'),
2 => $outputlangs->trans('TimesheetWeekDailyRateMorning'),
3 => $outputlangs->trans('TimesheetWeekDailyRateAfternoon')
1 => $outputlangs->trans('TimesheetWeekDailyRateFullDay'),
2 => $outputlangs->trans('TimesheetWeekDailyRateMorning'),
3 => $outputlangs->trans('TimesheetWeekDailyRateAfternoon')
);
if ($useQuarterDayDailyContract) {
// EN: Append the duration hints to each option and expose the quarter-day entry when enabled.
// FR: Ajoute les repères de durée à chaque option et expose l'entrée quart de jour lorsqu'elle est activée.
$dailyRateOptions[1] .= ' ('.$outputlangs->trans('TimesheetWeekDailyRateOneDay').')';
$halfDayLabel = $outputlangs->trans('TimesheetWeekDailyRateHalfDay');
$dailyRateOptions[2] = $halfDayLabel;
$dailyRateOptions[3] = $halfDayLabel;
$dailyRateOptions[4] = $outputlangs->trans('TimesheetWeekDailyRateQuarterDay');
}
}
$dailyRateHoursMap = $this->getDailyRateHoursMap();
$dayTotalsHours = array();
foreach ($days as $dayName) {
$dayTotalsHours[$dayName] = 0.0;
}
$grandHours = 0.0;


// EN: Prepare the legend describing available daily-rate durations when quarter-day support is active.
// FR: Prépare la légende décrivant les durées forfait jour disponibles lorsque le quart de jour est actif.
$dailyRateLegendText = '';
if ($isDailyRateEmployee && $useQuarterDayDailyContract) {
$dailyRateLegendText = $outputlangs->trans(
'TimesheetWeekDailyRateDurationsLegend',
$outputlangs->trans('TimesheetWeekDailyRateQuarterDay'),
$outputlangs->trans('TimesheetWeekDailyRateHalfDay'),
$outputlangs->trans('TimesheetWeekDailyRateOneDay')
);
}

// EN: Build the HTML table mirroring the editable grid layout.
// FR: Construit le tableau HTML reflétant la grille éditable.
$htmlGrid = '';
if ($dailyRateLegendText !== '') {
$htmlGrid .= '<p style="font-size:9px;font-style:italic;">'.tw_pdf_format_cell_html($dailyRateLegendText).'</p>';
}
if (empty($tasksByProject)) {
$htmlGrid .= '<p>'.tw_pdf_format_cell_html($outputlangs->trans('NoTasksAssigned')).'</p>';
} else {
Expand Down Expand Up @@ -848,10 +876,16 @@ protected function formatDays($value, $langs)
*/
protected function getDailyRateHoursMap()
{
return array(
$map = array(
1 => 8.0,
2 => 4.0,
3 => 4.0
);
if (getDolGlobalInt('TIMESHEETWEEK_QUARTERDAYFORDAILYCONTRACT', 0)) {
// EN: Register the quarter-day equivalence when the selector is enabled in configuration.
// FR: Enregistre l'équivalence du quart de jour lorsque le sélecteur est activé en configuration.
$map[4] = 2.0;
}
return $map;
}
}
8 changes: 8 additions & 0 deletions langs/en_US/timesheetweek.lang
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ TimesheetWeekPDFModelsEmpty = No PDF template is available.
PDFStandardTimesheetWeekDescription = Standard PDF model for weekly timesheets
TimesheetWeekPdfReferenceLabel = Timesheet reference: %s
TimesheetWeekPdfApprovedDetails = Approved on %s by %s
TimesheetWeekDailyRateOptions = Daily-rate options
TimesheetWeekDailyRateOptionsHelp = Configure the helpers available for daily-rate contracts.
TimesheetWeekQuarterDayForDailyContract = Quarter-day selector for daily-rate contracts
TimesheetWeekQuarterDayForDailyContractHelp = Allow daily-rate employees to declare quarter days instead of half-days only.
NewSection=New section
TIMESHEETWEEK_MYPARAM1 = My param 1
TIMESHEETWEEK_MYPARAM1Tooltip = My param 1 tooltip
Expand Down Expand Up @@ -173,6 +177,10 @@ TimesheetWeekDailyRateLabel = Daily rate contract
TimesheetWeekDailyRateFullDay = Full day
TimesheetWeekDailyRateMorning = Morning
TimesheetWeekDailyRateAfternoon = Afternoon
TimesheetWeekDailyRateOneDay = 1 day
TimesheetWeekDailyRateHalfDay = 0.5 day
TimesheetWeekDailyRateQuarterDay = 0.25 day
TimesheetWeekDailyRateDurationsLegend = Available daily-rate durations: %1$s / %2$s / %3$s
LastModification = Last modification
TotalDays = Total days
TimesheetWeekTotalDays = Total days
Expand Down
8 changes: 8 additions & 0 deletions langs/fr_FR/timesheetweek.lang
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ TimesheetWeekPDFModelsEmpty = Aucun modèle PDF disponible.
PDFStandardTimesheetWeekDescription = Modèle PDF standard pour les feuilles hebdomadaires
TimesheetWeekPdfReferenceLabel = Référence de la feuille : %s
TimesheetWeekPdfApprovedDetails = Approuvée le %s par %s
TimesheetWeekDailyRateOptions = Options forfait jour
TimesheetWeekDailyRateOptionsHelp = Configure les assistants disponibles pour les contrats au forfait jour.
TimesheetWeekQuarterDayForDailyContract = Sélecteur quart de jour pour forfait jour
TimesheetWeekQuarterDayForDailyContractHelp = Autorise les salariés au forfait jour à déclarer des quarts de jour au lieu des seules demi-journées.
NewSection=Nouvelle section
TIMESHEETWEEK_MYPARAM1 = Mon paramètre 1
TIMESHEETWEEK_MYPARAM1Tooltip = Info-bulle de mon paramètre 1
Expand Down Expand Up @@ -241,6 +245,10 @@ TimesheetWeekDailyRateLabel = Contrat forfait jour
TimesheetWeekDailyRateFullDay = Journée
TimesheetWeekDailyRateMorning = Matin
TimesheetWeekDailyRateAfternoon = Après-midi
TimesheetWeekDailyRateOneDay = 1 jour
TimesheetWeekDailyRateHalfDay = 0.5 jour
TimesheetWeekDailyRateQuarterDay = 0.25 jour
TimesheetWeekDailyRateDurationsLegend = Durées forfait jour disponibles : %1$s / %2$s / %3$s
LastModification = Dernière modification
TotalDays = Total jours
TimesheetWeekTotalDays = Total jours
Expand Down
35 changes: 35 additions & 0 deletions lib/timesheetweek_pdf.lib.php
Original file line number Diff line number Diff line change
Expand Up @@ -984,6 +984,20 @@ function tw_generate_summary_pdf($db, $conf, $langs, User $user, array $timeshee
});
$sortedUsers = array_values($dataset);

// EN: Detect whether at least one daily-rate employee is included to display duration hints later on.
// FR: Détecte si au moins un salarié au forfait jour est inclus afin d'afficher les repères de durée ensuite.
$hasDailyRateEmployee = false;
foreach ($sortedUsers as $sortedUserSummary) {
if (!empty($sortedUserSummary['is_daily_rate'])) {
$hasDailyRateEmployee = true;
break;
}
}

// EN: Remember if the quarter-day selector is enabled to conditionally show the duration legend in PDFs.
// FR: Mémorise si le sélecteur quart de jour est actif pour afficher conditionnellement la légende de durée dans les PDF.
$useQuarterDayDailyContract = (bool) getDolGlobalInt('TIMESHEETWEEK_QUARTERDAYFORDAILYCONTRACT', 0);

// EN: Track the lowest and highest ISO weeks among the selected records for filename generation.
// FR: Suit les semaines ISO minimale et maximale parmi les enregistrements sélectionnés pour le nom du fichier.
$earliestWeek = null;
Expand Down Expand Up @@ -1133,6 +1147,27 @@ function tw_generate_summary_pdf($db, $conf, $langs, User $user, array $timeshee

$usableWidth = $pdf->getPageWidth() - $margeGauche - $margeDroite;

// EN: Describe the legend reminding daily-rate durations whenever the quarter-day selector is active.
// FR: Décrit la légende rappelant les durées forfait jour lorsque le sélecteur quart de jour est actif.
$dailyRateLegendText = '';
if ($useQuarterDayDailyContract && $hasDailyRateEmployee) {
$dailyRateLegendText = $langs->trans(
'TimesheetWeekDailyRateDurationsLegend',
$langs->trans('TimesheetWeekDailyRateQuarterDay'),
$langs->trans('TimesheetWeekDailyRateHalfDay'),
$langs->trans('TimesheetWeekDailyRateOneDay')
);
}
if ($dailyRateLegendText !== '') {
// EN: Print the legend in italics to highlight the duration equivalences without overpowering the tables.
// FR: Affiche la légende en italique pour mettre en avant les équivalences de durée sans dominer les tableaux.
$pdf->SetFont('', 'I', max($defaultFontSize - 1, 6));
$pdf->SetX($margeGauche);
$pdf->MultiCell($usableWidth, 0, tw_pdf_normalize_string($dailyRateLegendText), 0, 'L', false);
$pdf->Ln(2);
$pdf->SetFont('', '', $defaultFontSize);
}

// EN: Describe the standard hour-based layout used for classic employees.
// FR: Décrit la mise en page standard en heures utilisée pour les salariés classiques.
$hoursColumnConfig = array(
Expand Down
Loading