task-managment / src / server / material / infrastructure / repositories-impl.ts
repositories-impl.ts
Raw
import { dbGetAll, dbGetOne } from "@/lib/db/drizzle-client";
import {
  DeleteMaterial,
  DeleteProjectMaterial,
  Material,
  MaterialId,
  ProjectMaterial,
  UpdateProjectMaterial,
} from "../domain/models";
import { db } from "@/database/db";
import { materials, projectMaterials } from "@/database/schema";
import { and, desc, eq, inArray, sql } from "drizzle-orm";
import { createLogRecord } from "@/server/log";
import { UserId } from "@/server/user/domain/models";
import { MaterialRepository } from "../domain/repositories";
import { ProjectId } from "@/server/project/domain/models";

class MaterialRepositoryImpl implements MaterialRepository {
  getAll(): Promise<Material[]> {
    return dbGetAll("materials", {
      orderBy: desc(materials.name),
    }) as Promise<Material[]>;
  }

  async getById(materialId: MaterialId): Promise<Material> {
    const data = await dbGetOne("materials", {
      where: eq(materials.id, materialId.value),
    });
    return data as Material;
  }

  async getProjectMaterialByProject(
    materialId: MaterialId,
    projectId: ProjectId
  ): Promise<ProjectMaterial> {
    const data = await dbGetOne("projectMaterials", {
      where: and(
        eq(projectMaterials.materialId, materialId.value),
        eq(projectMaterials.projectId, projectId.value)
      ),
    });
    return data as ProjectMaterial;
  }
  async create(data: Material, userId: UserId): Promise<Material> {
    await db.transaction(async (tx) => {
      const createPromise = db.insert(materials).values(data);

      const logRecordPromise = createLogRecord(tx, {
        userId: userId.value,
        modifiedItem: `Material-${data.name} creado`,
        eventType: "crear",
        tableName: "materials",
        item: data,
      });

      await Promise.all([createPromise, logRecordPromise]);
    });

    return data;
  }

  async createProjectMaterial(
    data: ProjectMaterial,
    userId: UserId
  ): Promise<ProjectMaterial> {
    await db.transaction(async (tx) => {
      const material = await this.getById({ value: data.materialId });
      const createPromise = tx
        .insert(projectMaterials)
        .values(data)
        .onConflictDoUpdate({
          target: [projectMaterials.materialId, projectMaterials.projectId],
          set: {
            requiredQuantity: sql`${projectMaterials.requiredQuantity} + ${data.requiredQuantity}`,
            availableQuantity: sql`${projectMaterials.availableQuantity} + ${data.availableQuantity}`,
          },
        });

      const logRecordPromise = createLogRecord(tx, {
        userId: userId.value,
        modifiedItem: `Material-${material.name}-${data.projectName}`,
        eventType: "crear",
        tableName: "projectMaterials",
        item: data,
      });

      await Promise.all([createPromise, logRecordPromise]);
    });

    return data;
  }

  async updateProjectMaterial(
    data: Partial<ProjectMaterial>,
    userId: UserId
  ): Promise<Partial<ProjectMaterial>> {
    const { projectId, materialId, projectName, ...fieldsToUpdate } = data;
    await db.transaction(async (tx) => {
      const material = await this.getById({ value: data.materialId! });

      const updatePromise = tx
        .update(projectMaterials)
        .set(fieldsToUpdate)
        .where(
          and(
            eq(projectMaterials.materialId, data.materialId!),
            eq(projectMaterials.projectId, data.projectId!)
          )
        );

      const logRecordPromise = createLogRecord(tx, {
        userId: userId.value,
        modifiedItem: `Material-${material.name}-${data.projectName}`,
        eventType: "modificar",
        tableName: "projectMaterials",
        item: fieldsToUpdate,
      });
      await Promise.all([updatePromise, logRecordPromise]);
    });
    return data;
  }

  async delete(data: DeleteMaterial, userId: UserId): Promise<Material[]> {
    return await db.transaction(async (tx) => {
      const deletedMaterials = await tx
        .delete(materials)
        .where(inArray(materials.id, data.materialIds))
        .returning();

      const logRecordPromises = deletedMaterials.map((item) =>
        createLogRecord(tx, {
          userId: userId.value,
          modifiedItem: `Material-${item.name}`,
          eventType: "eliminar",
          tableName: "materials",
          item: deletedMaterials,
        })
      );

      await Promise.all([...logRecordPromises]);
      return deletedMaterials as Material[];
    });
  }

  async deleteProjectMaterials(
    data: DeleteProjectMaterial,
    userId: UserId
  ): Promise<DeleteProjectMaterial> {
    await db.transaction(async (tx) => {
      const material = await this.getById({ value: data.materialId });

      const deletePromise = tx
        .delete(projectMaterials)
        .where(
          and(
            eq(projectMaterials.materialId, data.materialId),
            eq(projectMaterials.projectId, data.projectId)
          )
        )
        .returning();

      const logRecordPromises = createLogRecord(tx, {
        userId: userId.value,
        modifiedItem: `Material-${material.name}-${data.projectName}`,
        eventType: "eliminar",
        tableName: "materials",
        item: data,
      });
      await Promise.all([deletePromise, logRecordPromises]);
    });

    return data;
  }
}

export const defaultMaterialRepository = new MaterialRepositoryImpl();