import { Op, type InferAttributes, type InferCreationAttributes, type WhereAttributeHash, } from 'sequelize'; import db from '@/db/models'; import { removeRecord, deleteRecordsByIds, autocompleteByField, findOwnedByPk, tenantWhere, } from '@/db/api/shared/repository'; import { BULK_IMPORT_TIMESTAMP_STEP_MS } from '@/shared/constants/database'; import { resolvePagination } from '@/shared/constants/pagination'; import Utils from '@/db/utils'; import type { Grades } from '@/db/models/grades'; import type { CurrentUser, DbApiOptions } from '@/db/api/types'; type GradesData = Partial> & { organization?: string | null; }; interface GradesFilter { limit?: number | string; page?: number | string; id?: string; name?: string; code?: string; description?: string; active?: boolean | string; organization?: string; sort_orderRange?: Array; createdAtRange?: Array; field?: string; sort?: string; } const NO_USER: CurrentUser = { id: null }; class GradesDBApi { static async create( data: GradesData, options?: DbApiOptions, ): Promise { const currentUser = options?.currentUser ?? NO_USER; const transaction = options?.transaction; const grades = await db.grades.create( { id: data.id || undefined, name: data.name || null, code: data.code || null, sort_order: data.sort_order || null, description: data.description || null, importHash: data.importHash || null, createdById: currentUser.id, updatedById: currentUser.id, }, { transaction }, ); await grades.setOrganization(currentUser.organizationId ?? undefined, { transaction, }); return grades; } static async bulkImport( data: GradesData[], options?: DbApiOptions, ): Promise { const currentUser = options?.currentUser ?? NO_USER; const transaction = options?.transaction; const gradesData = data.map((item, index) => ({ id: item.id || undefined, name: item.name || null, code: item.code || null, sort_order: item.sort_order || null, description: item.description || null, importHash: item.importHash || null, createdById: currentUser.id, updatedById: currentUser.id, createdAt: new Date(Date.now() + index * BULK_IMPORT_TIMESTAMP_STEP_MS), })); return db.grades.bulkCreate(gradesData, { transaction }); } static async update( id: string, data: GradesData, options?: DbApiOptions, ): Promise { const currentUser = options?.currentUser ?? NO_USER; const transaction = options?.transaction; const globalAccess = currentUser.app_role?.globalAccess; const grades = await findOwnedByPk(db.grades, id, options); if (!grades) { return null; } const updatePayload: Partial> = {}; if (data.name !== undefined) updatePayload.name = data.name; if (data.code !== undefined) updatePayload.code = data.code; if (data.sort_order !== undefined) updatePayload.sort_order = data.sort_order; if (data.description !== undefined) updatePayload.description = data.description; updatePayload.updatedById = currentUser.id; await grades.update(updatePayload, { transaction }); if (data.organization !== undefined) { const orgId = globalAccess ? data.organization : currentUser.organizationId; await grades.setOrganization(orgId ?? undefined, { transaction }); } return grades; } static async deleteByIds( ids: string[], options?: DbApiOptions, ): Promise { return deleteRecordsByIds(db.grades, ids, options); } static async remove( id: string, options?: DbApiOptions, ): Promise { return removeRecord(db.grades, id, options); } static async findBy( where: WhereAttributeHash, options?: DbApiOptions, ): Promise | null> { const transaction = options?.transaction; const grades = await db.grades.findOne({ where: { ...where, ...tenantWhere(options?.currentUser) }, transaction, }); if (!grades) { return null; } const output: Record = grades.get({ plain: true }); const [classes_grade, organization] = await Promise.all([ grades.getClasses_grade({ transaction }), grades.getOrganization({ transaction }), ]); output.classes_grade = classes_grade; output.organization = organization; return output; } static async findAll( filter: GradesFilter, globalAccess: boolean, options?: DbApiOptions, ): Promise<{ rows: Grades[]; count: number }> { const { limit, offset } = resolvePagination(filter.limit, filter.page); let where: WhereAttributeHash = {}; const userOrganizations = options?.currentUser?.organizations?.id ?? null; if (userOrganizations && options?.currentUser?.organizationId) { where.organizationId = options.currentUser.organizationId; } if (filter.id) { where = { ...where, id: Utils.uuid(filter.id) }; } if (filter.name) { where = { ...where, [Op.and]: Utils.ilike('grades', 'name', filter.name) }; } if (filter.code) { where = { ...where, [Op.and]: Utils.ilike('grades', 'code', filter.code) }; } if (filter.description) { where = { ...where, [Op.and]: Utils.ilike('grades', 'description', filter.description), }; } if (filter.sort_orderRange) { const [start, end] = filter.sort_orderRange; if (start !== undefined && start !== null && start !== '') { where = { ...where, sort_order: { [Op.gte]: start } }; } if (end !== undefined && end !== null && end !== '') { where = { ...where, sort_order: { ...(typeof where.sort_order === 'object' ? where.sort_order : {}), [Op.lte]: end, }, }; } } if (filter.active !== undefined) { where = { ...where, active: filter.active === true || filter.active === 'true', }; } if (filter.organization) { const listItems = filter.organization .split('|') .map((item) => Utils.uuid(item)); where = { ...where, organizationId: { [Op.or]: listItems } }; } if (filter.createdAtRange) { const [start, end] = filter.createdAtRange; if (start !== undefined && start !== null && start !== '') { where = { ...where, createdAt: { [Op.gte]: start } }; } if (end !== undefined && end !== null && end !== '') { where = { ...where, createdAt: { ...(typeof where.createdAt === 'object' ? where.createdAt : {}), [Op.lte]: end, }, }; } } if (globalAccess) { delete where.organizationId; } const order: [string, string][] = filter.field && filter.sort ? [[filter.field, filter.sort]] : [['createdAt', 'desc']]; const { rows, count } = await db.grades.findAndCountAll({ where, include: [{ model: db.organizations, as: 'organization' }], distinct: true, order, transaction: options?.transaction, limit: !options?.countOnly && limit ? limit : undefined, offset: !options?.countOnly && offset ? offset : undefined, }); return { rows: options?.countOnly ? [] : rows, count }; } static async findAllAutocomplete( query: string | undefined, limit: number | undefined, offset: number | undefined, globalAccess: boolean, organizationId: string | undefined, ): Promise> { return autocompleteByField( db.grades, 'name', query, limit, offset, globalAccess, organizationId, ); } } export default GradesDBApi;