feat(prices): fiyat kayıtları, birim fiyat motoru ve analizde aylık maliyet
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,7 @@ import {
|
||||
} from "@/features/categories/tree";
|
||||
import { hasHouseholdRole } from "@/features/households/roles";
|
||||
import { resolveMembership, type Membership } from "@/features/households/service";
|
||||
import { latestTypeUnitPrices } from "@/features/prices/service";
|
||||
import { tr } from "@/i18n/tr";
|
||||
import { fail, ok, type ActionResult } from "@/lib/action-result";
|
||||
import { writeAudit } from "@/lib/audit";
|
||||
@@ -24,6 +25,7 @@ import { StockCountModel } from "@/models/stock-count";
|
||||
import { StockMovementModel } from "@/models/stock-movement";
|
||||
import { todayIstanbul } from "@/shared/dates";
|
||||
import { shiftMonth } from "@/shared/budget";
|
||||
import { QUANTITY_SCALE } from "@/shared/quantity";
|
||||
import type {
|
||||
ProductTypeForm,
|
||||
StockCategoryForm,
|
||||
@@ -1242,6 +1244,10 @@ export type StockAnalysisRow = {
|
||||
avgMonthlyWasted: number | null;
|
||||
/** Fire / (kullanım + fire), %. */
|
||||
wastePct: number | null;
|
||||
/** Son bilinen birim fiyat (ürün tipi biriminde, kesirli kuruş) ve tarihi. */
|
||||
unitPrice: { minor: number; date: string } | null;
|
||||
/** Aylık ortalama maliyet tahmini: ortalama tüketim × son birim fiyat (kuruş). */
|
||||
avgMonthlyCostMinor: number | null;
|
||||
/** Bu ay (bugüne kadar). */
|
||||
thisMonth: MonthUsage;
|
||||
};
|
||||
@@ -1264,10 +1270,11 @@ export async function getStockAnalysis(
|
||||
shiftMonth(thisMonth, i - ANALYSIS_MONTHS),
|
||||
);
|
||||
const m = (await resolveMembership(userId, householdId, "viewer"))!;
|
||||
const moves = await loadMoves(
|
||||
m.householdId,
|
||||
data.items.map((i) => new Types.ObjectId(i.id)),
|
||||
);
|
||||
const ids = data.items.map((i) => new Types.ObjectId(i.id));
|
||||
const [moves, prices] = await Promise.all([
|
||||
loadMoves(m.householdId, ids),
|
||||
latestTypeUnitPrices(m.householdId, ids, data.today),
|
||||
]);
|
||||
const rows = data.items.map((item): StockAnalysisRow => {
|
||||
const list = moves.get(item.id) ?? [];
|
||||
const [usage, current] = [monthlyUsage(list, months), monthlyUsage(list, [thisMonth])[0]!];
|
||||
@@ -1277,6 +1284,8 @@ export async function getStockAnalysis(
|
||||
const sum = (k: "used" | "wasted") => full.reduce((s, u) => s + u[k], 0);
|
||||
const used = sum("used");
|
||||
const wasted = sum("wasted");
|
||||
const price = prices.get(item.id) ?? null;
|
||||
const avgUsed = full.length ? used / full.length : null;
|
||||
return {
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
@@ -1287,6 +1296,11 @@ export async function getStockAnalysis(
|
||||
avgMonthlyUsed: full.length ? roundForDisplay(used / full.length) : null,
|
||||
avgMonthlyWasted: full.length ? roundForDisplay(wasted / full.length) : null,
|
||||
wastePct: used + wasted > 0 ? Math.round((wasted * 100) / (used + wasted)) : null,
|
||||
unitPrice: price ? { minor: price.unitPriceMinor, date: price.date } : null,
|
||||
avgMonthlyCostMinor:
|
||||
price && avgUsed !== null
|
||||
? Math.round((avgUsed * price.unitPriceMinor) / QUANTITY_SCALE)
|
||||
: null,
|
||||
thisMonth: current,
|
||||
};
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user