feat(preferences): added validation pipes to DTO, commented migration scripts

This commit is contained in:
Matthieu Haineault 2025-11-27 08:47:47 -05:00
parent 7c9f3cda65
commit e76f2a9a72
10 changed files with 829 additions and 824 deletions

View File

@ -1,270 +1,270 @@
// src/scripts/import-employees-from-csv.ts // // src/scripts/import-employees-from-csv.ts
import { PrismaClient, Roles } from '@prisma/client'; // import { PrismaClient, Roles } from '@prisma/client';
import * as fs from 'fs'; // import * as fs from 'fs';
import * as path from 'path'; // import * as path from 'path';
const prisma = new PrismaClient(); // const prisma = new PrismaClient();
// ⚙️ Chemin vers ton CSV employees // // ⚙️ Chemin vers ton CSV employees
const CSV_PATH = path.resolve(__dirname, '../../data/export_new_employee_table.csv'); // const CSV_PATH = path.resolve(__dirname, '../../data/export_new_employee_table.csv');
// Rôles éligibles pour la table Employees // // Rôles éligibles pour la table Employees
const ELIGIBLE_ROLES: Roles[] = [ // const ELIGIBLE_ROLES: Roles[] = [
Roles.EMPLOYEE, // Roles.EMPLOYEE,
Roles.SUPERVISOR, // Roles.SUPERVISOR,
Roles.HR, // Roles.HR,
Roles.ACCOUNTING, // Roles.ACCOUNTING,
Roles.ADMIN, // Roles.ADMIN,
]; // ];
// Type correspondant EXACT aux colonnes de ton CSV // // Type correspondant EXACT aux colonnes de ton CSV
type EmployeeCsvRow = { // type EmployeeCsvRow = {
employee_number: string; // employee_number: string;
email: string; // email: string;
job_title: string; // job_title: string;
company: string; // sera converti en number // company: string; // sera converti en number
is_supervisor: string; // "True"/"False" (ou variantes) // is_supervisor: string; // "True"/"False" (ou variantes)
onboarding: string; // millis // onboarding: string; // millis
offboarding: string; // millis ou "NULL" // offboarding: string; // millis ou "NULL"
}; // };
// Représentation minimale d'un user // // Représentation minimale d'un user
type UserSummary = { // type UserSummary = {
id: string; // UUID // id: string; // UUID
email: string; // email: string;
role: Roles; // role: Roles;
}; // };
// ============ Helpers CSV ============ // // ============ Helpers CSV ============
function splitCsvLine(line: string): string[] { // function splitCsvLine(line: string): string[] {
const result: string[] = []; // const result: string[] = [];
let current = ''; // let current = '';
let inQuotes = false; // let inQuotes = false;
for (let i = 0; i < line.length; i++) { // for (let i = 0; i < line.length; i++) {
const char = line[i]; // const char = line[i];
if (char === '"') { // if (char === '"') {
// guillemet échappé "" // // guillemet échappé ""
if (inQuotes && line[i + 1] === '"') { // if (inQuotes && line[i + 1] === '"') {
current += '"'; // current += '"';
i++; // i++;
} else { // } else {
inQuotes = !inQuotes; // inQuotes = !inQuotes;
} // }
} else if (char === ',' && !inQuotes) { // } else if (char === ',' && !inQuotes) {
result.push(current); // result.push(current);
current = ''; // current = '';
} else { // } else {
current += char; // current += char;
} // }
} // }
result.push(current); // result.push(current);
return result.map((v) => v.trim()); // return result.map((v) => v.trim());
} // }
// ============ Helpers de parsing ============ // // ============ Helpers de parsing ============
function parseBoolean(value: string): boolean { // function parseBoolean(value: string): boolean {
const v = value.trim().toLowerCase(); // const v = value.trim().toLowerCase();
return v === 'true' || v === '1' || v === 'yes' || v === 'y' || v === 'oui'; // return v === 'true' || v === '1' || v === 'yes' || v === 'y' || v === 'oui';
} // }
function parseIntSafe(value: string, fieldName: string): number | null { // function parseIntSafe(value: string, fieldName: string): number | null {
const trimmed = value.trim(); // const trimmed = value.trim();
if (!trimmed || trimmed.toUpperCase() === 'NULL') return null; // if (!trimmed || trimmed.toUpperCase() === 'NULL') return null;
const n = Number.parseInt(trimmed, 10); // const n = Number.parseInt(trimmed, 10);
if (Number.isNaN(n)) { // if (Number.isNaN(n)) {
console.warn(`⚠️ Impossible de parser "${value}" en entier pour le champ ${fieldName}`); // console.warn(`⚠️ Impossible de parser "${value}" en entier pour le champ ${fieldName}`);
return null; // return null;
} // }
return n; // return n;
} // }
function millisToDate(value: string): Date | null { // function millisToDate(value: string): Date | null {
const trimmed = value.trim().toUpperCase(); // const trimmed = value.trim().toUpperCase();
if (!trimmed || trimmed === 'NULL') return null; // if (!trimmed || trimmed === 'NULL') return null;
const ms = Number(trimmed); // const ms = Number(trimmed);
if (!Number.isFinite(ms)) { // if (!Number.isFinite(ms)) {
console.warn(`⚠️ Impossible de parser "${value}" en millis pour une Date`); // console.warn(`⚠️ Impossible de parser "${value}" en millis pour une Date`);
return null; // return null;
} // }
const d = new Date(ms); // const d = new Date(ms);
// On normalise au jour (minuit UTC) // // On normalise au jour (minuit UTC)
const normalized = new Date(Date.UTC( // const normalized = new Date(Date.UTC(
d.getUTCFullYear(), // d.getUTCFullYear(),
d.getUTCMonth(), // d.getUTCMonth(),
d.getUTCDate(), // d.getUTCDate(),
)); // ));
return normalized; // return normalized;
} // }
// ============ MAIN ============ // // ============ MAIN ============
async function main() { // async function main() {
// 1. Lecture du CSV // // 1. Lecture du CSV
const fileContent = fs.readFileSync(CSV_PATH, 'utf-8'); // const fileContent = fs.readFileSync(CSV_PATH, 'utf-8');
const lines = fileContent // const lines = fileContent
.split(/\r?\n/) // .split(/\r?\n/)
.map((l) => l.trim()) // .map((l) => l.trim())
.filter((l) => l.length > 0); // .filter((l) => l.length > 0);
if (lines.length <= 1) { // if (lines.length <= 1) {
console.error('CSV vide ou seulement un header'); // console.error('CSV vide ou seulement un header');
return; // return;
} // }
const header = splitCsvLine(lines[0]); // ["employee_number","email",...] // const header = splitCsvLine(lines[0]); // ["employee_number","email",...]
const dataLines = lines.slice(1); // const dataLines = lines.slice(1);
const csvRows: EmployeeCsvRow[] = dataLines.map((line) => { // const csvRows: EmployeeCsvRow[] = dataLines.map((line) => {
const values = splitCsvLine(line); // const values = splitCsvLine(line);
const row: any = {}; // const row: any = {};
header.forEach((col, idx) => { // header.forEach((col, idx) => {
row[col] = values[idx] ?? ''; // row[col] = values[idx] ?? '';
}); // });
return row as EmployeeCsvRow; // return row as EmployeeCsvRow;
}); // });
console.log(`➡️ ${csvRows.length} lignes trouvées dans le CSV employees`); // console.log(`➡️ ${csvRows.length} lignes trouvées dans le CSV employees`);
// 2. Récupérer tous les emails du CSV // // 2. Récupérer tous les emails du CSV
const emails = Array.from( // const emails = Array.from(
new Set( // new Set(
csvRows // csvRows
.map((r) => r.email.trim()) // .map((r) => r.email.trim())
.filter((e) => e.length > 0), // .filter((e) => e.length > 0),
), // ),
); // );
console.log(`➡️ ${emails.length} emails uniques trouvés dans le CSV`); // console.log(`➡️ ${emails.length} emails uniques trouvés dans le CSV`);
// 3. Charger les users correspondants avec les bons rôles // // 3. Charger les users correspondants avec les bons rôles
const users = (await prisma.users.findMany({ // const users = (await prisma.users.findMany({
where: { // where: {
email: { in: emails }, // email: { in: emails },
role: { in: ELIGIBLE_ROLES }, // role: { in: ELIGIBLE_ROLES },
}, // },
select: { // select: {
id: true, // id: true,
email: true, // email: true,
role: true, // role: true,
}, // },
})) as UserSummary[]; // })) as UserSummary[];
console.log(`➡️ ${users.length} users éligibles trouvés dans la DB`); // console.log(`➡️ ${users.length} users éligibles trouvés dans la DB`);
// Map email → user // // Map email → user
const userByEmail = new Map<string, UserSummary>(); // const userByEmail = new Map<string, UserSummary>();
for (const user of users) { // for (const user of users) {
const key = user.email.trim().toLowerCase(); // const key = user.email.trim().toLowerCase();
userByEmail.set(key, user); // userByEmail.set(key, user);
} // }
// 4. Construire les données pour employees.createMany // // 4. Construire les données pour employees.createMany
const employeesToCreate: { // const employeesToCreate: {
user_id: string; // user_id: string;
external_payroll_id: number; // external_payroll_id: number;
company_code: number; // company_code: number;
first_work_day: Date; // first_work_day: Date;
last_work_day: Date | null; // last_work_day: Date | null;
job_title: string | null; // job_title: string | null;
is_supervisor: boolean; // is_supervisor: boolean;
supervisor_id?: number | null; // supervisor_id?: number | null;
}[] = []; // }[] = [];
const rowsWithoutUser: EmployeeCsvRow[] = []; // const rowsWithoutUser: EmployeeCsvRow[] = [];
const rowsWithInvalidNumbers: EmployeeCsvRow[] = []; // const rowsWithInvalidNumbers: EmployeeCsvRow[] = [];
for (const row of csvRows) { // for (const row of csvRows) {
const emailKey = row.email.trim().toLowerCase(); // const emailKey = row.email.trim().toLowerCase();
const user = userByEmail.get(emailKey); // const user = userByEmail.get(emailKey);
if (!user) { // if (!user) {
rowsWithoutUser.push(row); // rowsWithoutUser.push(row);
continue; // continue;
} // }
const external_payroll_id = parseIntSafe(row.employee_number, 'external_payroll_id'); // const external_payroll_id = parseIntSafe(row.employee_number, 'external_payroll_id');
const company_code = parseIntSafe(String(row.company), 'company_code'); // const company_code = parseIntSafe(String(row.company), 'company_code');
if (external_payroll_id === null || company_code === null) { // if (external_payroll_id === null || company_code === null) {
rowsWithInvalidNumbers.push(row); // rowsWithInvalidNumbers.push(row);
continue; // continue;
} // }
const first_work_day = millisToDate(row.onboarding); // const first_work_day = millisToDate(row.onboarding);
const last_work_day = millisToDate(row.offboarding); // const last_work_day = millisToDate(row.offboarding);
const is_supervisor = parseBoolean(row.is_supervisor); // const is_supervisor = parseBoolean(row.is_supervisor);
const job_title = row.job_title?.trim() || null; // const job_title = row.job_title?.trim() || null;
if (!first_work_day) { // if (!first_work_day) {
console.warn( // console.warn(
`⚠️ Date d'onboarding invalide pour ${row.email} (employee_number=${row.employee_number})`, // `⚠️ Date d'onboarding invalide pour ${row.email} (employee_number=${row.employee_number})`,
); // );
continue; // continue;
} // }
employeesToCreate.push({ // employeesToCreate.push({
user_id: user.id, // user_id: user.id,
external_payroll_id, // external_payroll_id,
company_code, // company_code,
first_work_day, // first_work_day,
last_work_day, // last_work_day,
job_title, // job_title,
is_supervisor, // is_supervisor,
supervisor_id: null, // on pourra gérer ça plus tard si tu as les infos // supervisor_id: null, // on pourra gérer ça plus tard si tu as les infos
}); // });
} // }
console.log(`➡️ ${employeesToCreate.length} entrées Employees prêtes à être insérées`); // console.log(`➡️ ${employeesToCreate.length} entrées Employees prêtes à être insérées`);
if (rowsWithoutUser.length > 0) { // if (rowsWithoutUser.length > 0) {
console.warn(`⚠️ ${rowsWithoutUser.length} lignes CSV sans user correspondant (email / rôle) :`); // console.warn(`⚠️ ${rowsWithoutUser.length} lignes CSV sans user correspondant (email / rôle) :`);
for (const row of rowsWithoutUser) { // for (const row of rowsWithoutUser) {
console.warn( // console.warn(
` - email=${row.email}, employee_number=${row.employee_number}, company=${row.company}`, // ` - email=${row.email}, employee_number=${row.employee_number}, company=${row.company}`,
); // );
} // }
} // }
if (rowsWithInvalidNumbers.length > 0) { // if (rowsWithInvalidNumbers.length > 0) {
console.warn(`⚠️ ${rowsWithInvalidNumbers.length} lignes CSV avec ids/compagnies invalides :`); // console.warn(`⚠️ ${rowsWithInvalidNumbers.length} lignes CSV avec ids/compagnies invalides :`);
for (const row of rowsWithInvalidNumbers) { // for (const row of rowsWithInvalidNumbers) {
console.warn( // console.warn(
` - email=${row.email}, employee_number="${row.employee_number}", company="${row.company}"`, // ` - email=${row.email}, employee_number="${row.employee_number}", company="${row.company}"`,
); // );
} // }
} // }
if (employeesToCreate.length === 0) { // if (employeesToCreate.length === 0) {
console.warn('⚠️ Aucun Employees à créer, arrêt.'); // console.warn('⚠️ Aucun Employees à créer, arrêt.');
return; // return;
} // }
// 5. Insert en batch // // 5. Insert en batch
const result = await prisma.employees.createMany({ // const result = await prisma.employees.createMany({
data: employeesToCreate, // data: employeesToCreate,
skipDuplicates: true, // évite les erreurs si tu relances le script // skipDuplicates: true, // évite les erreurs si tu relances le script
}); // });
console.log(`${result.count} employees insérés dans la DB`); // console.log(`✅ ${result.count} employees insérés dans la DB`);
} // }
main() // main()
.catch((err) => { // .catch((err) => {
console.error('❌ Erreur pendant limport CSV → Employees', err); // console.error('❌ Erreur pendant limport CSV → Employees', err);
process.exit(1); // process.exit(1);
}) // })
.finally(async () => { // .finally(async () => {
await prisma.$disconnect(); // await prisma.$disconnect();
}); // });

