feat: add salePrice field to Product model and update related files
- Added salePrice field to Product model in Prisma schema and generated files. - Updated Product scalar field enums in both TypeScript files. - Modified aggregate output types to include salePrice. - Enhanced input types for creating and updating products to support salePrice. - Updated migration file to add salePrice column to Products table. - Created DTOs for inventory management in POS module. - Implemented POS controller and service with methods for stock and product categories.
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
import { Injectable } from '@nestjs/common'
|
||||
import { ResponseMapper } from '../common/response/response-mapper'
|
||||
import { Prisma } from '../generated/prisma/client'
|
||||
import { PrismaService } from '../prisma/prisma.service'
|
||||
|
||||
@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 getInfo() {
|
||||
const info = await this.prisma.inventory.findFirst({
|
||||
where: { isPointOfSale: true },
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
},
|
||||
})
|
||||
|
||||
return ResponseMapper.single({ store: info })
|
||||
}
|
||||
|
||||
async getStock(inventoryId: number, isAvailable?: boolean, page = 1, pageSize = 50) {
|
||||
const query: Prisma.StockBalanceFindManyArgs = {
|
||||
where: {
|
||||
inventoryId,
|
||||
quantity:
|
||||
isAvailable === true
|
||||
? { gt: 0 }
|
||||
: isAvailable === false
|
||||
? { lte: 0 }
|
||||
: undefined,
|
||||
},
|
||||
}
|
||||
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(inventoryId: number) {
|
||||
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 createSaleInvoice(data: any) {
|
||||
const item = await this.prisma.salesInvoice.create({ data })
|
||||
return ResponseMapper.create(item)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user