import { container } from "tsyringe"; import { DataSource, In, IsNull, Not } from "typeorm"; import Stock from "../../domain/entities/stock"; import StockRepository from "../../domain/repositories/stock.repository"; import { AssignStockAttr, CreateStockAttr, DeleteStockAttr, FindStockAttr, ListUserStockAttr, UpdateStockAttr } from "../../domain/types/stock.type"; import { StockRecord } from "../db/records/stock.record"; export default class StockRepositoryImpl implements StockRepository { async find(payload: FindStockAttr): Promise { const record = await StockRecord.createQueryBuilder() .innerJoinAndSelect("StockRecord.item", "ItemRecord", "ItemRecord.id = :itemId") .setParameters({ itemId: payload.itemId }) .where({ ...(payload.userId && { assignedToId: payload.userId }), ...(payload.code && { code: payload.code }), ...(payload.encodedCode && { encodedCode: payload.encodedCode }), ...(payload.noAssigned && { assignedToId: IsNull() }), ...(payload.id && { id: payload.id }), available: true }) .getOne(); return record?.toEntity(); } async all(itemId: string, page: number, withCoupon?: boolean): Promise<{ count: number, stock: Stock[] }> { const filtered = StockRecord.createQueryBuilder() .leftJoinAndSelect("StockRecord.item", "ItemRecord") .where({ item: { id: itemId }, available: true, ...(withCoupon && { encodedCode: Not(IsNull()) }) }); const records = await filtered .take(Number(process.env.PER_PAGE)) .offset(Number(process.env.PER_PAGE) * ((page || 1) - 1)) .getMany(); const count = await filtered .getCount(); return { count, stock: records.map(record => record.toEntity()) }; } async list(payload: ListUserStockAttr): Promise<{ count: number, stock: Stock[] }> { const filtered = StockRecord.createQueryBuilder() .leftJoinAndSelect("StockRecord.item", "ItemRecord") .where({ assignedToId: payload.userId, available: true, }); const records = await filtered .take(Number(process.env.PER_PAGE)) .offset(Number(process.env.PER_PAGE) * ((payload?.page || 1) - 1)) .getMany(); const count = await filtered .getCount(); return { count, stock: records.map(record => record.toEntity()) }; } async create(payload: CreateStockAttr): Promise { const stock: { item: { id: string }, code: string, encodedCode?: string, assignedToId?: number }[] = []; payload.stock.forEach(s => stock.push({ item: { id: payload.itemId }, code: s.code, assignedToId: s.assignedToId, encodedCode: s.encodedCode || Stock.encode(), })); const record = await StockRecord.createQueryBuilder() .insert() .values(stock) .returning(["id"]) .execute(); const records = await StockRecord.createQueryBuilder() .leftJoinAndSelect("StockRecord.item", "ItemRecord") .where({ id: In(record.identifiers.map(i => i.id)) }) .getMany(); return records.map(record => record.toEntity()); } async update(payload: UpdateStockAttr): Promise { const record = await StockRecord.createQueryBuilder() .update() .where({ id: payload.id }) .set({ item: { id: payload.data.itemId }, code: payload.data.code, assignedToId: payload.data.assignedToId }) .execute(); return await this.find({ itemId: payload.data.itemId, id: payload.id }); } async delete(payload: DeleteStockAttr): Promise { const record = await StockRecord.createQueryBuilder() .update() .where({ id: payload.id }) .set({ available: false }) .execute(); return record.affected === 1; } async assign(payload: AssignStockAttr): Promise { const dataSource = container.resolve("Marketplace:DataSource"); const queryRunner = dataSource.createQueryRunner(); try { await queryRunner.connect(); await queryRunner.startTransaction(); const record = await this.findAvailableStock(payload.itemId); if (!record) throw Error("Sin stock"); await StockRecord .createQueryBuilder() .update(StockRecord, { assignedToId: payload.userId }) .where({ id: record.id }) .returning(["id"]) .updateEntity(true) .execute(); queryRunner.commitTransaction(); return await this.find({ itemId: payload.itemId, userId: payload.userId }); } catch (e) { await queryRunner.rollbackTransaction(); } finally { await queryRunner.release(); } } private async findAvailableStock(itemId: string): Promise { const record = await StockRecord .createQueryBuilder() .useTransaction(true) .setLock("pessimistic_write") .where("StockRecord.itemId = :itemId AND StockRecord.assignedToId IS NULL", { itemId: itemId }) .getOne(); return record; } async allAvailable(itemId: string): Promise { const records = await StockRecord .createQueryBuilder() .innerJoinAndSelect("StockRecord.item", "ItemRecord") .where("StockRecord.itemId = :itemId AND StockRecord.assignedToId IS NULL", { itemId: itemId }) .getMany(); return records.map(record => record.toEntity()); } }