View File

@ -1,106 +1,106 @@
// src/scripts/import-users-from-csv.ts // // src/scripts/import-users-from-csv.ts
import { PrismaClient } from '@prisma/client'; // import { PrismaClient } from '@prisma/client';
import * as fs from 'fs'; // import * as fs from 'fs';
import * as path from 'path'; // import * as path from 'path';
const prisma = new PrismaClient(); // const prisma = new PrismaClient();
// ⚙️ Chemin vers ton CSV (à adapter selon où tu le mets) // // ⚙️ Chemin vers ton CSV (à adapter selon où tu le mets)
const CSV_PATH = path.resolve(__dirname, '../../data/export_employee_table.csv'); // const CSV_PATH = path.resolve(__dirname, '../../data/export_employee_table.csv');
// Type aligné sur les colonnes du CSV // // Type aligné sur les colonnes du CSV
type CsvUserRow = { // type CsvUserRow = {
email: string; // email: string;
first_name: string; // first_name: string;
last_name: string; // last_name: string;
phone_number: string; // phone_number: string;
}; // };
// Petit parseur de ligne CSV sans dépendance // // Petit parseur de ligne CSV sans dépendance
function splitCsvLine(line: string): string[] { // function splitCsvLine(line: string): string[] {
const result: string[] = []; // const result: string[] = [];
let current = ''; // let current = '';
let inQuotes = false; // let inQuotes = false;
for (let i = 0; i < line.length; i++) { // for (let i = 0; i < line.length; i++) {
const char = line[i]; // const char = line[i];
if (char === '"') { // if (char === '"') {
// guillemet échappé "" // // guillemet échappé ""
if (inQuotes && line[i + 1] === '"') { // if (inQuotes && line[i + 1] === '"') {
current += '"'; // current += '"';
i++; // on saute le deuxième // i++; // on saute le deuxième
} else { // } else {
inQuotes = !inQuotes; // inQuotes = !inQuotes;
} // }
} else if (char === ',' && !inQuotes) { // } else if (char === ',' && !inQuotes) {
result.push(current); // result.push(current);
current = ''; // current = '';
} else { // } else {
current += char; // current += char;
} // }
} // }
result.push(current); // result.push(current);
return result.map((v) => v.trim()); // return result.map((v) => v.trim());
} // }
async function main() { // async function main() {
// 1. Lecture du fichier CSV // // 1. Lecture du fichier CSV
const fileContent = fs.readFileSync(CSV_PATH, 'utf-8'); // const fileContent = fs.readFileSync(CSV_PATH, 'utf-8');
const lines = fileContent // const lines = fileContent
.split(/\r?\n/) // .split(/\r?\n/)
.map((l) => l.trim()) // .map((l) => l.trim())
.filter((l) => l.length > 0); // .filter((l) => l.length > 0);
if (lines.length <= 1) { // if (lines.length <= 1) {
console.error('CSV vide ou seulement un header'); // console.error('CSV vide ou seulement un header');
return; // return;
} // }
// 2. Header (noms de colonnes) -> ["email", "first_name", "last_name", "phone_number"] // // 2. Header (noms de colonnes) -> ["email", "first_name", "last_name", "phone_number"]
const header = splitCsvLine(lines[0]); // const header = splitCsvLine(lines[0]);
const dataLines = lines.slice(1); // const dataLines = lines.slice(1);
// 3. Conversion de chaque ligne en objet { email, first_name, last_name, phone_number } // // 3. Conversion de chaque ligne en objet { email, first_name, last_name, phone_number }
const records: CsvUserRow[] = dataLines.map((line) => { // const records: CsvUserRow[] = dataLines.map((line) => {
const values = splitCsvLine(line); // const values = splitCsvLine(line);
const row: any = {}; // const row: any = {};
header.forEach((col, idx) => { // header.forEach((col, idx) => {
row[col] = values[idx] ?? ''; // row[col] = values[idx] ?? '';
}); // });
return row as CsvUserRow; // return row as CsvUserRow;
}); // });
// 4. Mapping vers le format attendu par Prisma (model Users) // // 4. Mapping vers le format attendu par Prisma (model Users)
const data = records.map((row) => ({ // const data = records.map((row) => ({
email: row.email.trim(), // email: row.email.trim(),
first_name: row.first_name.trim(), // first_name: row.first_name.trim(),
last_name: row.last_name.trim(), // last_name: row.last_name.trim(),
phone_number: row.phone_number.trim(), // phone_number: row.phone_number.trim(),
// residence: null, // si tu veux la forcer à null // // residence: null, // si tu veux la forcer à null
// role: 'GUEST', // sinon Prisma va appliquer la valeur par défaut // // role: 'GUEST', // sinon Prisma va appliquer la valeur par défaut
})); // }));
console.log(`➡️ ${data.length} lignes trouvées dans le CSV`); // console.log(`➡️ ${data.length} lignes trouvées dans le CSV`);
console.log('Exemple importé :', data[0]); // console.log('Exemple importé :', data[0]);
const result = await prisma.users.createMany({ // const result = await prisma.users.createMany({
data, // data,
}); // });
console.log(`${result.count} utilisateurs insérés dans la DB`); // console.log(`✅ ${result.count} utilisateurs insérés dans la DB`);
} // }
main() // main()
.catch((err) => { // .catch((err) => {
console.error('❌ Erreur pendant limport CSV → DB', err); // console.error('❌ Erreur pendant limport CSV → DB', err);
process.exit(1); // process.exit(1);
}) // })
.finally(async () => { // .finally(async () => {
await prisma.$disconnect(); // await prisma.$disconnect();
}); // });

