import { PurchaseModel } from '../../schemas/purchase.schema.js'; import type { PurchaseQueryInput, UpdatePurchaseInput } from '@meshitrack/shared'; export interface CreatePurchaseItemData { medicineProductId?: string; medicineId?: string; foodProductId?: string; name: string; quantity: number; unit: string; actualPrice?: number; currency?: string; priceRecordId?: string; addedToCabinet?: boolean; } export interface CreatePurchaseData { householdId: string; storeId: string; storeName: string; status: string; items: CreatePurchaseItemData[]; notes?: string; purchasedAt: Date; createdBy: string; } export class PurchasesRepository { public async create(data: CreatePurchaseData) { const purchase = new PurchaseModel(data); const saved = await purchase.save(); return saved.toObject(); } public async findByHousehold(householdId: string, query: PurchaseQueryInput) { const filter: Record = { householdId, isDeleted: false }; if (query.status) filter['status'] = query.status; if (query.storeId) filter['storeId'] = query.storeId; if (query.cursor) { const id = Buffer.from(query.cursor, 'base64').toString(); filter['_id'] = { $lt: id }; } const limit = query.limit; const items = await PurchaseModel.find(filter) .sort({ _id: -1 }) .limit(limit + 1) .lean() .exec(); const hasMore = items.length > limit; const data = hasMore ? items.slice(0, limit) : items; const cursor = data.length > 0 ? Buffer.from(data[data.length - 1]._id.toString()).toString('base64') : null; return { data, pagination: { cursor: hasMore ? cursor : null, hasMore } }; } public async findById(id: string, householdId: string) { return PurchaseModel.findOne({ _id: id, householdId, isDeleted: false }).lean().exec(); } public async update(id: string, householdId: string, data: UpdatePurchaseInput) { const updateSet: Record = {}; if (data.notes !== undefined) updateSet['notes'] = data.notes; if (data.items !== undefined) updateSet['items'] = data.items; return PurchaseModel.findOneAndUpdate( { _id: id, householdId, isDeleted: false }, { $set: updateSet }, { new: true, lean: true }, ).exec(); } public async markItemsAddedToCabinet( purchaseId: string, householdId: string, itemIndices: number[], ) { // Build update using positional array filters const updateSet: Record = {}; for (const idx of itemIndices) { updateSet[`items.${idx}.addedToCabinet`] = true; } return PurchaseModel.findOneAndUpdate( { _id: purchaseId, householdId, isDeleted: false }, { $set: updateSet }, { new: true, lean: true }, ).exec(); } public async receiveAll(purchaseId: string, householdId: string) { return PurchaseModel.findOneAndUpdate( { _id: purchaseId, householdId, isDeleted: false }, { $set: { status: 'in_cabinet', receivedAt: new Date(), 'items.$[].addedToCabinet': true, }, }, { new: true, lean: true }, ).exec(); } public async getPendingMedicineStock( householdId: string, ): Promise<{ medicineId: string; totalUnits: number }[]> { const results = await PurchaseModel.aggregate([ { $match: { householdId, status: 'ordered', isDeleted: false } }, { $unwind: '$items' }, { $match: { 'items.medicineId': { $exists: true, $ne: null }, 'items.addedToCabinet': false, }, }, { $group: { _id: '$items.medicineId', totalUnits: { $sum: '$items.quantity' }, }, }, { $project: { _id: 0, medicineId: '$_id', totalUnits: 1 } }, ]).exec(); return results as { medicineId: string; totalUnits: number }[]; } public async softDelete(id: string, householdId: string) { return PurchaseModel.findOneAndUpdate( { _id: id, householdId, isDeleted: false, status: 'ordered' }, { $set: { isDeleted: true } }, { new: true, lean: true }, ).exec(); } }