validate advanced func sin utilizar en base al ejemplo de rosario
This commit is contained in:
+142
-52
@@ -23,6 +23,9 @@ export interface UsuarioRow {
|
||||
rfc: string;
|
||||
}
|
||||
|
||||
type RuleFn = (r: UsuarioRow, rowNum: number, errors: string[]) => void;
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class ExcelService {
|
||||
private readonly logger = new Logger(ExcelService.name);
|
||||
@@ -43,59 +46,7 @@ export class ExcelService {
|
||||
// … inyecta otros repositorios si los usarás
|
||||
) { }
|
||||
|
||||
/** Lee el buffer del Excel y devuelve un arreglo de filas tipadas */
|
||||
private async parseFile(buffer: Buffer): Promise<UsuarioRow[]> {
|
||||
const wb = new Workbook();
|
||||
await wb.xlsx.load(buffer);
|
||||
const sheet = wb.worksheets[0];
|
||||
const rows: UsuarioRow[] = [];
|
||||
|
||||
// Asume que la primera fila es encabezados
|
||||
sheet.eachRow((row, idx) => {
|
||||
if (idx === 1) return; // salto encabezados
|
||||
|
||||
// 1) Asegurarnos de que row.values no sea null/undefined
|
||||
if (!row.values) {
|
||||
this.logger.warn(`Fila ${idx + 1}: row.values vacío, se omite.`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 2) row.values[0] es null, así que slice(1) sí existe
|
||||
const [
|
||||
cuenta,
|
||||
nombreCompleto,
|
||||
clave,
|
||||
nomCarr,
|
||||
gen,
|
||||
fechnac,
|
||||
apellidopa,
|
||||
apellidoma,
|
||||
nombres,
|
||||
sexo,
|
||||
tipo,
|
||||
correo,
|
||||
rfc,
|
||||
] = (row.values as any[]).slice(1);
|
||||
|
||||
rows.push({
|
||||
cuenta,
|
||||
nombreCompleto,
|
||||
clave,
|
||||
nomCarr,
|
||||
gen,
|
||||
fechnac,
|
||||
apellidopa,
|
||||
apellidoma,
|
||||
nombres,
|
||||
sexo,
|
||||
tipo,
|
||||
correo,
|
||||
rfc,
|
||||
});
|
||||
});
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
/** Valida duplicados, celdas vacías y patrones básicos */
|
||||
validateRows(rows: UsuarioRow[]) {
|
||||
@@ -148,6 +99,145 @@ export class ExcelService {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Describes which rules aplicar por tipo
|
||||
private typeRules: Record<string, RuleFn[]> = {
|
||||
// Regla común para todos los tipos
|
||||
__default: [
|
||||
this.ruleNonEmpty(['cuenta', 'nombreCompleto', 'tipo']),
|
||||
this.ruleUniqueCuenta(),
|
||||
this.ruleNumericField('gen', /^[0-9]{4}$/),
|
||||
this.ruleDateField('fechnac'),
|
||||
this.ruleRFC('rfc'),
|
||||
],
|
||||
|
||||
// Añade reglas extra para Licenciatura
|
||||
Licenciatura: [
|
||||
this.ruleNonEmpty(['clave', 'nomCarr']),
|
||||
(r, rowNum, errors) => {
|
||||
if (!/^[0-9]{5}$/.test(r.clave)) {
|
||||
errors.push(`Fila ${rowNum}: clave de Licenciatura debe tener 5 dígitos.`);
|
||||
}
|
||||
},
|
||||
],
|
||||
|
||||
// Añade reglas extra para Profesor
|
||||
Profesor: [
|
||||
(r, rowNum, errors) => {
|
||||
if (!r.correo?.includes('@')) {
|
||||
errors.push(`Fila ${rowNum}: Profesor debe tener correo válido.`);
|
||||
}
|
||||
},
|
||||
],
|
||||
|
||||
// … puedes definir más tipos aquí …
|
||||
};
|
||||
|
||||
/** Métodos auxiliares que devuelven funciones de regla */
|
||||
private ruleNonEmpty(fields: (keyof UsuarioRow)[]): RuleFn {
|
||||
return (r, rowNum, errors) => {
|
||||
for (const f of fields) {
|
||||
const v = (r[f] ?? '').toString().trim();
|
||||
if (!v) {
|
||||
errors.push(`Fila ${rowNum}: campo "${f}" está vacío.`);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private ruleUniqueCuenta(): RuleFn {
|
||||
const seen = new Set<string>();
|
||||
return (r, rowNum, errors) => {
|
||||
const c = (r.cuenta ?? '').toString().trim();
|
||||
if (seen.has(c)) {
|
||||
errors.push(`Fila ${rowNum}: cuenta duplicada "${c}".`);
|
||||
} else {
|
||||
seen.add(c);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private ruleNumericField(field: keyof UsuarioRow, regex: RegExp): RuleFn {
|
||||
return (r, rowNum, errors) => {
|
||||
const v = (r[field] ?? '').toString().trim();
|
||||
if (v && !regex.test(v)) {
|
||||
errors.push(`Fila ${rowNum}: campo "${field}" inválido: "${v}".`);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private ruleDateField(field: keyof UsuarioRow): RuleFn {
|
||||
return (r, rowNum, errors) => {
|
||||
const v = (r[field] ?? '').toString().trim();
|
||||
if (v && !/^[0-9]{8}$/.test(v)) {
|
||||
errors.push(`Fila ${rowNum}: fecha "${field}" debe ser YYYYMMDD: "${v}".`);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private ruleRFC(field: keyof UsuarioRow): RuleFn {
|
||||
return (r, rowNum, errors) => {
|
||||
const v = (r[field] ?? '').toString().trim();
|
||||
if (v && !/^[A-Z0-9]+$/.test(v)) {
|
||||
errors.push(`Fila ${rowNum}: RFC contiene caracteres inválidos: "${v}".`);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/** Lee el buffer del Excel y devuelve un arreglo de filas tipadas */
|
||||
private async parseFile(buffer: Buffer): Promise<UsuarioRow[]> {
|
||||
const wb = new Workbook();
|
||||
await wb.xlsx.load(buffer);
|
||||
const sheet = wb.worksheets[0];
|
||||
const rows: UsuarioRow[] = [];
|
||||
|
||||
sheet.eachRow((row, idx) => {
|
||||
if (idx === 1) return; // salto encabezados
|
||||
const vals = row.values as any[];
|
||||
rows.push({
|
||||
cuenta: vals[1] ?? '',
|
||||
nombreCompleto: vals[2] ?? '',
|
||||
clave: vals[3] ?? '',
|
||||
nomCarr: vals[4] ?? '',
|
||||
gen: vals[5] ?? '',
|
||||
fechnac: vals[6] ?? '',
|
||||
apellidopa: vals[7] ?? '',
|
||||
apellidoma: vals[8] ?? '',
|
||||
nombres: vals[9] ?? '',
|
||||
sexo: vals[10] ?? '',
|
||||
tipo: vals[11] ?? '',
|
||||
correo: vals[12] ?? '',
|
||||
rfc: vals[13] ?? '',
|
||||
});
|
||||
});
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Valida cada fila según reglas comunes y específicas por tipo.
|
||||
* Retorna un array de mensajes de error.
|
||||
*/
|
||||
validateRowsAdvanced(rows: UsuarioRow[]): string[] {
|
||||
const errors: string[] = [];
|
||||
rows.forEach((r, i) => {
|
||||
const rowNum = i + 2;
|
||||
// 1) Aplica reglas comunes
|
||||
for (const rule of this.typeRules.__default) {
|
||||
rule.call(this, r, rowNum, errors);
|
||||
}
|
||||
// 2) Aplica reglas del tipo específico
|
||||
const specificRules = this.typeRules[r.tipo] ?? [];
|
||||
for (const rule of specificRules) {
|
||||
rule.call(this, r, rowNum, errors);
|
||||
}
|
||||
});
|
||||
return errors;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Solo valida: retorna lista de errores (vacío = ok)
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user