From cdc7b860ac5a6ba8c199422b77d8131a44ee3ed1 Mon Sep 17 00:00:00 2001 From: Tom Butcher Date: Sat, 29 Aug 2026 00:48:13 +0100 Subject: [PATCH] Refactor database schemas and enhance inventory management - Removed unnecessary logging in the `getFilter` function to streamline query processing. - Updated the `models` export to include new policy models for payment, fulfillment, and return, enhancing schema flexibility. - Enhanced the `partStock` schema with a default state for `type` and added a `postedAt` field for better tracking. - Introduced a new `marketplaceMappingSchema` in the `productCategory` schema to manage marketplace references. - Updated the `taxRate` schema to include `jurisdiction` and `shippingAndHandlingTaxed` fields for improved tax management. - Enhanced the `listing` and `listingVariant` schemas with new states and policy references to support additional business logic. --- src/database/filter.js | 1 - .../schemas/finance/paymentpolicy.schema.js | 25 + .../schemas/inventory/partstock.schema.js | 15 +- .../management/productcategory.schema.js | 10 + .../schemas/management/taxrate.schema.js | 4 + src/database/schemas/models.js | 432 +++--------------- .../schemas/sales/fulfillmentpolicy.schema.js | 31 ++ src/database/schemas/sales/listing.schema.js | 34 +- .../schemas/sales/listingvarient.schema.js | 21 +- .../schemas/sales/marketplace.schema.js | 16 + .../sales/marketplaceMapping.schema.js | 23 + .../schemas/sales/returnpolicy.schema.js | 46 ++ 12 files changed, 289 insertions(+), 369 deletions(-) create mode 100644 src/database/schemas/finance/paymentpolicy.schema.js create mode 100644 src/database/schemas/sales/fulfillmentpolicy.schema.js create mode 100644 src/database/schemas/sales/marketplaceMapping.schema.js create mode 100644 src/database/schemas/sales/returnpolicy.schema.js diff --git a/src/database/filter.js b/src/database/filter.js index 4719048..80a30c6 100644 --- a/src/database/filter.js +++ b/src/database/filter.js @@ -900,7 +900,6 @@ async function getFilter(query, allowedFilters, parse = true, model = null) { for (const key of ['sortProperty', 'sortOrder', 'page', 'limit']) { if (key in queryClean) delete queryClean[key]; } - logger.info('queryExcludingSortAndOrder', queryClean); if ( !parse && queryClean['order._id'] !== undefined && diff --git a/src/database/schemas/finance/paymentpolicy.schema.js b/src/database/schemas/finance/paymentpolicy.schema.js new file mode 100644 index 0000000..6520018 --- /dev/null +++ b/src/database/schemas/finance/paymentpolicy.schema.js @@ -0,0 +1,25 @@ +import mongoose from 'mongoose'; +import { generateId } from '../../utils.js'; +import { marketplaceSyncMappingSchema } from '../sales/marketplaceMapping.schema.js'; + +const paymentPolicySchema = new mongoose.Schema( + { + _reference: { type: String, default: () => generateId()() }, + name: { type: String, required: true }, + description: { type: String, required: false }, + immediatePay: { type: Boolean, required: false, default: true }, + paymentInstructions: { type: String, required: false }, + marketplaces: { type: [marketplaceSyncMappingSchema()], default: [] }, + }, + { timestamps: true } +); + +paymentPolicySchema.index({ name: 'text', description: 'text', paymentInstructions: 'text' }); + +paymentPolicySchema.virtual('id').get(function () { + return this._id; +}); + +paymentPolicySchema.set('toJSON', { virtuals: true }); + +export const paymentPolicyModel = mongoose.model('paymentPolicy', paymentPolicySchema); diff --git a/src/database/schemas/inventory/partstock.schema.js b/src/database/schemas/inventory/partstock.schema.js index 4d2aaa6..dab4fef 100644 --- a/src/database/schemas/inventory/partstock.schema.js +++ b/src/database/schemas/inventory/partstock.schema.js @@ -1,16 +1,16 @@ import mongoose from 'mongoose'; import { generateId } from '../../utils.js'; -const { Schema } = mongoose; import { aggregateRollups, aggregateRollupsHistory, editObject } from '../../database.js'; // Define the main partStock schema -const partStockSchema = new Schema( +const partStockSchema = new mongoose.Schema( { _reference: { type: String, default: () => generateId()() }, state: { - type: { type: String, required: true }, + type: { type: String, required: true, default: 'draft' }, progress: { type: Number, required: false }, }, + postedAt: { type: Date, required: false }, part: { type: mongoose.Schema.Types.ObjectId, ref: 'part', required: true }, partSku: { type: mongoose.Schema.Types.ObjectId, ref: 'partSku', required: true }, stockLocation: { @@ -25,13 +25,11 @@ const partStockSchema = new Schema( timestamp: { type: Date, default: Date.now }, }, ], - sourceType: { type: String, required: true }, - source: { type: Schema.Types.ObjectId, refPath: 'sourceType', required: true }, }, { timestamps: true } ); -partStockSchema.index({ sourceType: 'text', 'state.type': 'text' }); +partStockSchema.index({ 'state.type': 'text' }); partStockSchema.pre('validate', async function () { if (!this.part && this.partSku) { @@ -46,6 +44,11 @@ const rollupConfigs = [ filter: {}, rollups: [{ name: 'totalCurrentQuantity', property: 'currentQuantity', operation: 'sum' }], }, + { + name: 'draft', + filter: { 'state.type': 'draft' }, + rollups: [{ name: 'draft', property: 'state.type', operation: 'count' }], + }, { name: 'new', filter: { 'state.type': 'new' }, diff --git a/src/database/schemas/management/productcategory.schema.js b/src/database/schemas/management/productcategory.schema.js index 5d735f6..1a75e04 100644 --- a/src/database/schemas/management/productcategory.schema.js +++ b/src/database/schemas/management/productcategory.schema.js @@ -1,10 +1,20 @@ import mongoose from 'mongoose'; import { generateId } from '../../utils.js'; +const { Schema } = mongoose; + +const marketplaceMappingSchema = new mongoose.Schema( + { + marketplace: { type: Schema.Types.ObjectId, ref: 'marketplace', required: true }, + externalReference: { type: String, required: false }, + }, + { _id: true } +); const productCategorySchema = new mongoose.Schema( { _reference: { type: String, default: () => generateId()() }, name: { required: true, type: String }, + marketplaces: { type: [marketplaceMappingSchema], default: [] }, }, { timestamps: true } ); diff --git a/src/database/schemas/management/taxrate.schema.js b/src/database/schemas/management/taxrate.schema.js index 074eb63..ba00165 100644 --- a/src/database/schemas/management/taxrate.schema.js +++ b/src/database/schemas/management/taxrate.schema.js @@ -1,5 +1,6 @@ import mongoose from 'mongoose'; import { generateId } from '../../utils.js'; +import { marketplaceSyncMappingSchema } from '../sales/marketplaceMapping.schema.js'; const taxRateSchema = new mongoose.Schema( { @@ -10,8 +11,11 @@ const taxRateSchema = new mongoose.Schema( active: { required: true, type: Boolean, default: true }, description: { required: false, type: String }, country: { required: false, type: String }, + jurisdiction: { required: false, type: String }, + shippingAndHandlingTaxed: { required: false, type: Boolean, default: false }, effectiveFrom: { required: false, type: Date }, effectiveTo: { required: false, type: Date }, + marketplaces: { type: [marketplaceSyncMappingSchema()], default: [] }, }, { timestamps: true } ); diff --git a/src/database/schemas/models.js b/src/database/schemas/models.js index a72289f..138a752 100644 --- a/src/database/schemas/models.js +++ b/src/database/schemas/models.js @@ -50,365 +50,79 @@ import { listingModel } from './sales/listing.schema.js'; import { listingVarientModel } from './sales/listingvarient.schema.js'; import { marketplaceEventModel } from './sales/marketplaceevent.schema.js'; import { paymentModel } from './finance/payment.schema.js'; +import { paymentPolicyModel } from './finance/paymentpolicy.schema.js'; +import { fulfillmentPolicyModel } from './sales/fulfillmentpolicy.schema.js'; +import { returnPolicyModel } from './sales/returnpolicy.schema.js'; -// Map prefixes to models and id fields +function modelEntry(getModel, type, label) { + return { + get model() { + return getModel(); + }, + idField: '_id', + type, + referenceField: '_reference', + label, + }; +} + +// Map prefixes to models and id fields. +// Model getters are lazy so circular ESM imports (schema -> utils -> models -> schema) +// do not read bindings that are still in the temporal dead zone. export const models = { - PRN: { - model: printerModel, - idField: '_id', - type: 'printer', - referenceField: '_reference', - label: 'Printer', - }, - PPF: { - model: printerProfileModel, - idField: '_id', - type: 'printerProfile', - referenceField: '_reference', - label: 'Printer Profile', - }, - FPF: { - model: filamentProfileModel, - idField: '_id', - type: 'filamentProfile', - referenceField: '_reference', - label: 'Filament Profile', - }, - FIL: { - model: filamentModel, - idField: '_id', - type: 'filament', - referenceField: '_reference', - label: 'Filament', - }, - FSU: { - model: filamentSkuModel, - idField: '_id', - type: 'filamentSku', - referenceField: '_reference', - label: 'Filament SKU', - }, - GCF: { - model: gcodeFileModel, - idField: '_id', - type: 'gcodeFile', - referenceField: '_reference', - label: 'G-Code File', - }, - JOB: { model: jobModel, idField: '_id', type: 'job', referenceField: '_reference', label: 'Job' }, - PRT: { - model: partModel, - idField: '_id', - type: 'part', - referenceField: '_reference', - label: 'Part', - }, - PSU: { - model: partSkuModel, - idField: '_id', - type: 'partSku', - referenceField: '_reference', - label: 'Part SKU', - }, - PRD: { - model: productModel, - idField: '_id', - type: 'product', - referenceField: '_reference', - label: 'Product', - }, - PCG: { - model: productCategoryModel, - idField: '_id', - type: 'productCategory', - referenceField: '_reference', - label: 'Product Category', - }, - SKU: { - model: productSkuModel, - idField: '_id', - type: 'productSku', - referenceField: '_reference', - label: 'Product SKU', - }, - VEN: { - model: vendorModel, - idField: '_id', - type: 'vendor', - referenceField: '_reference', - label: 'Vendor', - }, - MAT: { - model: materialModel, - idField: '_id', - type: 'material', - referenceField: '_reference', - label: 'Material', - }, - SJB: { - model: subJobModel, - idField: '_id', - type: 'subJob', - referenceField: '_reference', - label: 'Sub Job', - }, - FLS: { - model: filamentStockModel, - idField: '_id', - type: 'filamentStock', - referenceField: '_reference', - label: 'Filament Stock', - }, - SEV: { - model: stockEventModel, - idField: '_id', - type: 'stockEvent', - referenceField: '_reference', - label: 'Stock Event', - }, - SAU: { - model: stockAuditModel, - idField: '_id', - type: 'stockAudit', - referenceField: '_reference', - label: 'Stock Audit', - }, - PTS: { - model: partStockModel, - idField: '_id', - type: 'partStock', - referenceField: '_reference', - label: 'Part Stock', - }, - PDS: { - model: productStockModel, - idField: '_id', - type: 'productStock', - referenceField: '_reference', - label: 'Product Stock', - }, - SLN: { - model: stockLocationModel, - idField: '_id', - type: 'stockLocation', - referenceField: '_reference', - label: 'Stock Location', - }, - STT: { - model: stockTransferModel, - idField: '_id', - type: 'stockTransfer', - referenceField: '_reference', - label: 'Stock Transfer', - }, - ADL: { - model: auditLogModel, - idField: '_id', - type: 'auditLog', - referenceField: '_reference', - label: 'Audit Log', - }, - USR: { - model: userModel, - idField: '_id', - type: 'user', - referenceField: '_reference', - label: 'User', - }, - UGP: { - model: userGroupModel, - idField: '_id', - type: 'userGroup', - referenceField: '_reference', - label: 'User Group', - }, - PMS: { - model: permissionSettingModel, - idField: '_id', - type: 'permissionSetting', - referenceField: '_reference', - label: 'Permission Settings', - }, - APP: { - model: appPasswordModel, - idField: '_id', - type: 'appPassword', - referenceField: '_reference', - label: 'App Password', - }, - NTY: { - model: noteTypeModel, - idField: '_id', - type: 'noteType', - referenceField: '_reference', - label: 'Note Type', - }, - NTE: { - model: noteModel, - idField: '_id', - type: 'note', - referenceField: '_reference', - label: 'Note', - }, - NTF: { - model: notificationModel, - idField: '_id', - type: 'notification', - label: 'Notification', - referenceField: '_reference', - }, - ONF: { - model: userNotifierModel, - idField: '_id', - type: 'userNotifier', - label: 'User Notifier', - referenceField: '_reference', - }, - DSZ: { - model: documentSizeModel, - idField: '_id', - type: 'documentSize', - label: 'Document Size', - referenceField: '_reference', - }, - DTP: { - model: documentTemplateModel, - idField: '_id', - type: 'documentTemplate', - label: 'Document Template', - referenceField: '_reference', - }, - DPR: { - model: documentPrinterModel, - idField: '_id', - type: 'documentPrinter', - label: 'Document Printer', - referenceField: '_reference', - }, - DJB: { - model: documentJobModel, - idField: '_id', - type: 'documentJob', - label: 'Document Job', - referenceField: '_reference', - }, - HST: { - model: hostModel, - idField: '_id', - type: 'host', - referenceField: '_reference', - label: 'Host', - }, - FLE: { - model: fileModel, - idField: '_id', - type: 'file', - referenceField: '_reference', - label: 'File', - }, - POR: { - model: purchaseOrderModel, - idField: '_id', - type: 'purchaseOrder', - label: 'Purchase Order', - referenceField: '_reference', - }, - ODI: { - model: orderItemModel, - idField: '_id', - type: 'orderItem', - label: 'Order Item', - referenceField: '_reference', - }, - COS: { - model: courierServiceModel, - idField: '_id', - type: 'courierService', - label: 'Courier Service', - referenceField: '_reference', - }, - COR: { - model: courierModel, - idField: '_id', - type: 'courier', - label: 'Courier', - referenceField: '_reference', - }, - TXR: { - model: taxRateModel, - idField: '_id', - type: 'taxRate', - label: 'Tax Rate', - referenceField: '_reference', - }, - TXD: { - model: taxRecordModel, - idField: '_id', - type: 'taxRecord', - label: 'Tax Record', - referenceField: '_reference', - }, - SHP: { - model: shipmentModel, - idField: '_id', - type: 'shipment', - label: 'Shipment', - referenceField: '_reference', - }, - INV: { - model: invoiceModel, - idField: '_id', - type: 'invoice', - label: 'Invoice', - referenceField: '_reference', - }, - CLI: { - model: clientModel, - idField: '_id', - type: 'client', - label: 'Client', - referenceField: '_reference', - }, - SOR: { - model: salesOrderModel, - idField: '_id', - type: 'salesOrder', - label: 'Sales Order', - referenceField: '_reference', - }, - MKT: { - model: marketplaceModel, - idField: '_id', - type: 'marketplace', - label: 'Marketplace', - referenceField: '_reference', - }, - LST: { - model: listingModel, - idField: '_id', - type: 'listing', - label: 'Listing', - referenceField: '_reference', - }, - LVR: { - model: listingVarientModel, - idField: '_id', - type: 'listingVarient', - label: 'Listing Varient', - referenceField: '_reference', - }, - MKE: { - model: marketplaceEventModel, - idField: '_id', - type: 'marketplaceEvent', - label: 'Marketplace Event', - referenceField: '_reference', - }, - PAY: { - model: paymentModel, - idField: '_id', - type: 'payment', - label: 'Payment', - referenceField: '_reference', - }, + PRN: modelEntry(() => printerModel, 'printer', 'Printer'), + PPF: modelEntry(() => printerProfileModel, 'printerProfile', 'Printer Profile'), + FPF: modelEntry(() => filamentProfileModel, 'filamentProfile', 'Filament Profile'), + FIL: modelEntry(() => filamentModel, 'filament', 'Filament'), + FSU: modelEntry(() => filamentSkuModel, 'filamentSku', 'Filament SKU'), + GCF: modelEntry(() => gcodeFileModel, 'gcodeFile', 'G-Code File'), + JOB: modelEntry(() => jobModel, 'job', 'Job'), + PRT: modelEntry(() => partModel, 'part', 'Part'), + PSU: modelEntry(() => partSkuModel, 'partSku', 'Part SKU'), + PRD: modelEntry(() => productModel, 'product', 'Product'), + PCG: modelEntry(() => productCategoryModel, 'productCategory', 'Product Category'), + SKU: modelEntry(() => productSkuModel, 'productSku', 'Product SKU'), + VEN: modelEntry(() => vendorModel, 'vendor', 'Vendor'), + MAT: modelEntry(() => materialModel, 'material', 'Material'), + SJB: modelEntry(() => subJobModel, 'subJob', 'Sub Job'), + FLS: modelEntry(() => filamentStockModel, 'filamentStock', 'Filament Stock'), + SEV: modelEntry(() => stockEventModel, 'stockEvent', 'Stock Event'), + SAU: modelEntry(() => stockAuditModel, 'stockAudit', 'Stock Audit'), + PTS: modelEntry(() => partStockModel, 'partStock', 'Part Stock'), + PDS: modelEntry(() => productStockModel, 'productStock', 'Product Stock'), + SLN: modelEntry(() => stockLocationModel, 'stockLocation', 'Stock Location'), + STT: modelEntry(() => stockTransferModel, 'stockTransfer', 'Stock Transfer'), + ADL: modelEntry(() => auditLogModel, 'auditLog', 'Audit Log'), + USR: modelEntry(() => userModel, 'user', 'User'), + UGP: modelEntry(() => userGroupModel, 'userGroup', 'User Group'), + PMS: modelEntry(() => permissionSettingModel, 'permissionSetting', 'Permission Settings'), + APP: modelEntry(() => appPasswordModel, 'appPassword', 'App Password'), + NTY: modelEntry(() => noteTypeModel, 'noteType', 'Note Type'), + NTE: modelEntry(() => noteModel, 'note', 'Note'), + NTF: modelEntry(() => notificationModel, 'notification', 'Notification'), + ONF: modelEntry(() => userNotifierModel, 'userNotifier', 'User Notifier'), + DSZ: modelEntry(() => documentSizeModel, 'documentSize', 'Document Size'), + DTP: modelEntry(() => documentTemplateModel, 'documentTemplate', 'Document Template'), + DPR: modelEntry(() => documentPrinterModel, 'documentPrinter', 'Document Printer'), + DJB: modelEntry(() => documentJobModel, 'documentJob', 'Document Job'), + HST: modelEntry(() => hostModel, 'host', 'Host'), + FLE: modelEntry(() => fileModel, 'file', 'File'), + POR: modelEntry(() => purchaseOrderModel, 'purchaseOrder', 'Purchase Order'), + ODI: modelEntry(() => orderItemModel, 'orderItem', 'Order Item'), + COS: modelEntry(() => courierServiceModel, 'courierService', 'Courier Service'), + COR: modelEntry(() => courierModel, 'courier', 'Courier'), + TXR: modelEntry(() => taxRateModel, 'taxRate', 'Tax Rate'), + TXD: modelEntry(() => taxRecordModel, 'taxRecord', 'Tax Record'), + SHP: modelEntry(() => shipmentModel, 'shipment', 'Shipment'), + INV: modelEntry(() => invoiceModel, 'invoice', 'Invoice'), + CLI: modelEntry(() => clientModel, 'client', 'Client'), + SOR: modelEntry(() => salesOrderModel, 'salesOrder', 'Sales Order'), + MKT: modelEntry(() => marketplaceModel, 'marketplace', 'Marketplace'), + LST: modelEntry(() => listingModel, 'listing', 'Listing'), + LVR: modelEntry(() => listingVarientModel, 'listingVarient', 'Listing Varient'), + MKE: modelEntry(() => marketplaceEventModel, 'marketplaceEvent', 'Marketplace Event'), + PAY: modelEntry(() => paymentModel, 'payment', 'Payment'), + PPL: modelEntry(() => paymentPolicyModel, 'paymentPolicy', 'Payment Policy'), + FPL: modelEntry(() => fulfillmentPolicyModel, 'fulfillmentPolicy', 'Fulfillment Policy'), + RPL: modelEntry(() => returnPolicyModel, 'returnPolicy', 'Return Policy'), }; diff --git a/src/database/schemas/sales/fulfillmentpolicy.schema.js b/src/database/schemas/sales/fulfillmentpolicy.schema.js new file mode 100644 index 0000000..25e91d3 --- /dev/null +++ b/src/database/schemas/sales/fulfillmentpolicy.schema.js @@ -0,0 +1,31 @@ +import mongoose from 'mongoose'; +import { generateId } from '../../utils.js'; +import { marketplaceSyncMappingSchema } from './marketplaceMapping.schema.js'; + +const { Schema } = mongoose; + +const fulfillmentPolicySchema = new Schema( + { + _reference: { type: String, default: () => generateId()() }, + name: { type: String, required: true }, + description: { type: String, required: false }, + handlingTime: { type: Number, required: false, default: 1 }, + localPickup: { type: Boolean, required: false, default: false }, + globalShipping: { type: Boolean, required: false, default: false }, + freightShipping: { type: Boolean, required: false, default: false }, + pickupDropOff: { type: Boolean, required: false, default: false }, + courierServices: [{ type: Schema.Types.ObjectId, ref: 'courierService', required: false }], + marketplaces: { type: [marketplaceSyncMappingSchema()], default: [] }, + }, + { timestamps: true } +); + +fulfillmentPolicySchema.index({ name: 'text', description: 'text' }); + +fulfillmentPolicySchema.virtual('id').get(function () { + return this._id; +}); + +fulfillmentPolicySchema.set('toJSON', { virtuals: true }); + +export const fulfillmentPolicyModel = mongoose.model('fulfillmentPolicy', fulfillmentPolicySchema); diff --git a/src/database/schemas/sales/listing.schema.js b/src/database/schemas/sales/listing.schema.js index 6b3f08f..a110f27 100644 --- a/src/database/schemas/sales/listing.schema.js +++ b/src/database/schemas/sales/listing.schema.js @@ -1,6 +1,12 @@ import mongoose from 'mongoose'; import { generateId } from '../../utils.js'; -import { editObject, newObject, deleteObject, aggregateRollups, aggregateRollupsHistory } from '../../database.js'; +import { + editObject, + newObject, + deleteObject, + aggregateRollups, + aggregateRollupsHistory, +} from '../../database.js'; const { Schema } = mongoose; const listingSchema = new Schema( @@ -14,13 +20,24 @@ const listingSchema = new Schema( state: { type: { type: String, - enum: ['draft', 'active', 'inactive', 'deleted', 'suspended', 'syncing'], + enum: [ + 'draft', + 'active', + 'inactive', + 'deleted', + 'suspended', + 'syncing', + 'publishing', + 'unpublishing', + ], default: 'draft', }, + progress: { type: Number, required: false }, message: { type: String, required: false }, }, url: { type: String, required: false }, description: { type: String, required: false }, + listingImages: [{ type: Schema.Types.ObjectId, ref: 'file', required: false }], externalReference: { type: String, required: false }, price: { type: Number, required: false }, currency: { type: String, required: false }, @@ -51,6 +68,9 @@ const listingSchema = new Schema( required: false, }, courierServices: [{ type: Schema.Types.ObjectId, ref: 'courierService', required: true }], + fulfillmentPolicy: { type: Schema.Types.ObjectId, ref: 'fulfillmentPolicy', required: false }, + paymentPolicy: { type: Schema.Types.ObjectId, ref: 'paymentPolicy', required: false }, + returnPolicy: { type: Schema.Types.ObjectId, ref: 'returnPolicy', required: false }, }, { timestamps: true } ); @@ -192,6 +212,16 @@ const rollupConfigs = [ filter: { 'state.type': 'syncing' }, rollups: [{ name: 'syncing', property: 'state.type', operation: 'count' }], }, + { + name: 'publishing', + filter: { 'state.type': 'publishing' }, + rollups: [{ name: 'publishing', property: 'state.type', operation: 'count' }], + }, + { + name: 'unpublishing', + filter: { 'state.type': 'unpublishing' }, + rollups: [{ name: 'unpublishing', property: 'state.type', operation: 'count' }], + }, { name: 'suspended', filter: { 'state.type': 'suspended' }, diff --git a/src/database/schemas/sales/listingvarient.schema.js b/src/database/schemas/sales/listingvarient.schema.js index 164f04d..a8072fc 100644 --- a/src/database/schemas/sales/listingvarient.schema.js +++ b/src/database/schemas/sales/listingvarient.schema.js @@ -9,16 +9,34 @@ const toId = (value) => { return String(value); }; +const listingVarientAspectSchema = new Schema( + { + name: { type: String, required: true }, + value: { type: String, required: true }, + }, + { _id: true } +); + const listingVarientSchema = new Schema( { _reference: { type: String, default: () => generateId()() }, listing: { type: Schema.Types.ObjectId, ref: 'listing', required: true }, product: { type: Schema.Types.ObjectId, ref: 'product', required: false }, productSku: { type: Schema.Types.ObjectId, ref: 'productSku', required: false }, + aspects: { type: [listingVarientAspectSchema], default: [] }, state: { type: { type: String, - enum: ['draft', 'active', 'inactive', 'deleted', 'suspended', 'syncing'], + enum: [ + 'draft', + 'active', + 'inactive', + 'deleted', + 'suspended', + 'syncing', + 'publishing', + 'unpublishing', + ], default: 'draft', }, message: { type: String, required: false }, @@ -29,6 +47,7 @@ const listingVarientSchema = new Schema( priceTaxRate: { type: Schema.Types.ObjectId, ref: 'taxRate', required: false }, priceWithTax: { type: Number, required: false }, lastSyncedAt: { type: Date, required: false }, + listingImages: [{ type: Schema.Types.ObjectId, ref: 'file', required: false }], stockQuantity: { type: Number, required: false, default: 0 }, }, { timestamps: true } diff --git a/src/database/schemas/sales/marketplace.schema.js b/src/database/schemas/sales/marketplace.schema.js index 85c79a1..2e931c0 100644 --- a/src/database/schemas/sales/marketplace.schema.js +++ b/src/database/schemas/sales/marketplace.schema.js @@ -24,8 +24,24 @@ const marketplaceSchema = new mongoose.Schema( }, // Provider-specific API configuration (flexible for eBay, Etsy, TikTok Shop) config: { type: mongoose.Schema.Types.Mixed, default: {} }, + defaultFulfillmentPolicy: { + type: mongoose.Schema.Types.ObjectId, + ref: 'fulfillmentPolicy', + required: false, + }, + defaultPaymentPolicy: { + type: mongoose.Schema.Types.ObjectId, + ref: 'paymentPolicy', + required: false, + }, + defaultReturnPolicy: { + type: mongoose.Schema.Types.ObjectId, + ref: 'returnPolicy', + required: false, + }, eBay: { availableShippingServices: { type: [String], default: [] }, + categoryReferences: { type: [mongoose.Schema.Types.Mixed], default: [] }, }, }, { timestamps: true } diff --git a/src/database/schemas/sales/marketplaceMapping.schema.js b/src/database/schemas/sales/marketplaceMapping.schema.js new file mode 100644 index 0000000..1326d4e --- /dev/null +++ b/src/database/schemas/sales/marketplaceMapping.schema.js @@ -0,0 +1,23 @@ +import mongoose from 'mongoose'; + +const { Schema } = mongoose; + +export const MARKETPLACE_MAPPING_STATES = ['pending', 'syncing', 'ready', 'failed']; + +export function marketplaceSyncMappingSchema() { + return new Schema( + { + marketplace: { type: Schema.Types.ObjectId, ref: 'marketplace', required: true }, + externalReference: { type: String, required: false }, + state: { + type: { + type: String, + enum: MARKETPLACE_MAPPING_STATES, + default: 'pending', + }, + message: { type: String, required: false }, + }, + }, + { _id: true } + ); +} diff --git a/src/database/schemas/sales/returnpolicy.schema.js b/src/database/schemas/sales/returnpolicy.schema.js new file mode 100644 index 0000000..b416d62 --- /dev/null +++ b/src/database/schemas/sales/returnpolicy.schema.js @@ -0,0 +1,46 @@ +import mongoose from 'mongoose'; +import { generateId } from '../../utils.js'; +import { marketplaceSyncMappingSchema } from './marketplaceMapping.schema.js'; + +const returnPolicySchema = new mongoose.Schema( + { + _reference: { type: String, default: () => generateId()() }, + name: { type: String, required: true }, + description: { type: String, required: false }, + returnsAccepted: { type: Boolean, required: true, default: true }, + returnPeriodDays: { type: Number, required: false, default: 30 }, + returnShippingCostPayer: { + type: String, + enum: ['buyer', 'seller'], + required: false, + default: 'buyer', + }, + refundMethod: { + type: String, + enum: ['moneyBack', 'merchandiseCredit'], + required: false, + default: 'moneyBack', + }, + restockingFeePercentage: { type: Number, required: false }, + returnInstructions: { type: String, required: false }, + internationalReturnsAccepted: { type: Boolean, required: false }, + internationalReturnPeriodDays: { type: Number, required: false }, + internationalReturnShippingCostPayer: { + type: String, + enum: ['buyer', 'seller'], + required: false, + }, + marketplaces: { type: [marketplaceSyncMappingSchema()], default: [] }, + }, + { timestamps: true } +); + +returnPolicySchema.index({ name: 'text', description: 'text', returnInstructions: 'text' }); + +returnPolicySchema.virtual('id').get(function () { + return this._id; +}); + +returnPolicySchema.set('toJSON', { virtuals: true }); + +export const returnPolicyModel = mongoose.model('returnPolicy', returnPolicySchema);