View File

@ -1,67 +1,67 @@
// src/scripts/init-preferences.ts // // src/scripts/init-preferences.ts
import { PrismaClient } from '@prisma/client'; // import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient(); // const prisma = new PrismaClient();
type UserSummary = { // type UserSummary = {
id: string; // UUID // id: string; // UUID
email: string; // email: string;
}; // };
async function main() { // async function main() {
console.log('➡️ Initialisation des préférences utilisateurs…'); // console.log('➡️ Initialisation des préférences utilisateurs…');
// 1. Récupérer tous les users // // 1. Récupérer tous les users
const users = (await prisma.users.findMany({ // const users = (await prisma.users.findMany({
select: { // select: {
id: true, // id: true,
email: true, // email: true,
}, // },
})) as UserSummary[]; // })) as UserSummary[];
console.log(`➡️ ${users.length} users trouvés dans la DB`); // console.log(`➡️ ${users.length} users trouvés dans la DB`);
// 2. Récupérer toutes les préférences existantes // // 2. Récupérer toutes les préférences existantes
const existingPrefs = await prisma.preferences.findMany({ // const existingPrefs = await prisma.preferences.findMany({
select: { // select: {
user_id: true, // user_id: true,
}, // },
}); // });
const userIdsWithPrefs = new Set(existingPrefs.map((p) => p.user_id)); // const userIdsWithPrefs = new Set(existingPrefs.map((p) => p.user_id));
console.log(`➡️ ${existingPrefs.length} users ont déjà des préférences`); // console.log(`➡️ ${existingPrefs.length} users ont déjà des préférences`);
// 3. Filtrer les users qui n'ont pas encore de preferences // // 3. Filtrer les users qui n'ont pas encore de preferences
const usersWithoutPrefs = users.filter((u) => !userIdsWithPrefs.has(u.id)); // const usersWithoutPrefs = users.filter((u) => !userIdsWithPrefs.has(u.id));
console.log(`➡️ ${usersWithoutPrefs.length} users n'ont pas encore de préférences`); // console.log(`➡️ ${usersWithoutPrefs.length} users n'ont pas encore de préférences`);
if (usersWithoutPrefs.length === 0) { // if (usersWithoutPrefs.length === 0) {
console.log('✅ Rien à faire, toutes les préférences sont déjà créées.'); // console.log('✅ Rien à faire, toutes les préférences sont déjà créées.');
return; // return;
} // }
// 4. Préparer les entrées pour createMany // // 4. Préparer les entrées pour createMany
const prefsToCreate = usersWithoutPrefs.map((u) => ({ // const prefsToCreate = usersWithoutPrefs.map((u) => ({
user_id: u.id, // user_id: u.id,
// tous les autres champs prendront leurs valeurs par défaut (0) // // tous les autres champs prendront leurs valeurs par défaut (0)
})); // }));
// 5. Insertion en batch // // 5. Insertion en batch
const result = await prisma.preferences.createMany({ // const result = await prisma.preferences.createMany({
data: prefsToCreate, // data: prefsToCreate,
skipDuplicates: true, // sécurité si jamais le script est relancé // skipDuplicates: true, // sécurité si jamais le script est relancé
}); // });
console.log(`${result.count} préférences créées dans la DB`); // console.log(`✅ ${result.count} préférences créées dans la DB`);
} // }
main() // main()
.catch((err) => { // .catch((err) => {
console.error('❌ Erreur pendant linitialisation des préférences', err); // console.error('❌ Erreur pendant linitialisation des préférences', err);
process.exit(1); // process.exit(1);
}) // })
.finally(async () => { // .finally(async () => {
await prisma.$disconnect(); // await prisma.$disconnect();
}); // });

