import { randomUUID } from "node:crypto"; import bcrypt from "bcrypt"; import type { QueryInterface, Sequelize } from "sequelize"; import { QueryTypes } from "sequelize"; type CreateAccountParams = { id?: string; email: string; password: string; name?: string; status?: "active" | "disabled"; languageCode?: string; }; type CreateCompanyParams = { id?: string; legalName: string; tradeName?: string | null; tin?: string | null; slug?: string; email?: string | null; phone?: string | null; website?: string | null; defaultPaymentTermId?: string | null; status?: "active" | "disabled"; }; type CreateMembershipParams = { id?: string; accountId: string; companyId: string; status?: "active" | "disabled" | "invited"; }; type CreatePaymentTermParams = { id?: string; companyId: string; name: string; description?: string | null; isActive?: boolean; isSystem?: boolean; dueRules?: Array<{ dueDays: number; percentageValue: number; percentageScale?: number; }>; }; export async function createTestAccount(database: Sequelize, params: CreateAccountParams) { const queryInterface = database.getQueryInterface(); const now = new Date(); const id = params.id ?? randomUUID(); const passwordHash = await bcrypt.hash(params.password, 10); await queryInterface.bulkInsert("identity_accounts", [ { id, email: params.email, password_hash: passwordHash, name: params.name ?? "Test Account", avatar_url: null, language_code: params.languageCode ?? "es", status: params.status ?? "active", created_at: now, updated_at: now, }, ]); return { id, email: params.email, password: params.password, }; } export async function createTestCompany(database: Sequelize, params: CreateCompanyParams) { const queryInterface = database.getQueryInterface(); const now = new Date(); const id = params.id ?? randomUUID(); await queryInterface.bulkInsert("companies", [ { id, legal_name: params.legalName, trade_name: params.tradeName ?? null, tin: params.tin ?? null, slug: params.slug ?? `company-${id.slice(0, 8)}`, email: params.email ?? null, phone: params.phone ?? null, website: params.website ?? null, default_payment_term_id: params.defaultPaymentTermId ?? null, status: params.status ?? "active", created_at: now, updated_at: now, }, ]); return { id, slug: params.slug ?? `company-${id.slice(0, 8)}`, }; } export async function createTestMembership(database: Sequelize, params: CreateMembershipParams) { const queryInterface = database.getQueryInterface(); const now = new Date(); const id = params.id ?? randomUUID(); await queryInterface.bulkInsert("identity_company_memberships", [ { id, account_id: params.accountId, company_id: params.companyId, status: params.status ?? "active", created_at: now, updated_at: now, }, ]); return { id, }; } export async function createTestPaymentTerm(database: Sequelize, params: CreatePaymentTermParams) { const queryInterface = database.getQueryInterface(); const now = new Date(); const id = params.id ?? randomUUID(); const dueRules = params.dueRules ?? [ { dueDays: 30, percentageValue: 10000, percentageScale: 2, }, ]; await queryInterface.bulkInsert("payment_terms", [ { id, company_id: params.companyId, name: params.name, description: params.description ?? null, is_active: params.isActive ?? true, is_system: params.isSystem ?? false, created_at: now, updated_at: now, deleted_at: null, }, ]); await queryInterface.bulkInsert( "payment_term_due_rules", dueRules.map((rule) => ({ due_id: randomUUID(), payment_term_id: id, due_days: rule.dueDays, percentage_value: rule.percentageValue, percentage_scale: rule.percentageScale ?? 2, created_at: now, updated_at: now, })) ); return { id, }; } export async function findCompanyRow(database: Sequelize, companyId: string) { const rows = await database.query<{ id: string; legal_name: string; trade_name: string | null; tin: string | null; slug: string; email: string | null; phone: string | null; website: string | null; default_payment_term_id: string | null; status: string; }>("SELECT * FROM companies WHERE id = :companyId", { replacements: { companyId }, type: QueryTypes.SELECT, }); return rows[0] ?? null; } export async function findAccountRow(database: Sequelize, accountId: string) { const rows = await database.query<{ id: string; email: string; password_hash: string; name: string; avatar_url: string | null; language_code: string; status: string; }>("SELECT * FROM identity_accounts WHERE id = :accountId", { replacements: { accountId }, type: QueryTypes.SELECT, }); return rows[0] ?? null; } export async function updateAccountStatus( database: Sequelize, params: { accountId: string; status: "active" | "disabled" | "pending_verification"; } ) { const queryInterface = database.getQueryInterface(); await queryInterface.bulkUpdate( "identity_accounts", { status: params.status, updated_at: new Date(), }, { id: params.accountId, } ); } export async function loginByApi(params: { request: ( path: string, init?: RequestInit ) => Promise<{ status: number; headers: Headers; body: unknown; }>; email: string; password: string; }) { return params.request("/api/v1/identity/auth/login", { method: "POST", headers: { "content-type": "application/json", }, body: JSON.stringify({ email: params.email, password: params.password, }), }); } export function buildCompanyHeaders(params: { accessToken?: string; companyId?: string; }): Record { const headers: Record = {}; if (params.accessToken) { headers.authorization = `Bearer ${params.accessToken}`; } if (params.companyId) { headers["x-company-id"] = params.companyId; } return headers; } export function getQueryInterface(database: Sequelize): QueryInterface { return database.getQueryInterface(); }