Refactor database schemas and enhance inventory management
All checks were successful
farmcontrol/farmcontrol-ws/pipeline/head This commit looks good

- 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.
This commit is contained in:
Tom Butcher 2026-08-29 00:48:13 +01:00
parent 4109523f59
commit cdc7b860ac
12 changed files with 289 additions and 369 deletions

View File

@ -900,7 +900,6 @@ async function getFilter(query, allowedFilters, parse = true, model = null) {
for (const key of ['sortProperty', 'sortOrder', 'page', 'limit']) { for (const key of ['sortProperty', 'sortOrder', 'page', 'limit']) {
if (key in queryClean) delete queryClean[key]; if (key in queryClean) delete queryClean[key];
} }
logger.info('queryExcludingSortAndOrder', queryClean);
if ( if (
!parse && !parse &&
queryClean['order._id'] !== undefined && queryClean['order._id'] !== undefined &&

View File

@ -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);

View File

@ -1,16 +1,16 @@
import mongoose from 'mongoose'; import mongoose from 'mongoose';
import { generateId } from '../../utils.js'; import { generateId } from '../../utils.js';
const { Schema } = mongoose;
import { aggregateRollups, aggregateRollupsHistory, editObject } from '../../database.js'; import { aggregateRollups, aggregateRollupsHistory, editObject } from '../../database.js';
// Define the main partStock schema // Define the main partStock schema
const partStockSchema = new Schema( const partStockSchema = new mongoose.Schema(
{ {
_reference: { type: String, default: () => generateId()() }, _reference: { type: String, default: () => generateId()() },
state: { state: {
type: { type: String, required: true }, type: { type: String, required: true, default: 'draft' },
progress: { type: Number, required: false }, progress: { type: Number, required: false },
}, },
postedAt: { type: Date, required: false },
part: { type: mongoose.Schema.Types.ObjectId, ref: 'part', required: true }, part: { type: mongoose.Schema.Types.ObjectId, ref: 'part', required: true },
partSku: { type: mongoose.Schema.Types.ObjectId, ref: 'partSku', required: true }, partSku: { type: mongoose.Schema.Types.ObjectId, ref: 'partSku', required: true },
stockLocation: { stockLocation: {
@ -25,13 +25,11 @@ const partStockSchema = new Schema(
timestamp: { type: Date, default: Date.now }, timestamp: { type: Date, default: Date.now },
}, },
], ],
sourceType: { type: String, required: true },
source: { type: Schema.Types.ObjectId, refPath: 'sourceType', required: true },
}, },
{ timestamps: true } { timestamps: true }
); );
partStockSchema.index({ sourceType: 'text', 'state.type': 'text' }); partStockSchema.index({ 'state.type': 'text' });
partStockSchema.pre('validate', async function () { partStockSchema.pre('validate', async function () {
if (!this.part && this.partSku) { if (!this.part && this.partSku) {
@ -46,6 +44,11 @@ const rollupConfigs = [
filter: {}, filter: {},
rollups: [{ name: 'totalCurrentQuantity', property: 'currentQuantity', operation: 'sum' }], rollups: [{ name: 'totalCurrentQuantity', property: 'currentQuantity', operation: 'sum' }],
}, },
{
name: 'draft',
filter: { 'state.type': 'draft' },
rollups: [{ name: 'draft', property: 'state.type', operation: 'count' }],
},
{ {
name: 'new', name: 'new',
filter: { 'state.type': 'new' }, filter: { 'state.type': 'new' },

View File

@ -1,10 +1,20 @@
import mongoose from 'mongoose'; import mongoose from 'mongoose';
import { generateId } from '../../utils.js'; 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( const productCategorySchema = new mongoose.Schema(
{ {
_reference: { type: String, default: () => generateId()() }, _reference: { type: String, default: () => generateId()() },
name: { required: true, type: String }, name: { required: true, type: String },
marketplaces: { type: [marketplaceMappingSchema], default: [] },
}, },
{ timestamps: true } { timestamps: true }
); );

View File

@ -1,5 +1,6 @@
import mongoose from 'mongoose'; import mongoose from 'mongoose';
import { generateId } from '../../utils.js'; import { generateId } from '../../utils.js';
import { marketplaceSyncMappingSchema } from '../sales/marketplaceMapping.schema.js';
const taxRateSchema = new mongoose.Schema( const taxRateSchema = new mongoose.Schema(
{ {
@ -10,8 +11,11 @@ const taxRateSchema = new mongoose.Schema(
active: { required: true, type: Boolean, default: true }, active: { required: true, type: Boolean, default: true },
description: { required: false, type: String }, description: { required: false, type: String },
country: { 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 }, effectiveFrom: { required: false, type: Date },
effectiveTo: { required: false, type: Date }, effectiveTo: { required: false, type: Date },
marketplaces: { type: [marketplaceSyncMappingSchema()], default: [] },
}, },
{ timestamps: true } { timestamps: true }
); );

View File

@ -50,365 +50,79 @@ import { listingModel } from './sales/listing.schema.js';
import { listingVarientModel } from './sales/listingvarient.schema.js'; import { listingVarientModel } from './sales/listingvarient.schema.js';
import { marketplaceEventModel } from './sales/marketplaceevent.schema.js'; import { marketplaceEventModel } from './sales/marketplaceevent.schema.js';
import { paymentModel } from './finance/payment.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 = { export const models = {
PRN: { PRN: modelEntry(() => printerModel, 'printer', 'Printer'),
model: printerModel, PPF: modelEntry(() => printerProfileModel, 'printerProfile', 'Printer Profile'),
idField: '_id', FPF: modelEntry(() => filamentProfileModel, 'filamentProfile', 'Filament Profile'),
type: 'printer', FIL: modelEntry(() => filamentModel, 'filament', 'Filament'),
referenceField: '_reference', FSU: modelEntry(() => filamentSkuModel, 'filamentSku', 'Filament SKU'),
label: 'Printer', GCF: modelEntry(() => gcodeFileModel, 'gcodeFile', 'G-Code File'),
}, JOB: modelEntry(() => jobModel, 'job', 'Job'),
PPF: { PRT: modelEntry(() => partModel, 'part', 'Part'),
model: printerProfileModel, PSU: modelEntry(() => partSkuModel, 'partSku', 'Part SKU'),
idField: '_id', PRD: modelEntry(() => productModel, 'product', 'Product'),
type: 'printerProfile', PCG: modelEntry(() => productCategoryModel, 'productCategory', 'Product Category'),
referenceField: '_reference', SKU: modelEntry(() => productSkuModel, 'productSku', 'Product SKU'),
label: 'Printer Profile', VEN: modelEntry(() => vendorModel, 'vendor', 'Vendor'),
}, MAT: modelEntry(() => materialModel, 'material', 'Material'),
FPF: { SJB: modelEntry(() => subJobModel, 'subJob', 'Sub Job'),
model: filamentProfileModel, FLS: modelEntry(() => filamentStockModel, 'filamentStock', 'Filament Stock'),
idField: '_id', SEV: modelEntry(() => stockEventModel, 'stockEvent', 'Stock Event'),
type: 'filamentProfile', SAU: modelEntry(() => stockAuditModel, 'stockAudit', 'Stock Audit'),
referenceField: '_reference', PTS: modelEntry(() => partStockModel, 'partStock', 'Part Stock'),
label: 'Filament Profile', PDS: modelEntry(() => productStockModel, 'productStock', 'Product Stock'),
}, SLN: modelEntry(() => stockLocationModel, 'stockLocation', 'Stock Location'),
FIL: { STT: modelEntry(() => stockTransferModel, 'stockTransfer', 'Stock Transfer'),
model: filamentModel, ADL: modelEntry(() => auditLogModel, 'auditLog', 'Audit Log'),
idField: '_id', USR: modelEntry(() => userModel, 'user', 'User'),
type: 'filament', UGP: modelEntry(() => userGroupModel, 'userGroup', 'User Group'),
referenceField: '_reference', PMS: modelEntry(() => permissionSettingModel, 'permissionSetting', 'Permission Settings'),
label: 'Filament', APP: modelEntry(() => appPasswordModel, 'appPassword', 'App Password'),
}, NTY: modelEntry(() => noteTypeModel, 'noteType', 'Note Type'),
FSU: { NTE: modelEntry(() => noteModel, 'note', 'Note'),
model: filamentSkuModel, NTF: modelEntry(() => notificationModel, 'notification', 'Notification'),
idField: '_id', ONF: modelEntry(() => userNotifierModel, 'userNotifier', 'User Notifier'),
type: 'filamentSku', DSZ: modelEntry(() => documentSizeModel, 'documentSize', 'Document Size'),
referenceField: '_reference', DTP: modelEntry(() => documentTemplateModel, 'documentTemplate', 'Document Template'),
label: 'Filament SKU', DPR: modelEntry(() => documentPrinterModel, 'documentPrinter', 'Document Printer'),
}, DJB: modelEntry(() => documentJobModel, 'documentJob', 'Document Job'),
GCF: { HST: modelEntry(() => hostModel, 'host', 'Host'),
model: gcodeFileModel, FLE: modelEntry(() => fileModel, 'file', 'File'),
idField: '_id', POR: modelEntry(() => purchaseOrderModel, 'purchaseOrder', 'Purchase Order'),
type: 'gcodeFile', ODI: modelEntry(() => orderItemModel, 'orderItem', 'Order Item'),
referenceField: '_reference', COS: modelEntry(() => courierServiceModel, 'courierService', 'Courier Service'),
label: 'G-Code File', COR: modelEntry(() => courierModel, 'courier', 'Courier'),
}, TXR: modelEntry(() => taxRateModel, 'taxRate', 'Tax Rate'),
JOB: { model: jobModel, idField: '_id', type: 'job', referenceField: '_reference', label: 'Job' }, TXD: modelEntry(() => taxRecordModel, 'taxRecord', 'Tax Record'),
PRT: { SHP: modelEntry(() => shipmentModel, 'shipment', 'Shipment'),
model: partModel, INV: modelEntry(() => invoiceModel, 'invoice', 'Invoice'),
idField: '_id', CLI: modelEntry(() => clientModel, 'client', 'Client'),
type: 'part', SOR: modelEntry(() => salesOrderModel, 'salesOrder', 'Sales Order'),
referenceField: '_reference', MKT: modelEntry(() => marketplaceModel, 'marketplace', 'Marketplace'),
label: 'Part', LST: modelEntry(() => listingModel, 'listing', 'Listing'),
}, LVR: modelEntry(() => listingVarientModel, 'listingVarient', 'Listing Varient'),
PSU: { MKE: modelEntry(() => marketplaceEventModel, 'marketplaceEvent', 'Marketplace Event'),
model: partSkuModel, PAY: modelEntry(() => paymentModel, 'payment', 'Payment'),
idField: '_id', PPL: modelEntry(() => paymentPolicyModel, 'paymentPolicy', 'Payment Policy'),
type: 'partSku', FPL: modelEntry(() => fulfillmentPolicyModel, 'fulfillmentPolicy', 'Fulfillment Policy'),
referenceField: '_reference', RPL: modelEntry(() => returnPolicyModel, 'returnPolicy', 'Return Policy'),
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',
},
}; };

View File

@ -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);

View File

@ -1,6 +1,12 @@
import mongoose from 'mongoose'; import mongoose from 'mongoose';
import { generateId } from '../../utils.js'; 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 { Schema } = mongoose;
const listingSchema = new Schema( const listingSchema = new Schema(
@ -14,13 +20,24 @@ const listingSchema = new Schema(
state: { state: {
type: { type: {
type: String, type: String,
enum: ['draft', 'active', 'inactive', 'deleted', 'suspended', 'syncing'], enum: [
'draft',
'active',
'inactive',
'deleted',
'suspended',
'syncing',
'publishing',
'unpublishing',
],
default: 'draft', default: 'draft',
}, },
progress: { type: Number, required: false },
message: { type: String, required: false }, message: { type: String, required: false },
}, },
url: { type: String, required: false }, url: { type: String, required: false },
description: { type: String, required: false }, description: { type: String, required: false },
listingImages: [{ type: Schema.Types.ObjectId, ref: 'file', required: false }],
externalReference: { type: String, required: false }, externalReference: { type: String, required: false },
price: { type: Number, required: false }, price: { type: Number, required: false },
currency: { type: String, required: false }, currency: { type: String, required: false },
@ -51,6 +68,9 @@ const listingSchema = new Schema(
required: false, required: false,
}, },
courierServices: [{ type: Schema.Types.ObjectId, ref: 'courierService', required: true }], 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 } { timestamps: true }
); );
@ -192,6 +212,16 @@ const rollupConfigs = [
filter: { 'state.type': 'syncing' }, filter: { 'state.type': 'syncing' },
rollups: [{ name: 'syncing', property: 'state.type', operation: 'count' }], 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', name: 'suspended',
filter: { 'state.type': 'suspended' }, filter: { 'state.type': 'suspended' },

View File

@ -9,16 +9,34 @@ const toId = (value) => {
return String(value); return String(value);
}; };
const listingVarientAspectSchema = new Schema(
{
name: { type: String, required: true },
value: { type: String, required: true },
},
{ _id: true }
);
const listingVarientSchema = new Schema( const listingVarientSchema = new Schema(
{ {
_reference: { type: String, default: () => generateId()() }, _reference: { type: String, default: () => generateId()() },
listing: { type: Schema.Types.ObjectId, ref: 'listing', required: true }, listing: { type: Schema.Types.ObjectId, ref: 'listing', required: true },
product: { type: Schema.Types.ObjectId, ref: 'product', required: false }, product: { type: Schema.Types.ObjectId, ref: 'product', required: false },
productSku: { type: Schema.Types.ObjectId, ref: 'productSku', required: false }, productSku: { type: Schema.Types.ObjectId, ref: 'productSku', required: false },
aspects: { type: [listingVarientAspectSchema], default: [] },
state: { state: {
type: { type: {
type: String, type: String,
enum: ['draft', 'active', 'inactive', 'deleted', 'suspended', 'syncing'], enum: [
'draft',
'active',
'inactive',
'deleted',
'suspended',
'syncing',
'publishing',
'unpublishing',
],
default: 'draft', default: 'draft',
}, },
message: { type: String, required: false }, message: { type: String, required: false },
@ -29,6 +47,7 @@ const listingVarientSchema = new Schema(
priceTaxRate: { type: Schema.Types.ObjectId, ref: 'taxRate', required: false }, priceTaxRate: { type: Schema.Types.ObjectId, ref: 'taxRate', required: false },
priceWithTax: { type: Number, required: false }, priceWithTax: { type: Number, required: false },
lastSyncedAt: { type: Date, required: false }, lastSyncedAt: { type: Date, required: false },
listingImages: [{ type: Schema.Types.ObjectId, ref: 'file', required: false }],
stockQuantity: { type: Number, required: false, default: 0 }, stockQuantity: { type: Number, required: false, default: 0 },
}, },
{ timestamps: true } { timestamps: true }

View File

@ -24,8 +24,24 @@ const marketplaceSchema = new mongoose.Schema(
}, },
// Provider-specific API configuration (flexible for eBay, Etsy, TikTok Shop) // Provider-specific API configuration (flexible for eBay, Etsy, TikTok Shop)
config: { type: mongoose.Schema.Types.Mixed, default: {} }, 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: { eBay: {
availableShippingServices: { type: [String], default: [] }, availableShippingServices: { type: [String], default: [] },
categoryReferences: { type: [mongoose.Schema.Types.Mixed], default: [] },
}, },
}, },
{ timestamps: true } { timestamps: true }

View File

@ -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 }
);
}

View File

@ -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);