142 lines
4.1 KiB
TypeScript
142 lines
4.1 KiB
TypeScript
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<string, unknown> = { 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<string, unknown> = {};
|
|
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<string, unknown> = {};
|
|
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();
|
|
}
|
|
}
|