View File

@ -1,165 +1,165 @@
import { PrismaClient as Prisma } from "@prisma/client"; // import { PrismaClient as Prisma } from "@prisma/client";
import { PrismaClient as PrismaLegacy } from "@prisma/client-legacy" // import { PrismaClient as PrismaLegacy } from "@prisma/client-legacy"
import { toDateFromString, toHHmmFromDate, toStringFromDate } from "src/common/utils/date-utils"; // import { toDateFromString, toHHmmFromDate, toStringFromDate } from "src/common/utils/date-utils";
const prisma_legacy = new PrismaLegacy({}); // const prisma_legacy = new PrismaLegacy({});
const prisma = new Prisma({}); // const prisma = new Prisma({});
type NewEmployee = { // type NewEmployee = {
id: number; // id: number;
company_code: number; // company_code: number;
external_payroll_id: number; // external_payroll_id: number;
} // }
type OldExpense = { // type OldExpense = {
time_sheet_id: string | null; // time_sheet_id: string | null;
date: string | null; // date: string | null;
code: string | null; // code: string | null;
description: string | null; // description: string | null;
value: number | null; // value: number | null;
status: boolean | null; // status: boolean | null;
} // }
export const extractOldExpenses = async () => { // export const extractOldExpenses = async () => {
for (let id = 1; id <= 61; id++) { // for (let id = 1; id <= 61; id++) {
console.log(`Start of Expense migration ***************************************************************`); // console.log(`Start of Expense migration ***************************************************************`);
const new_employee = await findOneNewEmployee(id); // const new_employee = await findOneNewEmployee(id);
console.log(`Employee ${id} found in new DB`); // console.log(`Employee ${id} found in new DB`);
const new_timesheets = await findManyNewTimesheets(new_employee.id); // const new_timesheets = await findManyNewTimesheets(new_employee.id);
console.log(`New Timesheets found for employee ${id}`); // console.log(`New Timesheets found for employee ${id}`);
const old_employee_id = await findOneOldEmployee(new_employee); // const old_employee_id = await findOneOldEmployee(new_employee);
console.log(`Employee ${new_employee.id} found in old DB`); // console.log(`Employee ${new_employee.id} found in old DB`);
const old_timesheets = await findManyOldTimesheets(old_employee_id); // const old_timesheets = await findManyOldTimesheets(old_employee_id);
console.log(`Timesheets for employee ${old_employee_id}/${new_employee.id} found in old DB`); // console.log(`Timesheets for employee ${old_employee_id}/${new_employee.id} found in old DB`);
console.log('Start of Expense creation*****************************************************************'); // console.log('Start of Expense creation*****************************************************************');
for (const old_timesheet of old_timesheets) { // for (const old_timesheet of old_timesheets) {
if (!old_timesheet.start_date) continue; // if (!old_timesheet.start_date) continue;
const new_timesheet = new_timesheets.find((ts) => ts.start_date.getTime() === old_timesheet.start_date!.getTime()); // const new_timesheet = new_timesheets.find((ts) => ts.start_date.getTime() === old_timesheet.start_date!.getTime());
if (!new_timesheet) { // if (!new_timesheet) {
console.warn(`No new timesheet matching legacy timesheet ${old_timesheet.id}`); // console.warn(`No new timesheet matching legacy timesheet ${old_timesheet.id}`);
continue; // continue;
} // }
const old_expenses = await prisma_legacy.expenses.findMany({ // const old_expenses = await prisma_legacy.expenses.findMany({
where: { time_sheet_id: old_timesheet.id }, // where: { time_sheet_id: old_timesheet.id },
select: { // select: {
time_sheet_id: true, // time_sheet_id: true,
date: true, // date: true,
code: true, // code: true,
description: true, // description: true,
value: true, // value: true,
status: true, // status: true,
}, // },
}); // });
await createManyNewExpenses(new_timesheet.id, old_expenses); // await createManyNewExpenses(new_timesheet.id, old_expenses);
} // }
} // }
await prisma_legacy.$disconnect(); // await prisma_legacy.$disconnect();
await prisma.$disconnect(); // await prisma.$disconnect();
} // }
const findOneNewEmployee = async (id: number): Promise<NewEmployee> => { // const findOneNewEmployee = async (id: number): Promise<NewEmployee> => {
const new_employee = await prisma.employees.findUnique({ // const new_employee = await prisma.employees.findUnique({
where: { id: id }, // where: { id: id },
select: { // select: {
id: true, // id: true,
company_code: true, // company_code: true,
external_payroll_id: true, // external_payroll_id: true,
}, // },
}); // });
if (!new_employee) throw new Error(`New Employee with id ${id} not found`) // if (!new_employee) throw new Error(`New Employee with id ${id} not found`)
return new_employee; // return new_employee;
} // }
const findOneOldEmployee = async (new_employee: NewEmployee): Promise<string> => { // const findOneOldEmployee = async (new_employee: NewEmployee): Promise<string> => {
const old_employee = await prisma_legacy.employees.findFirst({ // const old_employee = await prisma_legacy.employees.findFirst({
where: { // where: {
company: new_employee.company_code, // company: new_employee.company_code,
employee_number: new_employee.external_payroll_id.toString(), // employee_number: new_employee.external_payroll_id.toString(),
}, // },
select: { // select: {
id: true, // id: true,
}, // },
}); // });
if (!old_employee) throw new Error(`Old Employee not found`); // if (!old_employee) throw new Error(`Old Employee not found`);
return old_employee.id; // return old_employee.id;
} // }
const findManyOldTimesheets = async (old_employee_id: string) => { // const findManyOldTimesheets = async (old_employee_id: string) => {
const old_timesheets = await prisma_legacy.time_sheets.findMany({ // const old_timesheets = await prisma_legacy.time_sheets.findMany({
where: { employee_id: old_employee_id }, // where: { employee_id: old_employee_id },
select: { id: true, start_date: true, status: true } // select: { id: true, start_date: true, status: true }
}); // });
return old_timesheets; // return old_timesheets;
} // }
const findManyNewTimesheets = async (employee_id: number) => { // const findManyNewTimesheets = async (employee_id: number) => {
const timesheets = await prisma.timesheets.findMany({ // const timesheets = await prisma.timesheets.findMany({
where: { employee_id: employee_id }, // where: { employee_id: employee_id },
select: { id: true, start_date: true } // select: { id: true, start_date: true }
}) // })
return timesheets; // return timesheets;
} // }
const createManyNewExpenses = async (timesheet_id: number, old_expenses: OldExpense[]) => { // const createManyNewExpenses = async (timesheet_id: number, old_expenses: OldExpense[]) => {
for (const old_expense of old_expenses) { // for (const old_expense of old_expenses) {
let mileage: number = 0; // let mileage: number = 0;
let amount: number = old_expense.value ?? 0; // let amount: number = old_expense.value ?? 0;
if (old_expense.code === 'G503') { // if (old_expense.code === 'G503') {
mileage = old_expense.value!; // mileage = old_expense.value!;
amount = mileage * 0.72; // amount = mileage * 0.72;
} // }
if (mileage < 0 || amount < 0) { // if (mileage < 0 || amount < 0) {
console.warn(`expense of value less than '0' found`) // console.warn(`expense of value less than '0' found`)
continue; // continue;
} // }
if (old_expense.date == null) { // if (old_expense.date == null) {
console.warn(`Expense date invalid ${old_expense.date}`); // console.warn(`Expense date invalid ${old_expense.date}`);
continue; // continue;
} // }
const date = toDateFromString(old_expense.date); // const date = toDateFromString(old_expense.date);
if (old_expense.status == null) { // if (old_expense.status == null) {
console.warn(`status null for legacy expense ${old_expense}`); // console.warn(`status null for legacy expense ${old_expense}`);
continue; // continue;
} // }
if (old_expense.code == null) { // if (old_expense.code == null) {
console.warn(`Code null for legacy expense ${old_expense.code}`); // console.warn(`Code null for legacy expense ${old_expense.code}`);
continue; // continue;
} // }
const bank_code_id = await findBankCodeIdUsingOldCode(old_expense.code); // const bank_code_id = await findBankCodeIdUsingOldCode(old_expense.code);
await prisma.expenses.create({ // await prisma.expenses.create({
// where: { unique_ts_id_date_amount_mileage: { timesheet_id: timesheet_id, date, amount, mileage } }, // // where: { unique_ts_id_date_amount_mileage: { timesheet_id: timesheet_id, date, amount, mileage } },
// update: { // // update: {
// is_approved: old_expense.status, // // is_approved: old_expense.status,
// }, // // },
data: { // data: {
date: date, // date: date,
comment: old_expense.description ?? '', // comment: old_expense.description ?? '',
timesheet_id: timesheet_id, // timesheet_id: timesheet_id,
bank_code_id: bank_code_id, // bank_code_id: bank_code_id,
amount: amount, // amount: amount,
mileage: mileage, // mileage: mileage,
} // }
}); // });
} // }
} // }
const findBankCodeIdUsingOldCode = async (code: string): Promise<number> => { // const findBankCodeIdUsingOldCode = async (code: string): Promise<number> => {
const bank_code = await prisma.bankCodes.findFirst({ // const bank_code = await prisma.bankCodes.findFirst({
where: { bank_code: code }, // where: { bank_code: code },
select: { id: true }, // select: { id: true },
}); // });
if (!bank_code) throw new Error(`Bank_code_id not found for Code ${code}`) // if (!bank_code) throw new Error(`Bank_code_id not found for Code ${code}`)
return bank_code.id; // return bank_code.id;
} // }

