- Products store packing-slip title aliases; matcher checks exact title/alias first so previously-matched items skip fuzzy matching entirely - Packing-slip imports snapshot printingCost/costOfGoods/productId onto order items; profit analysis reads stored costs so catalog edits don't rewrite history - Etsy statement fees allocate to orders via Order # references in Title/Info instead of date proximity; shop-level fees (listings, ads) no longer leak into order fees - Remove broken transaction-fee exclusion guard (order totals are gross, so all expenses count once) - Remove debug buttons, date-fix banner, test files, and console.log noise; Clear All Orders now uses a bulk DELETE /orders endpoint Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
83 lines
No EOL
2.3 KiB
TypeScript
83 lines
No EOL
2.3 KiB
TypeScript
import mongoose, { Document, Schema } from 'mongoose';
|
|
|
|
export interface IProduct extends Document {
|
|
title: string;
|
|
description: string;
|
|
price: number;
|
|
costOfGoods: number;
|
|
printingCost: number;
|
|
sku: string;
|
|
category: string;
|
|
tags: string[];
|
|
aliases: string[];
|
|
images: string[];
|
|
variants: {
|
|
name: string;
|
|
options: string[];
|
|
price?: number;
|
|
sku?: string;
|
|
}[];
|
|
inventory: {
|
|
quantity: number;
|
|
lowStockAlert: number;
|
|
location?: string;
|
|
};
|
|
dimensions: {
|
|
length?: number;
|
|
width?: number;
|
|
height?: number;
|
|
weight?: number;
|
|
};
|
|
materials: string[];
|
|
isActive: boolean;
|
|
etsyListingId?: string;
|
|
userId: mongoose.Types.ObjectId;
|
|
dateCreated: Date;
|
|
dateUpdated: Date;
|
|
}
|
|
|
|
const ProductSchema: Schema = new Schema({
|
|
title: { type: String, required: true, trim: true },
|
|
description: { type: String, default: '' },
|
|
price: { type: Number, required: true, min: 0 },
|
|
costOfGoods: { type: Number, default: 0, min: 0 },
|
|
printingCost: { type: Number, default: 0, min: 0 },
|
|
sku: { type: String, trim: true, default: '' },
|
|
category: { type: String, default: 'Other' },
|
|
tags: [{ type: String, trim: true }],
|
|
// Packing-slip item titles previously matched to this product; used for
|
|
// deterministic matching on future imports
|
|
aliases: [{ type: String, trim: true }],
|
|
images: [{ type: String }],
|
|
variants: [{
|
|
name: { type: String, required: true },
|
|
options: [{ type: String, required: true }],
|
|
price: { type: Number, min: 0 },
|
|
sku: { type: String }
|
|
}],
|
|
inventory: {
|
|
quantity: { type: Number, required: true, min: 0 },
|
|
lowStockAlert: { type: Number, default: 5 },
|
|
location: { type: String }
|
|
},
|
|
dimensions: {
|
|
length: { type: Number, min: 0 },
|
|
width: { type: Number, min: 0 },
|
|
height: { type: Number, min: 0 },
|
|
weight: { type: Number, min: 0 }
|
|
},
|
|
materials: [{ type: String }],
|
|
isActive: { type: Boolean, default: true },
|
|
etsyListingId: { type: String },
|
|
userId: { type: Schema.Types.ObjectId, ref: 'User', required: true, index: true },
|
|
dateCreated: { type: Date, default: Date.now },
|
|
dateUpdated: { type: Date, default: Date.now }
|
|
});
|
|
|
|
// Update dateUpdated on save
|
|
ProductSchema.pre('save', function(next) {
|
|
this.dateUpdated = new Date();
|
|
next();
|
|
});
|
|
|
|
export default mongoose.model<IProduct>('Product', ProductSchema); |