Add additional lint rules

This commit is contained in:
Aerilyn Weber 2026-05-19 16:15:15 +09:00
parent 02d782c3da
commit 420b18eb78
67 changed files with 3686 additions and 1415 deletions

View file

@ -3,7 +3,8 @@ import type { RegimensService } from '../regimens/regimens.service.js';
import type { CabinetRepository } from '../cabinet/cabinet.repository.js';
import type { CabinetService } from '../cabinet/cabinet.service.js';
import type { MedicinePricesRepository } from '../medicine-prices/medicine-prices.repository.js';
import type { PurchasesRepository } from '../purchases/purchases.repository.js';
import type { ShoppingListsRepository } from '../shopping-lists/shopping-lists.repository.js';
import type { RefillListDocument } from '../../schemas/refill-list.schema.js';
import type {
CreateRefillListInput,
UpdateRefillListInput,
@ -19,7 +20,46 @@ interface Deps {
cabinetRepository: CabinetRepository;
cabinetService: CabinetService;
medicinePricesRepository: MedicinePricesRepository;
purchasesRepository: PurchasesRepository;
shoppingListsRepository: ShoppingListsRepository;
}
interface RefillAlert {
medicineId: string;
medicineName: string;
medicineStrength: number;
medicineStrengthUnit: string;
daysUntilEmpty: number;
dailyConsumption: number;
currentStock: number;
pendingOrderStock: number;
daysUntilEmptyWithOrders: number | null;
suggestedQuantity: number;
lastKnownPrice?: {
price: number;
pricePerUnit: number;
storeName: string;
storeId: string;
date: Date;
};
cheapestOption?: {
price: number;
pricePerUnit: number;
storeName: string;
storeId: string;
date: Date;
};
}
interface CabinetAggregateSummaryGroup {
_id: string;
medicineName: string;
medicineStrength: number;
medicineStrengthUnit: string;
medicineForm: string;
totalQuantity: number;
unit: string;
earliestExpiry: Date | null;
itemCount: number;
}
export class RefillsService {
@ -28,7 +68,7 @@ export class RefillsService {
private readonly cabinetRepository: CabinetRepository;
private readonly cabinetService: CabinetService;
private readonly medicinePricesRepository: MedicinePricesRepository;
private readonly purchasesRepository: PurchasesRepository;
private readonly shoppingListsRepository: ShoppingListsRepository;
public constructor({
refillsRepository,
@ -36,17 +76,21 @@ export class RefillsService {
cabinetRepository,
cabinetService,
medicinePricesRepository,
purchasesRepository,
shoppingListsRepository,
}: Deps) {
this.refillsRepository = refillsRepository;
this.regimensService = regimensService;
this.cabinetRepository = cabinetRepository;
this.cabinetService = cabinetService;
this.medicinePricesRepository = medicinePricesRepository;
this.purchasesRepository = purchasesRepository;
this.shoppingListsRepository = shoppingListsRepository;
}
public async getAlerts(householdId: string, userId: string, thresholdDays = 7) {
public async getAlerts(
householdId: string,
userId: string,
thresholdDays = 7,
): Promise<RefillAlert[]> {
const burnRates = await this.regimensService.calculateBurnRates(householdId, userId);
const triggered = burnRates.filter(
@ -56,23 +100,36 @@ export class RefillsService {
if (triggered.length === 0) return [];
// Get strength data from cabinet aggregate
const summaries = await this.cabinetRepository.getAggregateSummary(householdId);
const summariesRaw = await this.cabinetRepository.getAggregateSummary(householdId);
const summaries = summariesRaw as unknown as CabinetAggregateSummaryGroup[];
const summaryMap = new Map<
string,
{ medicineStrength: number; medicineStrengthUnit: string }
>();
for (const s of summaries) {
summaryMap.set(s._id as string, {
medicineStrength: s.medicineStrength as number,
medicineStrengthUnit: s.medicineStrengthUnit as string,
summaryMap.set(s._id, {
medicineStrength: s.medicineStrength,
medicineStrengthUnit: s.medicineStrengthUnit,
});
}
// Get pending stock from ordered purchases
const pendingStockRows = await this.purchasesRepository.getPendingMedicineStock(householdId);
// Get pending stock from active shopping lists
const activeLists = await this.shoppingListsRepository.findActiveByHousehold(householdId);
const pendingStockMap = new Map<string, number>();
for (const row of pendingStockRows) {
pendingStockMap.set(row.medicineId, row.totalUnits);
for (const list of activeLists) {
if (list.items) {
for (const item of list.items) {
const itemAny = item as unknown as {
productId?: string;
quantity: number;
checked: boolean;
};
if (itemAny.productId && !itemAny.checked) {
const currentQty = pendingStockMap.get(itemAny.productId) ?? 0;
pendingStockMap.set(itemAny.productId, currentQty + itemAny.quantity);
}
}
}
}
const alerts = await Promise.all(
@ -94,7 +151,7 @@ export class RefillsService {
medicineId: br.medicineId,
medicineName: br.medicineName,
medicineStrength: summary?.medicineStrength ?? 0,
medicineStrengthUnit: (summary?.medicineStrengthUnit ?? 'mg') as never,
medicineStrengthUnit: summary?.medicineStrengthUnit ?? 'mg',
daysUntilEmpty: br.daysUntilEmpty as number,
dailyConsumption: br.dailyConsumption,
currentStock: br.totalInCabinet,
@ -103,11 +160,11 @@ export class RefillsService {
suggestedQuantity,
lastKnownPrice: latestRecord
? {
price: latestRecord.price as number,
pricePerUnit: latestRecord.pricePerUnit as number,
storeName: latestRecord.storeName as string,
storeId: latestRecord.storeId as string,
date: latestRecord.date as Date,
price: latestRecord.price,
pricePerUnit: latestRecord.pricePerUnit,
storeName: latestRecord.storeName,
storeId: latestRecord.storeId,
date: latestRecord.date,
}
: undefined,
cheapestOption:
@ -127,7 +184,11 @@ export class RefillsService {
return alerts;
}
public async createList(data: CreateRefillListInput, householdId: string, userId: string) {
public async createList(
data: CreateRefillListInput,
householdId: string,
userId: string,
): Promise<RefillListDocument> {
let items: Array<{
medicineId: string;
medicineName: string;
@ -144,7 +205,7 @@ export class RefillsService {
medicineId: alert.medicineId,
medicineName: alert.medicineName,
quantity: alert.suggestedQuantity,
unit: 'tablet' as string,
unit: 'tablet',
estimatedPrice: alert.cheapestOption?.price ?? alert.lastKnownPrice?.price,
storeId: alert.cheapestOption?.storeId ?? alert.lastKnownPrice?.storeId,
}));
@ -153,7 +214,7 @@ export class RefillsService {
medicineId: item.medicineId,
medicineName: item.medicineName,
quantity: item.quantity,
unit: item.unit as string,
unit: item.unit,
estimatedPrice: item.estimatedPrice,
storeId: item.storeId,
notes: item.notes,
@ -176,17 +237,27 @@ export class RefillsService {
});
}
public async list(householdId: string, query: RefillListQueryInput) {
public async list(
householdId: string,
query: RefillListQueryInput,
): Promise<{
data: RefillListDocument[];
pagination: { cursor: string | null; hasMore: boolean };
}> {
return this.refillsRepository.findByHousehold(householdId, query);
}
public async getById(id: string, householdId: string) {
public async getById(id: string, householdId: string): Promise<RefillListDocument> {
const list = await this.refillsRepository.findById(id, householdId);
if (!list) throw new NotFoundError('Refill list not found');
return list;
}
public async updateList(id: string, householdId: string, data: UpdateRefillListInput) {
public async updateList(
id: string,
householdId: string,
data: UpdateRefillListInput,
): Promise<RefillListDocument> {
await this.getById(id, householdId);
const updated = await this.refillsRepository.update(id, householdId, data);
if (!updated) throw new NotFoundError('Refill list not found');
@ -198,7 +269,7 @@ export class RefillsService {
householdId: string,
itemId: string,
data: UpdateRefillListItemInput,
) {
): Promise<RefillListDocument> {
await this.getById(listId, householdId);
const updateData: UpdateRefillListItemInput & { checkedAt?: Date } = { ...data };
@ -216,7 +287,11 @@ export class RefillsService {
return updated;
}
public async addToCabinet(listId: string, householdId: string, userId: string) {
public async addToCabinet(
listId: string,
householdId: string,
userId: string,
): Promise<{ addedCount: number; priceRecordsCreated: number }> {
const list = await this.getById(listId, householdId);
const checkedItems = (
@ -266,7 +341,23 @@ export class RefillsService {
return { addedCount, priceRecordsCreated: 0 };
}
public async getStoreComparison(listId: string, householdId: string) {
public async getStoreComparison(
listId: string,
householdId: string,
): Promise<
Array<{
medicineId: string;
storeOptions: Array<{
storeId: string;
storeName: string;
latestPrice: number;
latestPricePerUnit: number;
currency: string;
date: Date;
isInsurancePrice: boolean;
}>;
}>
> {
const list = await this.getById(listId, householdId);
const medicineIds = [