View File

@ -1,210 +1,210 @@
import { PrismaClient as PrismaNew } from "@prisma/client"; // import { PrismaClient as PrismaNew } from "@prisma/client";
import { PrismaClient as PrismaLegacy } from "@prisma/client-legacy" // import { PrismaClient as PrismaLegacy } from "@prisma/client-legacy"
const prisma_legacy = new PrismaLegacy({}); // const prisma_legacy = new PrismaLegacy({});
const prisma = new PrismaNew({}); // const prisma = new PrismaNew({});
type NewEmployee = { // type NewEmployee = {
id: number; // id: number;
company_code: number; // company_code: number;
external_payroll_id: number; // external_payroll_id: number;
} // }
type OldShifts = { // type OldShifts = {
time_sheet_id: string | null; // time_sheet_id: string | null;
code: string | null; // code: string | null;
type: string | null; // type: string | null;
date: Date | null; // date: Date | null;
start_time: bigint | null; // start_time: bigint | null;
end_time: bigint | null; // end_time: bigint | null;
comment: string | null; // comment: string | null;
status: boolean | null; // status: boolean | null;
} // }
export const extractOldShifts = async () => { // export const extractOldShifts = async () => {
// for (let id = 1; id <= 61; id++) { // // for (let id = 1; id <= 61; id++) {
console.log(`Start of shift migration ***************************************************************`); // console.log(`Start of shift migration ***************************************************************`);
const new_employee = await findOneNewEmployee(50); // const new_employee = await findOneNewEmployee(50);
console.log(`Employee ${50} found in new DB`); // console.log(`Employee ${50} found in new DB`);
const new_timesheets = await findManyNewTimesheets(new_employee.id); // const new_timesheets = await findManyNewTimesheets(new_employee.id);
console.log(`New Timesheets found for employee ${50}`); // console.log(`New Timesheets found for employee ${50}`);
for (const ts of new_timesheets) { // for (const ts of new_timesheets) {
console.log(`start_date = ${ts.start_date} timesheet_id = ${ts.id}`) // console.log(`start_date = ${ts.start_date} timesheet_id = ${ts.id}`)
} // }
console.log('***************************************************************'); // console.log('***************************************************************');
const old_employee_id = await findOneOldEmployee(new_employee); // const old_employee_id = await findOneOldEmployee(new_employee);
console.log(`Employee ${new_employee.id} found in old DB`); // console.log(`Employee ${new_employee.id} found in old DB`);
const old_timesheets = await findManyOldTimesheets(old_employee_id); // const old_timesheets = await findManyOldTimesheets(old_employee_id);
console.log(`Timesheets for employee ${old_employee_id}/${new_employee.id} found in old DB`); // console.log(`Timesheets for employee ${old_employee_id}/${new_employee.id} found in old DB`);
for (const old_timesheet of old_timesheets) { // for (const old_timesheet of old_timesheets) {
if (!old_timesheet.start_date) continue; // if (!old_timesheet.start_date) continue;
const new_timesheet = new_timesheets.find((ts) => ts.start_date.getTime() === old_timesheet.start_date!.getTime()); // const new_timesheet = new_timesheets.find((ts) => ts.start_date.getTime() === old_timesheet.start_date!.getTime());
if (!new_timesheet) { // if (!new_timesheet) {
console.warn(`No new timesheet ${new_timesheet} matching legacy timesheet ${old_timesheet.id}`); // console.warn(`No new timesheet ${new_timesheet} matching legacy timesheet ${old_timesheet.id}`);
continue; // continue;
} // }
const old_shifts = await prisma_legacy.shifts.findMany({ // const old_shifts = await prisma_legacy.shifts.findMany({
where: { time_sheet_id: old_timesheet.id }, // where: { time_sheet_id: old_timesheet.id },
select: { // select: {
time_sheet_id: true, // time_sheet_id: true,
code: true, // code: true,
type: true, // type: true,
date: true, // date: true,
start_time: true, // start_time: true,
end_time: true, // end_time: true,
comment: true, // comment: true,
status: true, // status: true,
}, // },
}); // });
await createManyNewShifts(new_timesheet.id, old_shifts); // await createManyNewShifts(new_timesheet.id, old_shifts);
} // }
// } // // }
await prisma_legacy.$disconnect(); // await prisma_legacy.$disconnect();
await prisma.$disconnect(); // await prisma.$disconnect();
} // }
const findOneNewEmployee = async (id: number): Promise<NewEmployee> => { // const findOneNewEmployee = async (id: number): Promise<NewEmployee> => {
const new_employee = await prisma.employees.findUnique({ // const new_employee = await prisma.employees.findUnique({
where: { id: id }, // where: { id: id },
select: { // select: {
id: true, // id: true,
company_code: true, // company_code: true,
external_payroll_id: true, // external_payroll_id: true,
}, // },
}); // });
if (!new_employee) throw new Error(`New Employee with id ${id} not found`) // if (!new_employee) throw new Error(`New Employee with id ${id} not found`)
return new_employee; // return new_employee;
} // }
const findOneOldEmployee = async (new_employee: NewEmployee): Promise<string> => { // const findOneOldEmployee = async (new_employee: NewEmployee): Promise<string> => {
const old_employee = await prisma_legacy.employees.findFirst({ // const old_employee = await prisma_legacy.employees.findFirst({
where: { // where: {
company: new_employee.company_code, // company: new_employee.company_code,
employee_number: new_employee.external_payroll_id.toString(), // employee_number: new_employee.external_payroll_id.toString(),
}, // },
select: { // select: {
id: true, // id: true,
}, // },
}); // });
if (!old_employee) throw new Error(`Old Employee not found`); // if (!old_employee) throw new Error(`Old Employee not found`);
return old_employee.id; // return old_employee.id;
} // }
const findManyOldTimesheets = async (old_employee_id: string) => { // const findManyOldTimesheets = async (old_employee_id: string) => {
const old_timesheets = await prisma_legacy.time_sheets.findMany({ // const old_timesheets = await prisma_legacy.time_sheets.findMany({
where: { employee_id: old_employee_id }, // where: { employee_id: old_employee_id },
select: { id: true, start_date: true, status: true } // select: { id: true, start_date: true, status: true }
}); // });
return old_timesheets; // return old_timesheets;
} // }
const findManyNewTimesheets = async (employee_id: number) => { // const findManyNewTimesheets = async (employee_id: number) => {
const timesheets = await prisma.timesheets.findMany({ // const timesheets = await prisma.timesheets.findMany({
where: { employee_id: employee_id }, // where: { employee_id: employee_id },
select: { id: true, start_date: true } // select: { id: true, start_date: true }
}) // })
return timesheets; // return timesheets;
} // }
const createManyNewShifts = async (timesheet_id: number, old_shifts: OldShifts[]) => { // const createManyNewShifts = async (timesheet_id: number, old_shifts: OldShifts[]) => {
for (const old_shift of old_shifts) { // for (const old_shift of old_shifts) {
let is_remote = true; // let is_remote = true;
const start = toHHmmfromLegacyTimestamp(old_shift.start_time); // const start = toHHmmfromLegacyTimestamp(old_shift.start_time);
if (old_shift.start_time == null || !start) { // if (old_shift.start_time == null || !start) {
console.warn(`Shift start invalid ${old_shift.start_time}`); // console.warn(`Shift start invalid ${old_shift.start_time}`);
continue; // continue;
} // }
const end = toHHmmfromLegacyTimestamp(old_shift.end_time); // const end = toHHmmfromLegacyTimestamp(old_shift.end_time);
if (old_shift.end_time == null || !end) { // if (old_shift.end_time == null || !end) {
console.warn(`Shift end invalid ${old_shift.end_time}`); // console.warn(`Shift end invalid ${old_shift.end_time}`);
continue; // continue;
} // }
if (old_shift.date == null) { // if (old_shift.date == null) {
console.warn(`Shift date invalid ${old_shift.date}`); // console.warn(`Shift date invalid ${old_shift.date}`);
continue; // continue;
} // }
if (old_shift.status == null) { // if (old_shift.status == null) {
console.warn(`status null for legacy shift ${old_shift}`); // console.warn(`status null for legacy shift ${old_shift}`);
continue; // continue;
} // }
if (old_shift.type == null) { // if (old_shift.type == null) {
console.warn(`type null for legacy shift ${old_shift.type}`); // console.warn(`type null for legacy shift ${old_shift.type}`);
continue; // continue;
} // }
if (old_shift.type === 'office') { // if (old_shift.type === 'office') {
is_remote = false; // is_remote = false;
} // }
if (old_shift.code == null) { // if (old_shift.code == null) {
console.warn(`Code null for legacy shift ${old_shift.code}`); // console.warn(`Code null for legacy shift ${old_shift.code}`);
continue; // continue;
} // }
const bank_code_id = await findBankCodeIdUsingOldCode(old_shift.code); // const bank_code_id = await findBankCodeIdUsingOldCode(old_shift.code);
try { // try {
await prisma.shifts.create({ // await prisma.shifts.create({
// where: { unique_ts_id_date_start_time: { // // where: { unique_ts_id_date_start_time: {
// timesheet_id, // // timesheet_id,
// date: old_shift.date, // // date: old_shift.date,
// start_time: toDateFromHHmm(start) }}, // // start_time: toDateFromHHmm(start) }},
// update: { // // update: {
// start_time: toDateFromHHmm(start), // // start_time: toDateFromHHmm(start),
// end_time: toDateFromHHmm(end), // // end_time: toDateFromHHmm(end),
// comment: old_shift.comment, // // comment: old_shift.comment,
// is_approved: old_shift.status, // // is_approved: old_shift.status,
// is_remote: is_remote, // // is_remote: is_remote,
// bank_code_id: bank_code_id, // // bank_code_id: bank_code_id,
// }, // // },
data: { // data: {
date: old_shift.date, // date: old_shift.date,
start_time: toDateFromHHmm(start), // start_time: toDateFromHHmm(start),
end_time: toDateFromHHmm(end), // end_time: toDateFromHHmm(end),
comment: old_shift.comment, // comment: old_shift.comment,
is_approved: old_shift.status, // is_approved: old_shift.status,
is_remote: is_remote, // is_remote: is_remote,
timesheet_id: timesheet_id, // timesheet_id: timesheet_id,
bank_code_id: bank_code_id, // bank_code_id: bank_code_id,
}, // },
}); // });
} catch (error) { // } catch (error) {
console.log('An error occured during shifts creation'); // console.log('An error occured during shifts creation');
} // }
} // }
} // }
const toHHmmfromLegacyTimestamp = (value: bigint | null): string | null => { // const toHHmmfromLegacyTimestamp = (value: bigint | null): string | null => {
if (value == null) return null; // if (value == null) return null;
const date = new Date(Number(value)); // const date = new Date(Number(value));
const hh = String(date.getHours()).padStart(2, '0'); // const hh = String(date.getHours()).padStart(2, '0');
const mm = String(date.getMinutes()).padStart(2, '0'); // const mm = String(date.getMinutes()).padStart(2, '0');
return `${hh}:${mm}`; // return `${hh}:${mm}`;
} // }
const toDateFromHHmm = (hhmm: string): Date => { // const toDateFromHHmm = (hhmm: string): Date => {
const [hh, mm] = hhmm.split(':'); // const [hh, mm] = hhmm.split(':');
const hours = Number(hh); // const hours = Number(hh);
const minutes = Number(mm); // const minutes = Number(mm);
return new Date(Date.UTC(1970, 0, 1, hours, minutes, 0, 0)); // return new Date(Date.UTC(1970, 0, 1, hours, minutes, 0, 0));
} // }
const findBankCodeIdUsingOldCode = async (code: string): Promise<number> => { // const findBankCodeIdUsingOldCode = async (code: string): Promise<number> => {
if (code === 'G700') { // if (code === 'G700') {
code = 'G104'; // code = 'G104';
} else if (code === 'G140') { // } else if (code === 'G140') {
code = 'G56' // code = 'G56'
} // }
const bank_code = await prisma.bankCodes.findFirst({ // const bank_code = await prisma.bankCodes.findFirst({
where: { bank_code: code }, // where: { bank_code: code },
select: { id: true, bank_code: true }, // select: { id: true, bank_code: true },
}); // });
if (!bank_code) throw new Error(`Bank_code_id not found for Code ${code}`) // if (!bank_code) throw new Error(`Bank_code_id not found for Code ${code}`)
return bank_code.id; // return bank_code.id;
} // }

