package importer import ( "fmt" "regexp" "sort" "strconv" "strings" "github.com/xuri/excelize/v2" "git.dumerain.org/alban/paycheck/internal/models" ) // Feuille de mois acceptée : // - YYYY-MM (ex: 2025-01) // - MM-YYYY (ex: 01-2025) var sheetRe = regexp.MustCompile(`^(\d{4})-(\d{2})$|^(\d{2})-(\d{4})$`) func normalizeYearMonth(sheet string) (string, bool) { sheet = strings.TrimSpace(sheet) m := sheetRe.FindStringSubmatch(sheet) if m == nil { return "", false } // YYYY-MM if m[1] != "" && m[2] != "" { return fmt.Sprintf("%s-%s", m[1], m[2]), true } // MM-YYYY if m[3] != "" && m[4] != "" { return fmt.Sprintf("%s-%s", m[4], m[3]), true } return "", false } // parseNumberFR convertit "1,92" / "1.92" / "" -> float64 func parseNumberFR(s string) float64 { s = strings.TrimSpace(s) if s == "" { return 0 } s = strings.ReplaceAll(s, "\u00a0", "") s = strings.ReplaceAll(s, " ", "") s = strings.ReplaceAll(s, ",", ".") f, _ := strconv.ParseFloat(s, 64) return f } func isMatriculeCell(s string) bool { s = strings.TrimSpace(s) if s == "" { return false } for _, r := range s { if r < '0' || r > '9' { return false } } return true } // FindMonthSheet: prend la première feuille qui match YYYY-MM ou MM-YYYY, sinon la première feuille. func FindMonthSheet(f *excelize.File) (string, error) { sheets := f.GetSheetList() for _, sh := range sheets { if _, ok := normalizeYearMonth(sh); ok { return strings.TrimSpace(sh), nil } } if len(sheets) == 0 { return "", fmt.Errorf("aucune feuille trouvée dans le fichier") } return sheets[0], nil } // ParseAgentsAndTotals lit la feuille détectée et renvoie agents + totaux. // (Le mois est géré côté app/app.go via normalizeYearMonth) func ParseAgentsAndTotals(path string) ([]models.Agent, map[string]models.AgentTotals, error) { f, err := excelize.OpenFile(path) if err != nil { return nil, nil, err } defer func() { _ = f.Close() }() sheet, err := FindMonthSheet(f) if err != nil { return nil, nil, err } rows, err := f.GetRows(sheet) if err != nil { return nil, nil, err } agents := []models.Agent{} totals := map[string]models.AgentTotals{} // Dedup P_DIM-JF par agent (par date) dimDates := map[string]map[string]struct{}{} currentMatricule := "" cell := func(col string, excelRow int) string { v, _ := f.GetCellValue(sheet, fmt.Sprintf("%s%d", col, excelRow)) return v } for i := 0; i < len(rows); i++ { row := rows[i] colA := "" if len(row) >= 1 { colA = row[0] } if isMatriculeCell(colA) { currentMatricule = strings.TrimSpace(colA) if _, ok := totals[currentMatricule]; !ok { fullName := "" if len(row) >= 2 { fullName = strings.TrimSpace(row[1]) // Col B = "NOM Prenom" } nom, prenom := "", "" if fullName != "" { parts := strings.Fields(fullName) if len(parts) >= 1 { nom = parts[0] } if len(parts) >= 2 { prenom = strings.Join(parts[1:], " ") } } displayName := strings.TrimSpace(strings.Join([]string{nom, prenom}, " ")) if displayName == "" { displayName = currentMatricule } display := fmt.Sprintf("%s (%s)", displayName, currentMatricule) if i == 0 { continue } totalExcelRow := i // ligne au-dessus en Excel (cf logique actuelle) q471 := parseNumberFR(cell("G", totalExcelRow)) q456 := parseNumberFR(cell("L", totalExcelRow)) q459 := parseNumberFR(cell("M", totalExcelRow)) q458 := parseNumberFR(cell("N", totalExcelRow)) agents = append(agents, models.Agent{ Matricule: currentMatricule, Nom: nom, Prenom: prenom, Display: display, }) totals[currentMatricule] = models.AgentTotals{ Matricule: currentMatricule, Q471: q471, Q456: q456, Q459: q459, Q458: q458, NbDimFerie: 0, } } } if currentMatricule != "" { typeLigne := "" if len(row) >= 3 { typeLigne = strings.TrimSpace(row[2]) // Col C } if typeLigne == "P_DIM-JF" { dateStr := "" if len(row) >= 4 { dateStr = strings.TrimSpace(row[3]) // Col D } if _, ok := dimDates[currentMatricule]; !ok { dimDates[currentMatricule] = map[string]struct{}{} } if dateStr != "" { dimDates[currentMatricule][dateStr] = struct{}{} } else { dimDates[currentMatricule][fmt.Sprintf("row-%d", i+1)] = struct{}{} } } } } for mat, t := range totals { if s, ok := dimDates[mat]; ok { t.NbDimFerie = len(s) totals[mat] = t } } sort.Slice(agents, func(i, j int) bool { return strings.ToLower(agents[i].Display) < strings.ToLower(agents[j].Display) }) return agents, totals, nil }