import type { WeekdayName } from './field-purpose'; import { isWeekdayName } from './field-purpose'; export type WeekdayTemplateInput = { readonly startBranchId: string; readonly endBranchId: string; readonly customerIds: readonly string[]; }; export type WeekdaysInput = Partial>; export function isValidCycleNumber(raw: number): boolean { return Number.isInteger(raw) && raw >= 1; } export function assertCompleteWeekday( raw: WeekdayTemplateInput | undefined, ): WeekdayTemplateInput | undefined { if (raw === undefined) { return undefined; } if ( !raw.startBranchId || !raw.endBranchId || !Array.isArray(raw.customerIds) || raw.customerIds.length === 0 ) { throw new Error('incomplete'); } return raw; } export function parseWeekdaysInput(raw: unknown): WeekdaysInput { if (raw === undefined || raw === null) { return {}; } if (typeof raw !== 'object' || Array.isArray(raw)) { throw new Error('incomplete'); } const record = raw as Record; const result: WeekdaysInput = {}; for (const [key, value] of Object.entries(record)) { if (!isWeekdayName(key)) { throw new Error('incomplete'); } const complete = assertCompleteWeekday(value); if (complete) { result[key] = complete; } } return result; } /** RFC 4180-style record split that preserves commas inside quotes. */ export function parseCsvRecord(line: string): string[] { const cells: string[] = []; let current = ''; let inQuotes = false; for (let i = 0; i < line.length; i++) { const ch = line[i]; if (inQuotes) { if (ch === '"') { if (line[i + 1] === '"') { current += '"'; i += 1; } else { inQuotes = false; } } else { current += ch; } } else if (ch === '"') { inQuotes = true; } else if (ch === ',') { cells.push(current.trim()); current = ''; } else { current += ch; } } cells.push(current.trim()); return cells; } export function isAllowedCsvUpload(file: { mimetype: string; originalname: string; }): boolean { return ( file.mimetype.includes('csv') || file.originalname.toLowerCase().endsWith('.csv') ); }