View File

@ -1,118 +1,118 @@
import { PrismaClient as Prisma } from "@prisma/client"; // import { PrismaClient as Prisma } from "@prisma/client";
import { PrismaClient as PrismaLegacy } from "@prisma/client-legacy" // import { PrismaClient as PrismaLegacy } from "@prisma/client-legacy"
import { toStringFromDate } from "src/common/utils/date-utils"; // import { toStringFromDate } from "src/common/utils/date-utils";
type NewEmployee = { // type NewEmployee = {
id: number; // id: number;
company_code: number; // company_code: number;
external_payroll_id: number; // external_payroll_id: number;
} // }
type OldTimesheets = { // type OldTimesheets = {
id: string; // id: string;
start_date: Date | null; // start_date: Date | null;
status: boolean | null; // status: boolean | null;
} // }
const prisma_legacy = new PrismaLegacy({}); // const prisma_legacy = new PrismaLegacy({});
const prisma_new = new Prisma({}); // const prisma_new = new Prisma({});
export const extractOldTimesheets = async () => { // export const extractOldTimesheets = async () => {
for (let id = 1; id <= 61; id++) { // for (let id = 1; id <= 61; id++) {
const new_employee = await findOneNewEmployee(id); // const new_employee = await findOneNewEmployee(id);
console.log(`Employee ${id} found in new DB ${new_employee.external_payroll_id}`); // console.log(`Employee ${id} found in new DB ${new_employee.external_payroll_id}`);
const old_employee_id = await findOneOldEmployee(new_employee); // const old_employee_id = await findOneOldEmployee(new_employee);
console.log(`Employee ${new_employee.id} found in old DB`); // console.log(`Employee ${new_employee.id} found in old DB`);
const old_timesheets = await findManyOldTimesheets(old_employee_id); // const old_timesheets = await findManyOldTimesheets(old_employee_id);
console.log(` ${old_timesheets.length} Timesheets for employee ${old_employee_id}/${new_employee.id} found in old DB`); // console.log(` ${old_timesheets.length} Timesheets for employee ${old_employee_id}/${new_employee.id} found in old DB`);
await createManyNewTimesheets(old_timesheets, new_employee); // await createManyNewTimesheets(old_timesheets, new_employee);
console.log(`${old_timesheets.length} New Timesheets created in new DB for employee ${new_employee.id}`); // console.log(`${old_timesheets.length} New Timesheets created in new DB for employee ${new_employee.id}`);
} // }
await prisma_legacy.$disconnect(); // await prisma_legacy.$disconnect();
await prisma_new.$disconnect(); // await prisma_new.$disconnect();
} // }
const findOneNewEmployee = async (id: number): Promise<NewEmployee> => { // const findOneNewEmployee = async (id: number): Promise<NewEmployee> => {
const new_employee = await prisma_new.employees.findUnique({ // const new_employee = await prisma_new.employees.findUnique({
where: { id: id }, // where: { id: id },
select: { // select: {
id: true, // id: true,
company_code: true, // company_code: true,
external_payroll_id: true, // external_payroll_id: true,
}, // },
}); // });
if (!new_employee) throw new Error(`New Employee with id ${id} not found`) // if (!new_employee) throw new Error(`New Employee with id ${id} not found`)
return new_employee; // return new_employee;
} // }
const findOneOldEmployee = async (new_employee: NewEmployee): Promise<string> => { // const findOneOldEmployee = async (new_employee: NewEmployee): Promise<string> => {
const employee_number = new_employee.external_payroll_id.toString() // const employee_number = new_employee.external_payroll_id.toString()
const old_employee = await prisma_legacy.employees.findFirst({ // const old_employee = await prisma_legacy.employees.findFirst({
where: { // where: {
company: new_employee.company_code, // company: new_employee.company_code,
employee_number: employee_number, // employee_number: employee_number,
}, // },
select: { // select: {
id: true, // id: true,
}, // },
}); // });
if (!old_employee) throw new Error(`Old Employee not found`); // if (!old_employee) throw new Error(`Old Employee not found`);
return old_employee.id; // return old_employee.id;
} // }
const findManyOldTimesheets = async (old_employee_id: string) => { // const findManyOldTimesheets = async (old_employee_id: string) => {
const old_timesheets = await prisma_legacy.time_sheets.findMany({ // const old_timesheets = await prisma_legacy.time_sheets.findMany({
where: { employee_id: old_employee_id }, // where: { employee_id: old_employee_id },
select: { id: true, start_date: true, status: true } // select: { id: true, start_date: true, status: true }
}); // });
if (!old_timesheets) throw new Error(`old Timesheets not found for employee_id ${old_employee_id}`) // if (!old_timesheets) throw new Error(`old Timesheets not found for employee_id ${old_employee_id}`)
return old_timesheets; // return old_timesheets;
} // }
const createManyNewTimesheets = async (old_timesheets: OldTimesheets[], new_employee: NewEmployee) => { // const createManyNewTimesheets = async (old_timesheets: OldTimesheets[], new_employee: NewEmployee) => {
for (const timesheet of old_timesheets) { // for (const timesheet of old_timesheets) {
if (timesheet.start_date == null) { // if (timesheet.start_date == null) {
console.warn(`start_date invalid for legacy timesheet ${timesheet.id}`); // console.warn(`start_date invalid for legacy timesheet ${timesheet.id}`);
continue; // continue;
} // }
if (timesheet.status == null) { // if (timesheet.status == null) {
console.warn(`status null for legacy timesheet ${timesheet.id}`); // console.warn(`status null for legacy timesheet ${timesheet.id}`);
continue; // continue;
} // }
try { // try {
const new_timesheet = await prisma_new.timesheets.upsert({ // const new_timesheet = await prisma_new.timesheets.upsert({
where: { employee_id_start_date: { employee_id: new_employee.id, start_date: timesheet.start_date } }, // where: { employee_id_start_date: { employee_id: new_employee.id, start_date: timesheet.start_date } },
update: { // update: {
is_approved: timesheet.status, // is_approved: timesheet.status,
}, // },
create: { // create: {
employee_id: new_employee.id, // employee_id: new_employee.id,
start_date: timesheet.start_date, // start_date: timesheet.start_date,
is_approved: timesheet.status, // is_approved: timesheet.status,
}, // },
}); // });
if (!new_timesheet) throw new Error( // if (!new_timesheet) throw new Error(
`Timesheet with start_date: ${toStringFromDate(timesheet.start_date!)} for employee ${new_employee.id} not created` // `Timesheet with start_date: ${toStringFromDate(timesheet.start_date!)} for employee ${new_employee.id} not created`
); // );
} catch (error) { // } catch (error) {
throw new Error('An error occured during timesheets creation'); // throw new Error('An error occured during timesheets creation');
} // }
} // }
} // }
extractOldTimesheets() // extractOldTimesheets()
.then(() => { // .then(() => {
console.log("Migration completed"); // console.log("Migration completed");
}) // })
.catch((error) => { // .catch((error) => {
console.error("Migration failed:", error); // console.error("Migration failed:", error);
}) // })
.finally(async () => { // .finally(async () => {
await prisma_legacy.$disconnect(); // await prisma_legacy.$disconnect();
await prisma_new.$disconnect(); // await prisma_new.$disconnect();
}); // });

