import { Injectable } from '@nestjs/common' import { ResponseMapper } from '../../common/response/response-mapper' import { Prisma } from '../../generated/prisma/client' import { PrismaService } from '../../prisma/prisma.service' import { CreateOrderDto } from './dto/create-pos.dto' @Injectable() export class PosService { constructor(private prisma: PrismaService) {} async getDefaultInventoryId() { const inventory = await this.prisma.inventory.findFirst({ where: { isPointOfSale: true }, select: { id: true }, }) return inventory?.id || null } async getDefaultPosId(inventoryId: number) { const pos = await this.prisma.posAccount.findFirst({ where: { inventoryId }, select: { id: true }, }) return pos?.id || null } async getInfo(posId: number) { const info = await this.prisma.posAccount.findUniqueOrThrow({ where: { id: posId }, include: { inventory: { select: { id: true, name: true, }, }, bankAccount: { select: { id: true, name: true, accountNumber: true, branch: { select: { id: true, name: true, bank: { select: { id: true, name: true } }, }, }, }, }, }, }) return ResponseMapper.single({ ...info }) } async getStock( posId: number, options: { isAvailable?: boolean page?: number pageSize?: number q?: string } = {}, ) { const { isAvailable, page = 1, pageSize = 50, q } = options const pos = await this.prisma.posAccount.findUniqueOrThrow({ where: { id: posId }, select: { inventoryId: true }, }) const inventoryId = pos.inventoryId const query: Prisma.StockBalanceFindManyArgs = { where: { inventoryId, quantity: isAvailable === true ? { gt: 0 } : isAvailable === false ? { lte: 0 } : undefined, ...(q && { product: { OR: [{ name: { contains: q } }, { sku: { contains: q } }], }, }), }, orderBy: { quantity: 'desc', }, } const [items, count] = await this.prisma.$transaction([ this.prisma.stockBalance.findMany({ where: query.where, include: { product: { select: { id: true, name: true, sku: true, salePrice: true, category: { select: { id: true, name: true }, }, }, }, }, orderBy: { quantity: 'desc', }, skip: (page - 1) * pageSize, take: pageSize, }), this.prisma.stockBalance.count({ where: query.where }), ]) const mapped = items.map(item => ({ id: item.id, quantity: Number(item.quantity), product: item.product, })) return ResponseMapper.paginate(mapped, count, page, pageSize) } async getProductCategories(posId: number) { const pos = await this.prisma.posAccount.findUniqueOrThrow({ where: { id: posId }, select: { inventoryId: true }, }) const inventoryId = pos.inventoryId const balances = await this.prisma.stockBalance.findMany({ where: { inventoryId }, select: { product: { select: { id: true, category: { select: { id: true, name: true } } } }, quantity: true, }, }) const map = new Map< number, { id: number; name: string; totalQuantity: number; productCount: number } >() for (const b of balances) { const product = b.product const cat = product?.category if (!cat) continue // skip products without category const existing = map.get(cat.id) if (existing) { existing.totalQuantity += Number(b.quantity) existing.productCount += 1 } else { map.set(cat.id, { id: cat.id, name: cat.name, totalQuantity: Number(b.quantity), productCount: 1, }) } } const categories = Array.from(map.values()) return ResponseMapper.list(categories) } async createOrder(data: CreateOrderDto, inventoryId: number) { const { customerId, ...res } = data const preparedOrder = { ...res } as any const lastCode = await this.prisma.salesInvoice .findFirst({ orderBy: { code: 'desc' }, select: { code: true }, }) .then(si => si?.code || '0') preparedOrder.code = String(Number(lastCode) + 1) preparedOrder.totalAmount = data.items.reduce( (acc, item) => acc + Number(item.fee), 0, ) preparedOrder.inventory = { connect: { id: inventoryId } } preparedOrder.customer = { connect: { id: customerId } } if (Object.prototype.hasOwnProperty.call(preparedOrder, 'items')) { preparedOrder.items = { create: preparedOrder.items.map((item: any) => ({ product: { connect: { id: Number(item.productId) } }, count: item.count, fee: item.fee, total: item.count * item.fee, })), } } const item = await this.prisma.salesInvoice.create({ data: preparedOrder }) return ResponseMapper.create(item) } }