Implement medicine library and cabinet
This commit is contained in:
parent
db79af06f7
commit
1f66fab30f
72 changed files with 7642 additions and 319 deletions
144
packages/api/src/modules/cabinet/cabinet.repository.ts
Normal file
144
packages/api/src/modules/cabinet/cabinet.repository.ts
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
import { CabinetItemModel } from '../../schemas/cabinet-item.schema.js';
|
||||
import type { CabinetItemStatus, CreateCabinetItemInput, UpdateCabinetItemInput } from '@meshitrack/shared';
|
||||
|
||||
interface FindByHouseholdQuery {
|
||||
medicineId?: string;
|
||||
status?: CabinetItemStatus;
|
||||
expiringWithin?: number;
|
||||
cursor?: string;
|
||||
limit: number;
|
||||
}
|
||||
|
||||
export class CabinetRepository {
|
||||
public async findByHousehold(householdId: string, query: FindByHouseholdQuery) {
|
||||
const filter: Record<string, unknown> = { householdId, isDeleted: false };
|
||||
|
||||
if (query.medicineId) filter['medicineId'] = query.medicineId;
|
||||
if (query.status) filter['status'] = query.status;
|
||||
|
||||
if (query.expiringWithin) {
|
||||
const cutoff = new Date();
|
||||
cutoff.setDate(cutoff.getDate() + query.expiringWithin);
|
||||
filter['expirationDate'] = { $lte: cutoff, $gt: new Date() };
|
||||
filter['status'] = 'active';
|
||||
}
|
||||
|
||||
if (query.cursor) {
|
||||
const id = Buffer.from(query.cursor, 'base64').toString();
|
||||
filter['_id'] = { $gt: id };
|
||||
}
|
||||
|
||||
const limit = query.limit;
|
||||
const items = await CabinetItemModel.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 CabinetItemModel.findOne({ _id: id, householdId, isDeleted: false }).lean().exec();
|
||||
}
|
||||
|
||||
public async getAggregateSummary(householdId: string) {
|
||||
return CabinetItemModel.aggregate([
|
||||
{ $match: { householdId, isDeleted: false, status: 'active' } },
|
||||
{
|
||||
$group: {
|
||||
_id: '$medicineId',
|
||||
medicineName: { $first: '$medicineName' },
|
||||
medicineStrength: { $first: '$medicineStrength' },
|
||||
medicineStrengthUnit: { $first: '$medicineStrengthUnit' },
|
||||
medicineForm: { $first: '$medicineForm' },
|
||||
totalQuantity: { $sum: '$quantity' },
|
||||
unit: { $first: '$unit' },
|
||||
earliestExpiry: { $min: '$expirationDate' },
|
||||
itemCount: { $sum: 1 },
|
||||
},
|
||||
},
|
||||
{ $sort: { medicineName: 1 } },
|
||||
]).exec();
|
||||
}
|
||||
|
||||
public async create(
|
||||
data: CreateCabinetItemInput & {
|
||||
medicineName: string;
|
||||
medicineStrength: number;
|
||||
medicineStrengthUnit: string;
|
||||
medicineForm: string;
|
||||
medicineProductBrand?: string;
|
||||
concentration?: number;
|
||||
concentrationUnit?: string;
|
||||
},
|
||||
householdId: string,
|
||||
createdBy: string,
|
||||
) {
|
||||
const item = new CabinetItemModel({ ...data, householdId, createdBy });
|
||||
const saved = await item.save();
|
||||
return saved.toObject();
|
||||
}
|
||||
|
||||
public async update(id: string, householdId: string, data: UpdateCabinetItemInput) {
|
||||
return CabinetItemModel.findOneAndUpdate(
|
||||
{ _id: id, householdId, isDeleted: false },
|
||||
{ $set: data },
|
||||
{ new: true, lean: true },
|
||||
).exec();
|
||||
}
|
||||
|
||||
public async adjustQuantity(id: string, householdId: string, delta: number) {
|
||||
const item = await CabinetItemModel.findOne({
|
||||
_id: id,
|
||||
householdId,
|
||||
isDeleted: false,
|
||||
})
|
||||
.lean()
|
||||
.exec();
|
||||
|
||||
if (!item) return null;
|
||||
|
||||
const newQuantity = Math.max(0, item.quantity + delta);
|
||||
const newStatus =
|
||||
newQuantity === 0 ? 'depleted' : item.status === 'depleted' ? 'active' : item.status;
|
||||
|
||||
return CabinetItemModel.findOneAndUpdate(
|
||||
{ _id: id, householdId, isDeleted: false },
|
||||
{ $set: { quantity: newQuantity, status: newStatus } },
|
||||
{ new: true, lean: true },
|
||||
).exec();
|
||||
}
|
||||
|
||||
public async findExpiringSoon(householdId: string, withinDays: number) {
|
||||
const cutoff = new Date();
|
||||
cutoff.setDate(cutoff.getDate() + withinDays);
|
||||
|
||||
return CabinetItemModel.find({
|
||||
householdId,
|
||||
isDeleted: false,
|
||||
status: 'active',
|
||||
expirationDate: { $lte: cutoff, $gt: new Date() },
|
||||
})
|
||||
.sort({ expirationDate: 1 })
|
||||
.lean()
|
||||
.exec();
|
||||
}
|
||||
|
||||
public async countByMedicineId(medicineId: string): Promise<number> {
|
||||
return CabinetItemModel.countDocuments({ medicineId, isDeleted: false }).exec();
|
||||
}
|
||||
|
||||
public async softDelete(id: string, householdId: string) {
|
||||
return CabinetItemModel.findOneAndUpdate(
|
||||
{ _id: id, householdId, isDeleted: false },
|
||||
{ $set: { isDeleted: true } },
|
||||
{ new: true, lean: true },
|
||||
).exec();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue