feat(stock): sayım oturumları, fire ayarlı öneri motoru ve tüketim analizi
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
import { Types } from "mongoose";
|
||||
import { StockCountModel } from "@/models/stock-count";
|
||||
import { StockMovementModel } from "@/models/stock-movement";
|
||||
import { signedDelta, type StockMovementKind } from "@/shared/stock";
|
||||
import type { Migration } from "./types";
|
||||
|
||||
type Move = {
|
||||
_id: Types.ObjectId;
|
||||
householdId: Types.ObjectId;
|
||||
productTypeId: Types.ObjectId;
|
||||
kind: StockMovementKind;
|
||||
date: string;
|
||||
quantity: number;
|
||||
initial: boolean;
|
||||
countId: Types.ObjectId | null;
|
||||
createdBy: Types.ObjectId;
|
||||
createdAt: Date;
|
||||
};
|
||||
|
||||
/**
|
||||
* Faz 11b: sayım oturumları. Faz 11a'da tek ürün sayımıyla oluşan (oturumsuz) sayım
|
||||
* düzeltmeleri, o ana kadarki kayıtlardan beklenen miktar hesaplanarak hane + tarih + kişi
|
||||
* başına bir sayım oturumuna bağlanır. Başlangıç miktarları sayım sayılmaz.
|
||||
*/
|
||||
export const migration: Migration = {
|
||||
name: "012-stock-counts",
|
||||
description: "stockCounts index'leri; oturumsuz sayım düzeltmelerini sayım oturumuna bağla",
|
||||
async up() {
|
||||
await StockCountModel.createIndexes();
|
||||
const orphans = await StockMovementModel.find({
|
||||
kind: "count_adjust",
|
||||
initial: { $ne: true },
|
||||
countId: null,
|
||||
deletedAt: null,
|
||||
}).lean<Move[]>();
|
||||
if (orphans.length === 0) return;
|
||||
const products = [...new Set(orphans.map((o) => String(o.productTypeId)))];
|
||||
const all = await StockMovementModel.find({
|
||||
productTypeId: { $in: products.map((id) => new Types.ObjectId(id)) },
|
||||
deletedAt: null,
|
||||
}).lean<Move[]>();
|
||||
const byProduct = new Map<string, Move[]>();
|
||||
for (const m of all) {
|
||||
const list = byProduct.get(String(m.productTypeId)) ?? [];
|
||||
list.push(m);
|
||||
byProduct.set(String(m.productTypeId), list);
|
||||
}
|
||||
// Her düzeltme için beklenen = önceki hareketlerin toplamı (tarih, oluşturulma sırası).
|
||||
const expectedOf = new Map<string, number>();
|
||||
for (const list of byProduct.values()) {
|
||||
list.sort((a, b) =>
|
||||
a.date === b.date
|
||||
? a.createdAt.getTime() - b.createdAt.getTime()
|
||||
: a.date < b.date
|
||||
? -1
|
||||
: 1,
|
||||
);
|
||||
let running = 0;
|
||||
for (const m of list) {
|
||||
expectedOf.set(String(m._id), running);
|
||||
running += signedDelta(m);
|
||||
}
|
||||
}
|
||||
const sessions = new Map<string, Move[]>();
|
||||
for (const o of orphans) {
|
||||
const key = `${o.householdId}:${o.date}:${o.createdBy}`;
|
||||
sessions.set(key, [...(sessions.get(key) ?? []), o]);
|
||||
}
|
||||
for (const moves of sessions.values()) {
|
||||
const first = moves[0]!;
|
||||
const count = await StockCountModel.create({
|
||||
householdId: first.householdId,
|
||||
date: first.date,
|
||||
categoryId: null,
|
||||
items: moves.map((m) => {
|
||||
const expected = expectedOf.get(String(m._id)) ?? 0;
|
||||
return {
|
||||
productTypeId: m.productTypeId,
|
||||
expected,
|
||||
actual: expected + m.quantity,
|
||||
wasted: 0,
|
||||
};
|
||||
}),
|
||||
createdBy: first.createdBy,
|
||||
});
|
||||
await StockMovementModel.updateMany(
|
||||
{ _id: { $in: moves.map((m) => m._id) } },
|
||||
{ $set: { countId: count._id } },
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -11,6 +11,7 @@ import { migration as m008 } from "./008-in-kind";
|
||||
import { migration as m009 } from "./009-assets";
|
||||
import { migration as m010 } from "./010-budgets";
|
||||
import { migration as m011 } from "./011-stock";
|
||||
import { migration as m012 } from "./012-stock-counts";
|
||||
import type { Migration } from "./types";
|
||||
|
||||
/** Sıralı migration listesi. Yeni migration: dosyayı ekle ve buraya sırayla ekle. */
|
||||
@@ -26,6 +27,7 @@ export const MIGRATIONS: Migration[] = [
|
||||
m009,
|
||||
m010,
|
||||
m011,
|
||||
m012,
|
||||
];
|
||||
|
||||
const COLLECTION = "migrations";
|
||||
|
||||
Reference in New Issue
Block a user