f0b570284e
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
329 lines
7.8 KiB
TypeScript
329 lines
7.8 KiB
TypeScript
import { Types } from "mongoose";
|
||
import { ProductModel } from "@/models/product";
|
||
import { ProductTypeModel } from "@/models/product-type";
|
||
import { StoreModel } from "@/models/store";
|
||
import type { MealCardProvider } from "@/shared/accounts";
|
||
import { autoConvertible } from "@/shared/products";
|
||
import { foldTr } from "@/shared/search";
|
||
import type { StoreKind } from "@/shared/stores";
|
||
|
||
/**
|
||
* Örnek mağazalar ve ürünler (Faz 12). Çakır Ailesi (Kadıköy) için marketler, pazar ve online
|
||
* platform; stok ürün tiplerine bağlı marka/paketler. Elle dönüşüm gereken örnekler: deterjan
|
||
* (kg → yıkama), tablet paketi (paket → tablet), diş macunu/şampuan (ml → adet). Görsel yok.
|
||
* Idempotent (`seedRef`). Miktarlar binde bir, tutarlar kuruş.
|
||
*/
|
||
|
||
type SeedStore = {
|
||
ref: string;
|
||
name: string;
|
||
kind: StoreKind;
|
||
branch?: string;
|
||
distanceM?: number;
|
||
carry: number;
|
||
deliveryFee?: number;
|
||
freeDeliveryMin?: number;
|
||
mealCards?: MealCardProvider[];
|
||
note?: string;
|
||
};
|
||
|
||
export const STORES: SeedStore[] = [
|
||
{
|
||
ref: "migros",
|
||
name: "Migros",
|
||
kind: "market",
|
||
branch: "Bahariye",
|
||
distanceM: 1_200,
|
||
carry: 2,
|
||
mealCards: ["pluxee", "multinet", "edenred", "setcard"],
|
||
},
|
||
{ ref: "a101", name: "A101", kind: "market", branch: "Moda", distanceM: 400, carry: 1 },
|
||
{ ref: "bim", name: "BİM", kind: "market", branch: "Yeldeğirmeni", distanceM: 650, carry: 2 },
|
||
{
|
||
ref: "sok",
|
||
name: "Şok",
|
||
kind: "market",
|
||
branch: "Rasimpaşa",
|
||
distanceM: 300,
|
||
carry: 1,
|
||
mealCards: ["multinet", "pluxee"],
|
||
},
|
||
{
|
||
ref: "carrefoursa",
|
||
name: "CarrefourSA",
|
||
kind: "market",
|
||
branch: "Acıbadem",
|
||
distanceM: 3_800,
|
||
carry: 4,
|
||
mealCards: ["pluxee", "multinet", "edenred", "setcard", "metropol"],
|
||
note: "Hafta sonu arabayla; büyük alışveriş.",
|
||
},
|
||
{
|
||
ref: "sali-pazari",
|
||
name: "Salı Pazarı",
|
||
kind: "bazaar",
|
||
branch: "Kadıköy",
|
||
distanceM: 1_900,
|
||
carry: 4,
|
||
note: "Salı ve cuma; sebze-meyve.",
|
||
},
|
||
{
|
||
ref: "getir-buyuk",
|
||
name: "Getir Büyük",
|
||
kind: "online",
|
||
carry: 1,
|
||
deliveryFee: 39_90,
|
||
freeDeliveryMin: 750_00,
|
||
mealCards: ["multinet"],
|
||
},
|
||
{
|
||
ref: "metro",
|
||
name: "Metro Toptancı",
|
||
kind: "market",
|
||
branch: "Kozyatağı",
|
||
distanceM: 9_500,
|
||
carry: 5,
|
||
},
|
||
];
|
||
|
||
type SeedProduct = {
|
||
ref: string;
|
||
/** Stok seed'indeki ürün tipi `seedRef`'i. */
|
||
type: string;
|
||
brand: string;
|
||
name?: string;
|
||
barcode?: string;
|
||
qty: number;
|
||
unit: string;
|
||
/** Elle dönüşüm (bir paket = bu kadar ürün tipi birimi). */
|
||
typeQty?: number;
|
||
};
|
||
|
||
const K = 1000;
|
||
|
||
export const PRODUCTS: SeedProduct[] = [
|
||
{
|
||
ref: "ariel-5kg",
|
||
type: "toz-deterjan",
|
||
brand: "Ariel",
|
||
name: "Matik Dağ Esintisi",
|
||
qty: 5 * K,
|
||
unit: "kg",
|
||
typeQty: 33 * K,
|
||
barcode: "8690000100015",
|
||
},
|
||
{
|
||
ref: "omo-6kg",
|
||
type: "toz-deterjan",
|
||
brand: "Omo",
|
||
name: "Matik Active Fresh",
|
||
qty: 6 * K,
|
||
unit: "kg",
|
||
typeQty: 40 * K,
|
||
},
|
||
{
|
||
ref: "bingo-4kg",
|
||
type: "toz-deterjan",
|
||
brand: "Bingo",
|
||
name: "Matik",
|
||
qty: 4 * K,
|
||
unit: "kg",
|
||
typeQty: 26 * K,
|
||
},
|
||
{
|
||
ref: "fairy-50",
|
||
type: "tablet-deterjan",
|
||
brand: "Fairy",
|
||
name: "Platinum Plus",
|
||
qty: 1 * K,
|
||
unit: "paket",
|
||
typeQty: 50 * K,
|
||
barcode: "8690000100022",
|
||
},
|
||
{
|
||
ref: "finish-40",
|
||
type: "tablet-deterjan",
|
||
brand: "Finish",
|
||
name: "Quantum",
|
||
qty: 40 * K,
|
||
unit: "tablet",
|
||
},
|
||
{
|
||
ref: "yayla-mercimek",
|
||
type: "kirmizi-mercimek",
|
||
brand: "Yayla",
|
||
name: "Kırmızı Mercimek",
|
||
qty: 1 * K,
|
||
unit: "kg",
|
||
},
|
||
{
|
||
ref: "reis-mercimek",
|
||
type: "kirmizi-mercimek",
|
||
brand: "Reis",
|
||
name: "Kırmızı Mercimek",
|
||
qty: 1 * K,
|
||
unit: "kg",
|
||
},
|
||
{
|
||
ref: "duru-mercimek",
|
||
type: "kirmizi-mercimek",
|
||
brand: "Duru",
|
||
name: "Kırmızı Mercimek",
|
||
qty: 2_500,
|
||
unit: "kg",
|
||
},
|
||
{ ref: "yayla-baldo", type: "pirinc", brand: "Yayla", name: "Baldo", qty: 2_500, unit: "kg" },
|
||
{ ref: "reis-baldo", type: "pirinc", brand: "Reis", name: "Baldo", qty: 1 * K, unit: "kg" },
|
||
{
|
||
ref: "pinar-sut",
|
||
type: "sut",
|
||
brand: "Pınar",
|
||
name: "Tam Yağlı Süt",
|
||
qty: 1 * K,
|
||
unit: "lt",
|
||
barcode: "8690000100039",
|
||
},
|
||
{ ref: "sutas-sut", type: "sut", brand: "Sütaş", name: "Tam Yağlı Süt", qty: 1 * K, unit: "lt" },
|
||
{
|
||
ref: "icim-sut-500",
|
||
type: "sut",
|
||
brand: "İçim",
|
||
name: "Yarım Yağlı Süt",
|
||
qty: 500 * K,
|
||
unit: "ml",
|
||
},
|
||
{
|
||
ref: "keskinoglu-30",
|
||
type: "yumurta",
|
||
brand: "Keskinoğlu",
|
||
name: "L Boy 30'lu",
|
||
qty: 30 * K,
|
||
unit: "adet",
|
||
},
|
||
{
|
||
ref: "koy-yumurta-15",
|
||
type: "yumurta",
|
||
brand: "Köy Yumurtası",
|
||
name: "15'li",
|
||
qty: 15 * K,
|
||
unit: "adet",
|
||
},
|
||
{
|
||
ref: "pinar-peynir",
|
||
type: "beyaz-peynir",
|
||
brand: "Pınar",
|
||
name: "Beyaz Peynir",
|
||
qty: 500 * K,
|
||
unit: "g",
|
||
},
|
||
{
|
||
ref: "yumos-1440",
|
||
type: "yumusatici",
|
||
brand: "Yumoş",
|
||
name: "Extra Lilyum",
|
||
qty: 1_440 * K,
|
||
unit: "ml",
|
||
},
|
||
{
|
||
ref: "selpak-32",
|
||
type: "tuvalet-kagidi",
|
||
brand: "Selpak",
|
||
name: "32'li",
|
||
qty: 32 * K,
|
||
unit: "rulo",
|
||
},
|
||
{
|
||
ref: "solo-24",
|
||
type: "tuvalet-kagidi",
|
||
brand: "Solo",
|
||
name: "24'lü",
|
||
qty: 24 * K,
|
||
unit: "rulo",
|
||
},
|
||
{
|
||
ref: "colgate-75",
|
||
type: "dis-macunu",
|
||
brand: "Colgate",
|
||
name: "Total 75 ml",
|
||
qty: 75 * K,
|
||
unit: "ml",
|
||
typeQty: 1 * K,
|
||
},
|
||
{
|
||
ref: "sensodyne-75",
|
||
type: "dis-macunu",
|
||
brand: "Sensodyne",
|
||
name: "Repair 75 ml",
|
||
qty: 75 * K,
|
||
unit: "ml",
|
||
typeQty: 1 * K,
|
||
},
|
||
{
|
||
ref: "elidor-500",
|
||
type: "sampuan",
|
||
brand: "Elidor",
|
||
name: "500 ml",
|
||
qty: 500 * K,
|
||
unit: "ml",
|
||
typeQty: 1 * K,
|
||
},
|
||
];
|
||
|
||
export async function seedShopping(
|
||
householdIds: Map<string, Types.ObjectId>,
|
||
users: Map<string, string>,
|
||
): Promise<{ stores: number; products: number }> {
|
||
const actor = new Types.ObjectId(users.get("ayse")!);
|
||
const householdId = householdIds.get("Çakır Ailesi")!;
|
||
let stores = 0;
|
||
for (const s of STORES) {
|
||
if (await StoreModel.exists({ householdId, seedRef: s.ref })) continue;
|
||
await StoreModel.create({
|
||
householdId,
|
||
name: s.name,
|
||
nameKey: foldTr(s.name),
|
||
kind: s.kind,
|
||
branch: s.branch ?? "",
|
||
distanceM: s.kind === "online" ? null : (s.distanceM ?? null),
|
||
carryDifficulty: s.carry,
|
||
deliveryFeeMinor: s.kind === "online" ? (s.deliveryFee ?? null) : null,
|
||
freeDeliveryMinMinor: s.kind === "online" ? (s.freeDeliveryMin ?? null) : null,
|
||
mealCardAccepted: Boolean(s.mealCards?.length),
|
||
mealCardProviders: s.mealCards ?? [],
|
||
note: s.note ?? "",
|
||
seedRef: s.ref,
|
||
createdBy: actor,
|
||
updatedBy: actor,
|
||
});
|
||
stores++;
|
||
}
|
||
|
||
const types = await ProductTypeModel.find({ householdId, seedRef: { $type: "string" } })
|
||
.select({ seedRef: 1, unit: 1 })
|
||
.lean<{ _id: Types.ObjectId; seedRef: string; unit: string }[]>();
|
||
const typeByRef = new Map(types.map((x) => [x.seedRef, x]));
|
||
let products = 0;
|
||
for (const p of PRODUCTS) {
|
||
const type = typeByRef.get(p.type);
|
||
if (!type || (await ProductModel.exists({ householdId, seedRef: p.ref }))) continue;
|
||
if (!autoConvertible(p.unit, type.unit) && !p.typeQty)
|
||
throw new Error(`Seed ürünü ${p.ref}: ${p.unit} → ${type.unit} dönüşümü eksik`);
|
||
await ProductModel.create({
|
||
householdId,
|
||
productTypeId: type._id,
|
||
brand: p.brand,
|
||
name: p.name ?? "",
|
||
searchKey: foldTr(`${p.brand} ${p.name ?? ""}`),
|
||
barcode: p.barcode ?? null,
|
||
packageQuantity: p.qty,
|
||
packageUnit: p.unit,
|
||
typeQuantity: autoConvertible(p.unit, type.unit) ? null : p.typeQty,
|
||
seedRef: p.ref,
|
||
createdBy: actor,
|
||
updatedBy: actor,
|
||
});
|
||
products++;
|
||
}
|
||
return { stores, products };
|
||
}
|