Files
butcem/src/models/shopping-list.ts
T
suphicakir bd5a20ce64 feat(shopping): alışverişi tamamla transaction'ı ve karşılaştırma motoru
- tek transaction: stok alımı, fiş kaynaklı fiyat, mağaza başına bölünmüş gider, fiş ekleri, kalanların taşınması
- muadil, markasız ürün tipi ve plansız ekstra satırları; stok → gider kategorisi eşlemesi ve yedek kategori
- compareShopping saf fonksiyonu: aynı/muadil/alınmadı/ekstra, farkın nedenlere tam ayrışması
- tamamlanan listede rapor görüntüsü; testler

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 23:57:50 +03:00

64 lines
2.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { model, models, Schema, type InferSchemaType, type Model } from "mongoose";
import {
SHOPPING_ITEM_KINDS,
SHOPPING_ITEM_SOURCES,
SHOPPING_LIST_STATUSES,
} from "@/shared/shopping";
/**
* Alışveriş kalemi (Bölüm 6.12): ürün tipi (genel), belirli ürün (marka/paket) ya da serbest metin.
* Miktar binde bir: ürün tipinde tipin biriminde, üründe paket adedi, serbest metinde `unit` ile.
* `checked`: alışveriş modunda sepete alındı.
*/
const shoppingItemSchema = new Schema(
{
kind: { type: String, enum: SHOPPING_ITEM_KINDS, required: true },
productTypeId: { type: Schema.Types.ObjectId, default: null },
productId: { type: Schema.Types.ObjectId, default: null },
text: { type: String, default: "", trim: true, maxlength: 120 },
/** Boşsa "1 paket" kabul edilir. */
quantity: { type: Number, default: null, min: 0 },
/** Serbest metin kaleminin birimi. */
unit: { type: String, default: "", trim: true, maxlength: 20 },
note: { type: String, default: "", trim: true, maxlength: 200 },
source: { type: String, enum: SHOPPING_ITEM_SOURCES, default: "manual" },
checked: { type: Boolean, default: false },
checkedBy: { type: Schema.Types.ObjectId, default: null },
checkedAt: { type: Date, default: null },
addedBy: { type: Schema.Types.ObjectId, required: true },
addedAt: { type: Date, default: () => new Date() },
},
{ _id: true },
);
/** Alışveriş listesi; hane içinde ortak. Birden çok aktif liste olabilir. Silme: soft delete. */
const shoppingListSchema = new Schema(
{
householdId: { type: Schema.Types.ObjectId, required: true },
name: { type: String, required: true, trim: true, maxlength: 80 },
status: { type: String, enum: SHOPPING_LIST_STATUSES, default: "active" },
note: { type: String, default: "", trim: true, maxlength: 300 },
items: { type: [shoppingItemSchema], default: [] },
completedAt: { type: Date, default: null },
/** Tamamlama anındaki karşılaştırma görüntüsü (ShoppingCompletion; etiketler sabitlenir). */
completion: { type: Schema.Types.Mixed, default: null },
deletedAt: { type: Date, default: null },
seedRef: { type: String, default: null },
createdBy: { type: Schema.Types.ObjectId, required: true },
updatedBy: { type: Schema.Types.ObjectId, required: true },
},
{ timestamps: true, collection: "shoppingLists" },
);
shoppingListSchema.index({ householdId: 1, deletedAt: 1, status: 1, updatedAt: -1 });
shoppingListSchema.index(
{ householdId: 1, seedRef: 1 },
{ unique: true, partialFilterExpression: { seedRef: { $type: "string" } } },
);
export type ShoppingList = InferSchemaType<typeof shoppingListSchema>;
export const ShoppingListModel: Model<ShoppingList> =
(models.ShoppingList as Model<ShoppingList> | undefined) ??
model<ShoppingList>("ShoppingList", shoppingListSchema);