WebUI / Pages : =============== private async Task Export() { item.DT_WORK_FORCE_ID_WORK_FORCE = listDT_WORK_FORCE_ID_WORK_FORCE!.Where(e => e.ID_WORK_FORCE == item.ID_WORK_FORCE).FirstOrDefault(); if(item.DATE_START>item.DATE_END) { var dtFin = item.DATE_START; var dtDebut = item.DATE_END; item.DATE_START = dtDebut; item.DATE_END = dtFin; } try { var resultat = await ApiService.GetExportByFiltersAsync($"api/DT_TIME_SHEET/export", item); if (resultat == null || resultat.Length == 0) { // Gérer le cas d'erreur ou pas de données ToastService.ShowInfo($"Il n'y a pas de données disponibles.", settings => { settings.Timeout = 2000; settings.IconType = IconType.None; settings.Position = ToastPosition.BottomCenter; }); return; } // Nom de fichier avec timestamp var now = DateTime.Now; var fileName = $"reportings_{now:yyyyMMdd_HHmmss}.xlsx"; // Convertir en base64 var base64 = Convert.ToBase64String(resultat); // Appel JS pour lancer le téléchargement await JS!.InvokeVoidAsync("downloadFileFromByteArray", fileName, base64); } catch (Exception ex) { ToastService.ShowError($"Erreur dans les exports. Faites CTRL + F5 et recommencez... Si le soucis persiste, voir avec l'administrateur.", settings => { settings.Timeout = 2000; settings.IconType = IconType.None; settings.Position = ToastPosition.BottomCenter; }); } } WebUI / Services : ================== public async Task GetExportByFiltersAsync(string url, T data) { var response = await _httpClient.PostAsJsonAsync(url, data); if (!response.IsSuccessStatusCode) return null; return await response.Content.ReadAsByteArrayAsync(); } API / CONTROLLER ================ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Temporis.Domain; namespace Temporis.Api.Controllers { public partial class DT_TIME_SHEETController { [HttpPost("export")] [Authorize(Roles = "Admin,Manager")] public async Task Export([FromBody] DT_TIME_SHEET dt_time_sheet) { var bytes = await dt_time_sheetService.ExportReportings(dt_time_sheet); if (bytes == null || bytes.Length == 0) return NotFound("Aucune donnée à exporter."); return File(bytes, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", $"reportings.xlsx"); } } } APPLICATION / Service : ======================= >>> D'abord : Installer le nuget ClosedXML sur le projet "Application" #region Exports public async Task ExportReportings(DT_TIME_SHEET filters) { if (filters == null) return null; var lst = await dt_time_sheetRepository.GetAll_by_filters_Async(filters); var lstItems = lst.ToList(); if (lstItems == null || lstItems.Count == 0) return null; using (var workbook = new XLWorkbook()) { // Grouper les lignes par ID_TIME_SHEET var groupByTimeSheet = lstItems .GroupBy(l => l.ID_TIME_SHEET) .ToList(); foreach (var group in groupByTimeSheet) { var timeSheet = group.First().DT_TIME_SHEET; string nomOnglet = timeSheet?.LIBELLE ?? $"TimeSheet_{group.Key.ToString().Substring(0, 8)}"; // Crée l'onglet var worksheet = workbook.Worksheets.Add(nomOnglet); int row = 0; // Identification worksheet.Cell(row + 1, 1).Value = "Nom"; worksheet.Cell(row + 2, 1).Value = "Prénom"; worksheet.Cell(row + 3, 1).Value = "Début Période"; worksheet.Cell(row + 4, 1).Value = "Fin Période"; worksheet.Cell(row + 5, 1).Value = "Validation manager ?"; worksheet.Cell(row + 1, 1).Style.Font.Bold = true; worksheet.Cell(row + 2, 1).Style.Font.Bold = true; worksheet.Cell(row + 3, 1).Style.Font.Bold = true; worksheet.Cell(row + 4, 1).Style.Font.Bold = true; worksheet.Cell(row + 5, 1).Style.Font.Bold = true; worksheet.Cell(row + 1, 2).Value = timeSheet!.DT_WORK_FORCE_ID_WORK_FORCE!.LAST_NAME; worksheet.Cell(row + 2, 2).Value = timeSheet!.DT_WORK_FORCE_ID_WORK_FORCE!.FIRST_NAME; worksheet.Cell(row + 3, 2).Value = timeSheet!.DATE_START.ToString("dd-MM-yyyy"); worksheet.Cell(row + 4, 2).Value = timeSheet!.DATE_END.ToString("dd-MM-yyyy"); worksheet.Cell(row + 5, 2).Value = timeSheet!.IsValidat ? "Oui" : "Non"; row = row + 7; // Entêtes de lignes worksheet.Cell(row, 1).Value = "Date"; worksheet.Cell(row, 2).Value = "Projet"; worksheet.Cell(row, 3).Value = "Quantité (j)"; worksheet.Cell(row, 4).Value = "Télétravail"; worksheet.Row(row).Style.Font.Bold = true; // Remplir les lignes row = row + 1; double nbrTeleTravail = 0; var lstAlternance = group.Where(e=>e.DT_PROJECT?.ID_PROJECT_TYPE == ToolsRepo.ID_PROJECT_TYPE_ALTERNANCE).ToList(); foreach (var line in group) { worksheet.Cell(row, 1).Value = line.DAY.ToString("dd-MM-yyyy"); worksheet.Cell(row, 2).Value = line.DT_PROJECT?.LIBELLE_PROJECT ?? ""; worksheet.Cell(row, 3).Value = line.QUANTITY; worksheet.Cell(row, 4).Value = line.TELETRAVAIL ? "Oui" : "Non"; if(line.TELETRAVAIL) { nbrTeleTravail = nbrTeleTravail + line.QUANTITY; } row++; } row = row + 2; worksheet.Cell(row, 1).Value = "Total télétravail (j)"; worksheet.Cell(row, 1).Style.Font.Bold = true; worksheet.Cell(row, 2).Value = nbrTeleTravail; var lstAbs = await dt_day_offRepository.GetAll_By_ID_WORK_FORCE_And_Dates_Async(timeSheet!.ID_WORK_FORCE, timeSheet!.DATE_START, timeSheet!.DATE_END); var lstAbsences = lstAbs.ToList(); var nbrJoursOuvres = Infrastructure.Tools.NbrJoursOuvres(timeSheet!.DATE_START, timeSheet!.DATE_END); row = row + 2; worksheet.Cell(row, 1).Value = "Nbre de Tickets Restaurant"; worksheet.Cell(row, 1).Style.Font.Bold = true; worksheet.Cell(row, 2).Value = nbrJoursOuvres - lstAbsences.Count - lstAlternance.Count; row = row + 2; if (lstAbsences != null && lstAbsences.Count() > 0) { worksheet.Cell(row, 1).Value = "Absences"; worksheet.Cell(row, 1).Style.Font.Bold = true; row = row + 1; foreach (var item in lstAbsences) { worksheet.Cell(row, 1).Value = item.DATE_DAY_OFF.ToString("dd-MM-yyyy"); worksheet.Cell(row, 2).Value = item.DT_DAY_OFF!.DT_REF_DAY_OFF_TYPE!.LIBELLE_DAY_OFF_TYPE; row++; } } // Ajustement automatique des colonnes worksheet.Columns().AdjustToContents(); } using var stream = new MemoryStream(); workbook.SaveAs(stream); return stream.ToArray(); } } #endregion