View File

@ -1,21 +1,21 @@
// import { extractOldTimesheets } from "scripts/migrate-timesheets"; // // import { extractOldTimesheets } from "scripts/migrate-timesheets";
// import { extractOldExpenses } from "scripts/migrate-expenses"; // // import { extractOldExpenses } from "scripts/migrate-expenses";
// import { extractOldShifts } from "scripts/migrate-shifts"; // // import { extractOldShifts } from "scripts/migrate-shifts";
import { Injectable } from "@nestjs/common"; // import { Injectable } from "@nestjs/common";
@Injectable() // @Injectable()
export class MigrationService { // export class MigrationService {
constructor() {} // constructor() {}
async migrateTimesheets() { // async migrateTimesheets() {
// extractOldTimesheets(); // // extractOldTimesheets();
}; // };
async migrateShifts() { // async migrateShifts() {
// extractOldShifts(); // // extractOldShifts();
} // }
async migrateExpenses() { // async migrateExpenses() {
// extractOldExpenses(); // // extractOldExpenses();
} // }
} // }

View File

@ -1,10 +1,15 @@
import { IsInt } from "class-validator"; import { IsBoolean, IsEnum, IsInt, IsOptional } from "class-validator";
export enum DisplayLanguage {
FR = 'fr-FR',
EN = 'en-CA',
}
export class PreferencesDto { export class PreferencesDto {
notifications: number; @IsInt() notifications: number;
is_dark_mode: boolean | null; @IsOptional() @IsBoolean() is_dark_mode?: boolean;
display_language: string | 'fr-FR' | 'en-CA'; @IsEnum(DisplayLanguage) display_language: string;
is_lefty_mode: boolean; @IsBoolean() is_lefty_mode: boolean;
is_employee_list_grid: boolean; @IsBoolean() is_employee_list_grid: boolean;
is_timesheet_approval_grid: boolean; @IsBoolean() is_timesheet_approval_grid: boolean;
} }

