2026-04-18 12:36:29 +09:00
|
|
|
import type { MedicinePricesRepository } from './medicine-prices.repository.js';
|
|
|
|
|
import type { MedicineProductsRepository } from '../medicine-products/medicine-products.repository.js';
|
|
|
|
|
import type { StoresRepository } from '../stores/stores.repository.js';
|
|
|
|
|
import type {
|
|
|
|
|
CreateMedicinePriceRecordInput,
|
|
|
|
|
MedicinePriceHistoryQueryInput,
|
|
|
|
|
MedicinePriceAnalyticsQueryInput,
|
|
|
|
|
} from '@meshitrack/shared';
|
|
|
|
|
import { NotFoundError } from '../../common/errors.js';
|
|
|
|
|
|
|
|
|
|
interface Deps {
|
|
|
|
|
medicinePricesRepository: MedicinePricesRepository;
|
|
|
|
|
medicineProductsRepository: MedicineProductsRepository;
|
|
|
|
|
storesRepository: StoresRepository;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export class MedicinePricesService {
|
|
|
|
|
private readonly medicinePricesRepository: MedicinePricesRepository;
|
|
|
|
|
private readonly medicineProductsRepository: MedicineProductsRepository;
|
|
|
|
|
private readonly storesRepository: StoresRepository;
|
|
|
|
|
|
2026-04-26 18:44:59 +09:00
|
|
|
public constructor({
|
|
|
|
|
medicinePricesRepository,
|
|
|
|
|
medicineProductsRepository,
|
|
|
|
|
storesRepository,
|
|
|
|
|
}: Deps) {
|
2026-04-18 12:36:29 +09:00
|
|
|
this.medicinePricesRepository = medicinePricesRepository;
|
|
|
|
|
this.medicineProductsRepository = medicineProductsRepository;
|
|
|
|
|
this.storesRepository = storesRepository;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async recordPrice(
|
|
|
|
|
data: CreateMedicinePriceRecordInput,
|
|
|
|
|
householdId: string,
|
|
|
|
|
userId: string,
|
|
|
|
|
) {
|
|
|
|
|
const product = await this.medicineProductsRepository.findById(
|
|
|
|
|
data.medicineProductId,
|
|
|
|
|
householdId,
|
|
|
|
|
);
|
|
|
|
|
if (!product) throw new NotFoundError('Medicine product not found');
|
|
|
|
|
|
|
|
|
|
const store = await this.storesRepository.findById(data.storeId, householdId);
|
|
|
|
|
if (!store) throw new NotFoundError('Store not found');
|
|
|
|
|
|
|
|
|
|
const pricePerUnit = data.price / data.quantity;
|
|
|
|
|
const date = data.date ? new Date(data.date) : new Date();
|
|
|
|
|
|
|
|
|
|
return this.medicinePricesRepository.create({
|
|
|
|
|
householdId,
|
|
|
|
|
medicineProductId: data.medicineProductId,
|
|
|
|
|
medicineProductBrand: product.brand ?? product.medicineName,
|
|
|
|
|
medicineId: data.medicineId,
|
|
|
|
|
medicineName: product.medicineName,
|
|
|
|
|
storeId: data.storeId,
|
|
|
|
|
storeName: store.name,
|
|
|
|
|
price: data.price,
|
|
|
|
|
currency: data.currency,
|
|
|
|
|
quantity: data.quantity,
|
|
|
|
|
unit: data.unit,
|
|
|
|
|
pricePerUnit,
|
|
|
|
|
date,
|
|
|
|
|
isInsurancePrice: data.isInsurancePrice,
|
|
|
|
|
notes: data.notes,
|
|
|
|
|
createdBy: userId,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async getPriceHistory(
|
|
|
|
|
householdId: string,
|
|
|
|
|
medicineId: string,
|
|
|
|
|
query: MedicinePriceHistoryQueryInput,
|
|
|
|
|
) {
|
|
|
|
|
return this.medicinePricesRepository.findByMedicine(householdId, medicineId, query);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async compareStores(householdId: string, medicineId: string) {
|
|
|
|
|
return this.medicinePricesRepository.compareStores(householdId, medicineId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async estimatePrice(
|
|
|
|
|
householdId: string,
|
|
|
|
|
medicineId: string,
|
|
|
|
|
storeId?: string,
|
|
|
|
|
): Promise<number | null> {
|
|
|
|
|
const record = await this.medicinePricesRepository.getLatestForMedicine(
|
|
|
|
|
householdId,
|
|
|
|
|
medicineId,
|
|
|
|
|
storeId,
|
|
|
|
|
);
|
|
|
|
|
return record ? (record.pricePerUnit as number) : null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async getAnalytics(householdId: string, query: MedicinePriceAnalyticsQueryInput) {
|
|
|
|
|
return this.medicinePricesRepository.getAnalytics(householdId, query);
|
|
|
|
|
}
|
|
|
|
|
}
|