View File

@ -1,4 +1,4 @@
import { PreferencesDto } from "../dtos/preferences.dto"; import { DisplayLanguage, PreferencesDto } from "../dtos/preferences.dto";
import { PrismaService } from "src/prisma/prisma.service"; import { PrismaService } from "src/prisma/prisma.service";
import { Preferences } from "@prisma/client"; import { Preferences } from "@prisma/client";
import { Injectable } from "@nestjs/common"; import { Injectable } from "@nestjs/common";
@ -32,7 +32,7 @@ export class PreferencesService {
const preferences: PreferencesDto = { const preferences: PreferencesDto = {
is_dark_mode: user_preferences.is_dark_mode, is_dark_mode: user_preferences.is_dark_mode,
display_language: user_preferences.display_language, display_language: user_preferences.display_language ?? DisplayLanguage.FR ,
is_lefty_mode: user_preferences.is_lefty_mode, is_lefty_mode: user_preferences.is_lefty_mode,
notifications: user_preferences.notifications, notifications: user_preferences.notifications,
is_employee_list_grid: user_preferences.is_employee_list_grid, is_employee_list_grid: user_preferences.is_employee_list_grid,

View File

@ -18,9 +18,9 @@ import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { writeFileSync } from 'fs'; import { writeFileSync } from 'fs';
import * as session from 'express-session'; import * as session from 'express-session';
import * as passport from 'passport'; import * as passport from 'passport';
import { extractOldShifts } from 'scripts/migrate-shifts'; // import { extractOldShifts } from 'scripts/migrate-shifts';
import { extractOldTimesheets } from 'scripts/migrate-timesheets'; // import { extractOldTimesheets } from 'scripts/migrate-timesheets';
import { extractOldExpenses } from 'scripts/migrate-expenses'; // import { extractOldExpenses } from 'scripts/migrate-expenses';
const SESSION_TOKEN_DURATION_MINUTES = 180 const SESSION_TOKEN_DURATION_MINUTES = 180