Enhance database schemas and introduce marketplace event handling
All checks were successful
farmcontrol/farmcontrol-ws/pipeline/head This commit looks good
All checks were successful
farmcontrol/farmcontrol-ws/pipeline/head This commit looks good
- Updated the `notificationUserFromOwner` function to support 'marketplace' as a valid owner type. - Added a new `marketplaceEvent` schema to manage marketplace events, including fields for `externalReference`, `topic`, and `status`. - Enhanced existing schemas (e.g., `orderitem`, `shipment`, `client`, `listing`, `listingVarient`, `salesOrder`, `courierService`) to include `externalReference` fields for better integration with external systems. - Introduced unique indexes for `externalReference` in relevant schemas to ensure data integrity and prevent duplicates. - Improved the `marketplace` schema to include additional fields for eBay shipping services, enhancing its configurability.
This commit is contained in:
parent
e563538e89
commit
2dc2a9d296
@ -63,6 +63,9 @@ const orderItemSchema = new Schema(
|
|||||||
invoicedQuantityRemaining: { type: Number, required: false, default: 0 },
|
invoicedQuantityRemaining: { type: Number, required: false, default: 0 },
|
||||||
timestamp: { type: Date, default: Date.now },
|
timestamp: { type: Date, default: Date.now },
|
||||||
shipment: { type: Schema.Types.ObjectId, ref: 'shipment', required: false },
|
shipment: { type: Schema.Types.ObjectId, ref: 'shipment', required: false },
|
||||||
|
listing: { type: Schema.Types.ObjectId, ref: 'listing', required: false },
|
||||||
|
listingVarient: { type: Schema.Types.ObjectId, ref: 'listingVarient', required: false },
|
||||||
|
externalReference: { type: String, required: false },
|
||||||
orderedAt: { type: Date, required: false },
|
orderedAt: { type: Date, required: false },
|
||||||
receivedAt: { type: Date, required: false },
|
receivedAt: { type: Date, required: false },
|
||||||
},
|
},
|
||||||
@ -70,6 +73,7 @@ const orderItemSchema = new Schema(
|
|||||||
);
|
);
|
||||||
|
|
||||||
orderItemSchema.index({ name: 'text', itemType: 'text', orderType: 'text' });
|
orderItemSchema.index({ name: 'text', itemType: 'text', orderType: 'text' });
|
||||||
|
orderItemSchema.index({ order: 1, externalReference: 1 }, { unique: true, sparse: true });
|
||||||
|
|
||||||
const rollupConfigs = [
|
const rollupConfigs = [
|
||||||
{
|
{
|
||||||
@ -132,7 +136,13 @@ orderItemSchema.statics.recalculate = async function (orderItem, user) {
|
|||||||
cached: true,
|
cached: true,
|
||||||
});
|
});
|
||||||
if (sku) {
|
if (sku) {
|
||||||
const parentId = sku.part?._id || sku.part || sku.product?._id || sku.product || sku.filament?._id || sku.filament;
|
const parentId =
|
||||||
|
sku.part?._id ||
|
||||||
|
sku.part ||
|
||||||
|
sku.product?._id ||
|
||||||
|
sku.product ||
|
||||||
|
sku.filament?._id ||
|
||||||
|
sku.filament;
|
||||||
if (syncAmount === 'itemCost') {
|
if (syncAmount === 'itemCost') {
|
||||||
if (sku.overrideCost && sku.cost != null) {
|
if (sku.overrideCost && sku.cost != null) {
|
||||||
effectiveItemAmount = sku.cost;
|
effectiveItemAmount = sku.cost;
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import mongoose from 'mongoose';
|
import mongoose from 'mongoose';
|
||||||
import { generateId } from '../../utils.js';
|
import { generateId } from '../../utils.js';
|
||||||
const { Schema } = mongoose;
|
const { Schema } = mongoose;
|
||||||
import { aggregateRollups, aggregateRollupsHistory } from '../../database.js';
|
import { aggregateRollups, aggregateRollupsHistory, editObject } from '../../database.js';
|
||||||
|
|
||||||
const partStockUsageSchema = new Schema({
|
const partStockUsageSchema = new Schema({
|
||||||
partStock: { type: Schema.Types.ObjectId, ref: 'partStock', required: false },
|
partStock: { type: Schema.Types.ObjectId, ref: 'partStock', required: false },
|
||||||
@ -9,6 +9,12 @@ const partStockUsageSchema = new Schema({
|
|||||||
quantity: { type: Number, required: true },
|
quantity: { type: Number, required: true },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const toId = (value) => {
|
||||||
|
if (value == null) return null;
|
||||||
|
if (typeof value === 'object' && value._id) return String(value._id);
|
||||||
|
return String(value);
|
||||||
|
};
|
||||||
|
|
||||||
// Define the main productStock schema - tracks assembled products consisting of part stocks
|
// Define the main productStock schema - tracks assembled products consisting of part stocks
|
||||||
const productStockSchema = new Schema(
|
const productStockSchema = new Schema(
|
||||||
{
|
{
|
||||||
@ -76,6 +82,64 @@ productStockSchema.statics.history = async function (from, to) {
|
|||||||
return results;
|
return results;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
productStockSchema.statics.recalculate = async function (productStock, user) {
|
||||||
|
const productSkuId = toId(productStock?.productSku);
|
||||||
|
const stockLocationId = toId(productStock?.stockLocation);
|
||||||
|
if (!productSkuId || !stockLocationId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let productId = toId(productStock?.productSku?.product);
|
||||||
|
if (!productId) {
|
||||||
|
const productSku = await mongoose.model('productSku').findById(productSkuId).select('product').lean();
|
||||||
|
productId = toId(productSku?.product);
|
||||||
|
}
|
||||||
|
if (!productId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rollupResults = await aggregateRollups({
|
||||||
|
model: this,
|
||||||
|
baseFilter: {
|
||||||
|
productSku: new mongoose.Types.ObjectId(productSkuId),
|
||||||
|
stockLocation: new mongoose.Types.ObjectId(stockLocationId),
|
||||||
|
},
|
||||||
|
rollupConfigs: [
|
||||||
|
{
|
||||||
|
name: 'stockQuantity',
|
||||||
|
rollups: [{ name: 'stockQuantity', property: 'currentQuantity', operation: 'sum' }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const stockQuantity = rollupResults.stockQuantity?.sum || 0;
|
||||||
|
|
||||||
|
const listingVarientModel = mongoose.model('listingVarient');
|
||||||
|
const varients = await listingVarientModel
|
||||||
|
.find({ productSku: productSkuId })
|
||||||
|
.populate('listing')
|
||||||
|
.lean();
|
||||||
|
|
||||||
|
for (const varient of varients) {
|
||||||
|
const varientProductId =
|
||||||
|
toId(varient.product) || toId(varient.listing?.product);
|
||||||
|
const varientLocationId = toId(varient.listing?.stockLocation);
|
||||||
|
if (varientProductId !== productId || varientLocationId !== stockLocationId) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (varient.stockQuantity === stockQuantity) {
|
||||||
|
await listingVarientModel.recalculate(varient, user);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
await editObject({
|
||||||
|
model: listingVarientModel,
|
||||||
|
id: varient._id,
|
||||||
|
updateData: { stockQuantity },
|
||||||
|
user,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Add virtual id getter
|
// Add virtual id getter
|
||||||
productStockSchema.virtual('id').get(function () {
|
productStockSchema.virtual('id').get(function () {
|
||||||
return this._id;
|
return this._id;
|
||||||
|
|||||||
@ -13,6 +13,7 @@ const shipmentSchema = new Schema(
|
|||||||
order: { type: Schema.Types.ObjectId, refPath: 'orderType', required: true },
|
order: { type: Schema.Types.ObjectId, refPath: 'orderType', required: true },
|
||||||
courierService: { type: Schema.Types.ObjectId, ref: 'courierService', required: false },
|
courierService: { type: Schema.Types.ObjectId, ref: 'courierService', required: false },
|
||||||
trackingNumber: { type: String, required: false },
|
trackingNumber: { type: String, required: false },
|
||||||
|
externalReference: { type: String, required: false },
|
||||||
amount: { type: Number, required: true },
|
amount: { type: Number, required: true },
|
||||||
amountWithTax: { type: Number, required: true },
|
amountWithTax: { type: Number, required: true },
|
||||||
taxRate: { type: Schema.Types.ObjectId, ref: 'taxRate', required: false },
|
taxRate: { type: Schema.Types.ObjectId, ref: 'taxRate', required: false },
|
||||||
@ -35,6 +36,7 @@ const shipmentSchema = new Schema(
|
|||||||
);
|
);
|
||||||
|
|
||||||
shipmentSchema.index({ trackingNumber: 'text', orderType: 'text' });
|
shipmentSchema.index({ trackingNumber: 'text', orderType: 'text' });
|
||||||
|
shipmentSchema.index({ order: 1, externalReference: 1 }, { unique: true, sparse: true });
|
||||||
|
|
||||||
shipmentSchema.statics.recalculate = async function (shipment, user) {
|
shipmentSchema.statics.recalculate = async function (shipment, user) {
|
||||||
if (shipment.orderType !== 'purchaseOrder' && shipment.orderType !== 'salesOrder') {
|
if (shipment.orderType !== 'purchaseOrder' && shipment.orderType !== 'salesOrder') {
|
||||||
|
|||||||
@ -190,7 +190,7 @@ const recalculateParentStock = async (parentType, parentId, user) => {
|
|||||||
buildParentUpdateData(parentType, parentStock, events)
|
buildParentUpdateData(parentType, parentStock, events)
|
||||||
),
|
),
|
||||||
user,
|
user,
|
||||||
recalculate: false,
|
recalculate: parentType === 'productStock',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -30,7 +30,7 @@ const auditLogSchema = new Schema(
|
|||||||
ownerType: {
|
ownerType: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
enum: ['user', 'printer', 'host'],
|
enum: ['user', 'printer', 'host', 'marketplace'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ timestamps: true }
|
{ timestamps: true }
|
||||||
|
|||||||
@ -14,7 +14,14 @@ const courierSchema = new mongoose.Schema(
|
|||||||
{ timestamps: true }
|
{ timestamps: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
courierSchema.index({ name: 'text', website: 'text', email: 'text', phone: 'text', contact: 'text', country: 'text' });
|
courierSchema.index({
|
||||||
|
name: 'text',
|
||||||
|
website: 'text',
|
||||||
|
email: 'text',
|
||||||
|
phone: 'text',
|
||||||
|
contact: 'text',
|
||||||
|
country: 'text',
|
||||||
|
});
|
||||||
|
|
||||||
courierSchema.virtual('id').get(function () {
|
courierSchema.virtual('id').get(function () {
|
||||||
return this._id;
|
return this._id;
|
||||||
|
|||||||
@ -2,6 +2,14 @@ import mongoose from 'mongoose';
|
|||||||
import { generateId } from '../../utils.js';
|
import { generateId } from '../../utils.js';
|
||||||
const { Schema } = mongoose;
|
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 courierServiceSchema = new mongoose.Schema(
|
const courierServiceSchema = new mongoose.Schema(
|
||||||
{
|
{
|
||||||
_reference: { type: String, default: () => generateId()() },
|
_reference: { type: String, default: () => generateId()() },
|
||||||
@ -18,6 +26,7 @@ const courierServiceSchema = new mongoose.Schema(
|
|||||||
additionalCostWithTax: { required: false, type: Number },
|
additionalCostWithTax: { required: false, type: Number },
|
||||||
shippingCurrency: { required: true, type: String, default: 'GBP' },
|
shippingCurrency: { required: true, type: String, default: 'GBP' },
|
||||||
international: { required: true, type: Boolean, default: false },
|
international: { required: true, type: Boolean, default: false },
|
||||||
|
marketplaces: { type: [marketplaceMappingSchema], default: [] },
|
||||||
},
|
},
|
||||||
{ timestamps: true }
|
{ timestamps: true }
|
||||||
);
|
);
|
||||||
|
|||||||
@ -2,6 +2,53 @@ import mongoose from 'mongoose';
|
|||||||
import { generateId } from '../../utils.js';
|
import { generateId } from '../../utils.js';
|
||||||
const { Schema } = mongoose;
|
const { Schema } = mongoose;
|
||||||
|
|
||||||
|
const RENDER_DOCUMENT_TEMPLATE_CALL =
|
||||||
|
/fc\.renderDocumentTemplate\s*\(\s*(['"])([^'"]+)\1/g;
|
||||||
|
|
||||||
|
function extractRenderDocumentTemplateReferences(content) {
|
||||||
|
if (content == null || typeof content !== 'string' || content === '') {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const references = [];
|
||||||
|
const seen = new Set();
|
||||||
|
const regex = new RegExp(RENDER_DOCUMENT_TEMPLATE_CALL.source, 'g');
|
||||||
|
let match;
|
||||||
|
while ((match = regex.exec(content)) !== null) {
|
||||||
|
const reference = match[2]?.trim();
|
||||||
|
if (!reference || seen.has(reference)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
seen.add(reference);
|
||||||
|
references.push(reference);
|
||||||
|
}
|
||||||
|
return references;
|
||||||
|
}
|
||||||
|
|
||||||
|
function objectIdsFromFilterValue(value) {
|
||||||
|
if (value == null) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
if (value instanceof mongoose.Types.ObjectId) {
|
||||||
|
return [value];
|
||||||
|
}
|
||||||
|
if (typeof value === 'string' && /^[a-f\d]{24}$/i.test(value)) {
|
||||||
|
return [new mongoose.Types.ObjectId(value)];
|
||||||
|
}
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
return value.flatMap(objectIdsFromFilterValue);
|
||||||
|
}
|
||||||
|
if (typeof value === 'object') {
|
||||||
|
if (Array.isArray(value.$in)) {
|
||||||
|
return objectIdsFromFilterValue(value.$in);
|
||||||
|
}
|
||||||
|
if (value._id != null) {
|
||||||
|
return objectIdsFromFilterValue(value._id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
const documentTemplateSchema = new Schema(
|
const documentTemplateSchema = new Schema(
|
||||||
{
|
{
|
||||||
_reference: { type: String, default: () => generateId()() },
|
_reference: { type: String, default: () => generateId()() },
|
||||||
@ -39,6 +86,13 @@ const documentTemplateSchema = new Schema(
|
|||||||
required: false,
|
required: false,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
referencedTemplates: [
|
||||||
|
{
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: 'documentTemplate',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
content: {
|
content: {
|
||||||
type: String,
|
type: String,
|
||||||
required: false,
|
required: false,
|
||||||
@ -63,4 +117,50 @@ documentTemplateSchema.virtual('id').get(function () {
|
|||||||
// Configure JSON serialization to include virtuals
|
// Configure JSON serialization to include virtuals
|
||||||
documentTemplateSchema.set('toJSON', { virtuals: true });
|
documentTemplateSchema.set('toJSON', { virtuals: true });
|
||||||
|
|
||||||
|
documentTemplateSchema.statics.recalculate = async function (documentTemplate, user) {
|
||||||
|
const documentTemplateId = documentTemplate?._id || documentTemplate;
|
||||||
|
if (!documentTemplateId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const stillExists = await this.exists({ _id: documentTemplateId });
|
||||||
|
if (!stillExists) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { getFilter } = await import('../../../utils.js');
|
||||||
|
const references = extractRenderDocumentTemplateReferences(documentTemplate?.content);
|
||||||
|
const referencedTemplateIds = [];
|
||||||
|
const seenIds = new Set();
|
||||||
|
|
||||||
|
for (const reference of references) {
|
||||||
|
const filter = await getFilter({ parent: reference }, ['parent'], true, this);
|
||||||
|
const ids = objectIdsFromFilterValue(filter.parent);
|
||||||
|
for (const id of ids) {
|
||||||
|
const idString = String(id);
|
||||||
|
if (!idString || seenIds.has(idString)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
seenIds.add(idString);
|
||||||
|
referencedTemplateIds.push(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (documentTemplate && typeof documentTemplate === 'object' && !documentTemplate._bsontype) {
|
||||||
|
documentTemplate.referencedTemplates = referencedTemplateIds.map((id) => ({
|
||||||
|
_id: String(id),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
const { editObject } = await import('../../database.js');
|
||||||
|
await editObject({
|
||||||
|
model: this,
|
||||||
|
id: documentTemplateId,
|
||||||
|
updateData: { referencedTemplates: referencedTemplateIds },
|
||||||
|
user,
|
||||||
|
populate: [{ path: 'referencedTemplates', strictPopulate: false }],
|
||||||
|
recalculate: false,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const documentTemplateModel = mongoose.model('documentTemplate', documentTemplateSchema);
|
export const documentTemplateModel = mongoose.model('documentTemplate', documentTemplateSchema);
|
||||||
|
|||||||
@ -48,6 +48,7 @@ import { salesOrderModel } from './sales/salesorder.schema.js';
|
|||||||
import { marketplaceModel } from './sales/marketplace.schema.js';
|
import { marketplaceModel } from './sales/marketplace.schema.js';
|
||||||
import { listingModel } from './sales/listing.schema.js';
|
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 { paymentModel } from './finance/payment.schema.js';
|
import { paymentModel } from './finance/payment.schema.js';
|
||||||
|
|
||||||
// Map prefixes to models and id fields
|
// Map prefixes to models and id fields
|
||||||
@ -396,6 +397,13 @@ export const models = {
|
|||||||
label: 'Listing Varient',
|
label: 'Listing Varient',
|
||||||
referenceField: '_reference',
|
referenceField: '_reference',
|
||||||
},
|
},
|
||||||
|
MKE: {
|
||||||
|
model: marketplaceEventModel,
|
||||||
|
idField: '_id',
|
||||||
|
type: 'marketplaceEvent',
|
||||||
|
label: 'Marketplace Event',
|
||||||
|
referenceField: '_reference',
|
||||||
|
},
|
||||||
PAY: {
|
PAY: {
|
||||||
model: paymentModel,
|
model: paymentModel,
|
||||||
idField: '_id',
|
idField: '_id',
|
||||||
|
|||||||
@ -16,6 +16,7 @@ const clientSchema = new mongoose.Schema(
|
|||||||
_reference: { type: String, default: () => generateId()() },
|
_reference: { type: String, default: () => generateId()() },
|
||||||
name: { required: true, type: String },
|
name: { required: true, type: String },
|
||||||
marketplace: { type: mongoose.Schema.Types.ObjectId, ref: 'marketplace', required: false },
|
marketplace: { type: mongoose.Schema.Types.ObjectId, ref: 'marketplace', required: false },
|
||||||
|
externalReference: { type: String, required: false },
|
||||||
email: { required: false, type: String },
|
email: { required: false, type: String },
|
||||||
phone: { required: false, type: String },
|
phone: { required: false, type: String },
|
||||||
country: { required: false, type: String },
|
country: { required: false, type: String },
|
||||||
@ -27,6 +28,7 @@ const clientSchema = new mongoose.Schema(
|
|||||||
);
|
);
|
||||||
|
|
||||||
clientSchema.index({ name: 'text', email: 'text', phone: 'text', country: 'text', tags: 'text' });
|
clientSchema.index({ name: 'text', email: 'text', phone: 'text', country: 'text', tags: 'text' });
|
||||||
|
clientSchema.index({ marketplace: 1, externalReference: 1 }, { unique: true, sparse: true });
|
||||||
|
|
||||||
clientSchema.virtual('id').get(function () {
|
clientSchema.virtual('id').get(function () {
|
||||||
return this._id;
|
return this._id;
|
||||||
|
|||||||
@ -19,15 +19,43 @@ const listingSchema = new Schema(
|
|||||||
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 },
|
||||||
|
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 },
|
||||||
lastSyncedAt: { type: Date, required: false },
|
lastSyncedAt: { type: Date, required: false },
|
||||||
|
stockQuantity: { type: Number, required: false, default: 0 },
|
||||||
|
condition: {
|
||||||
|
type: String,
|
||||||
|
enum: [
|
||||||
|
'new',
|
||||||
|
'likeNew',
|
||||||
|
'newOther',
|
||||||
|
'newWithDefects',
|
||||||
|
'manufacturerRefurbished',
|
||||||
|
'certifiedRefurbished',
|
||||||
|
'excellentRefurbished',
|
||||||
|
'veryGoodRefurbished',
|
||||||
|
'goodRefurbished',
|
||||||
|
'sellerRefurbished',
|
||||||
|
'usedExcellent',
|
||||||
|
'usedVeryGood',
|
||||||
|
'usedGood',
|
||||||
|
'usedAcceptable',
|
||||||
|
'forPartsOrNotWorking',
|
||||||
|
'preOwnedExcellent',
|
||||||
|
'preOwnedFair',
|
||||||
|
],
|
||||||
|
default: 'new',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
courierServices: [{ type: Schema.Types.ObjectId, ref: 'courierService', required: true }],
|
courierServices: [{ type: Schema.Types.ObjectId, ref: 'courierService', required: true }],
|
||||||
},
|
},
|
||||||
{ timestamps: true }
|
{ timestamps: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
listingSchema.index({ title: 'text', url: 'text' });
|
listingSchema.index({ title: 'text', url: 'text' });
|
||||||
|
listingSchema.index({ marketplace: 1, externalReference: 1 }, { unique: true, sparse: true });
|
||||||
|
|
||||||
listingSchema.virtual('id').get(function () {
|
listingSchema.virtual('id').get(function () {
|
||||||
return this._id;
|
return this._id;
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import mongoose from 'mongoose';
|
import mongoose from 'mongoose';
|
||||||
import { generateId } from '../../utils.js';
|
import { generateId } from '../../utils.js';
|
||||||
|
import { aggregateRollups, editObject } from '../../database.js';
|
||||||
const { Schema } = mongoose;
|
const { Schema } = mongoose;
|
||||||
|
|
||||||
const listingVarientSchema = new Schema(
|
const listingVarientSchema = new Schema(
|
||||||
@ -16,16 +17,19 @@ const listingVarientSchema = new Schema(
|
|||||||
},
|
},
|
||||||
message: { type: String, required: false },
|
message: { 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 },
|
||||||
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 },
|
||||||
|
stockQuantity: { type: Number, required: false, default: 0 },
|
||||||
},
|
},
|
||||||
{ timestamps: true }
|
{ timestamps: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
listingVarientSchema.index({ currency: 'text', 'state.type': 'text' });
|
listingVarientSchema.index({ currency: 'text', 'state.type': 'text' });
|
||||||
|
listingVarientSchema.index({ listing: 1, externalReference: 1 }, { unique: true, sparse: true });
|
||||||
|
|
||||||
listingVarientSchema.virtual('id').get(function () {
|
listingVarientSchema.virtual('id').get(function () {
|
||||||
return this._id;
|
return this._id;
|
||||||
@ -42,4 +46,32 @@ listingVarientSchema.set('toJSON', {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
listingVarientSchema.statics.recalculate = async function (listingVarient, user) {
|
||||||
|
const listingId = listingVarient?.listing?._id || listingVarient?.listing;
|
||||||
|
if (!listingId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rollupResults = await aggregateRollups({
|
||||||
|
model: this,
|
||||||
|
baseFilter: { listing: new mongoose.Types.ObjectId(listingId) },
|
||||||
|
rollupConfigs: [
|
||||||
|
{
|
||||||
|
name: 'stockQuantity',
|
||||||
|
rollups: [{ name: 'stockQuantity', property: 'stockQuantity', operation: 'sum' }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
await editObject({
|
||||||
|
model: mongoose.model('listing'),
|
||||||
|
id: listingId,
|
||||||
|
updateData: {
|
||||||
|
stockQuantity: rollupResults.stockQuantity?.sum || 0,
|
||||||
|
},
|
||||||
|
user,
|
||||||
|
recalculate: false,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const listingVarientModel = mongoose.model('listingVarient', listingVarientSchema);
|
export const listingVarientModel = mongoose.model('listingVarient', listingVarientSchema);
|
||||||
|
|||||||
@ -17,13 +17,16 @@ const marketplaceSchema = new mongoose.Schema(
|
|||||||
state: {
|
state: {
|
||||||
type: {
|
type: {
|
||||||
type: String,
|
type: String,
|
||||||
enum: ['active', 'inactive', 'suspended', 'ready', 'offline', 'syncing'],
|
enum: ['active', 'inactive', 'suspended', 'ready', 'offline', 'syncing', 'disconnected'],
|
||||||
default: 'offline',
|
default: 'offline',
|
||||||
},
|
},
|
||||||
message: { type: String, required: false },
|
message: { type: String, required: false },
|
||||||
},
|
},
|
||||||
// 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: {} },
|
||||||
|
eBay: {
|
||||||
|
availableShippingServices: { type: [String], default: [] },
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{ timestamps: true }
|
{ timestamps: true }
|
||||||
);
|
);
|
||||||
|
|||||||
31
src/database/schemas/sales/marketplaceevent.schema.js
Normal file
31
src/database/schemas/sales/marketplaceevent.schema.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import mongoose from 'mongoose';
|
||||||
|
import { generateId } from '../../utils.js';
|
||||||
|
|
||||||
|
const marketplaceEventSchema = new mongoose.Schema(
|
||||||
|
{
|
||||||
|
_reference: { type: String, default: () => generateId()() },
|
||||||
|
marketplace: { type: mongoose.Schema.Types.ObjectId, ref: 'marketplace', required: true },
|
||||||
|
externalReference: { type: String, required: true },
|
||||||
|
topic: { type: String, required: true },
|
||||||
|
status: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
enum: ['received', 'processed', 'failed'],
|
||||||
|
default: 'received',
|
||||||
|
},
|
||||||
|
processedAt: { type: Date, required: false },
|
||||||
|
error: { type: String, required: false },
|
||||||
|
},
|
||||||
|
{ timestamps: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
marketplaceEventSchema.index({ marketplace: 1, externalReference: 1 }, { unique: true });
|
||||||
|
marketplaceEventSchema.index({ topic: 'text', status: 'text' });
|
||||||
|
|
||||||
|
marketplaceEventSchema.virtual('id').get(function () {
|
||||||
|
return this._id;
|
||||||
|
});
|
||||||
|
|
||||||
|
marketplaceEventSchema.set('toJSON', { virtuals: true });
|
||||||
|
|
||||||
|
export const marketplaceEventModel = mongoose.model('marketplaceEvent', marketplaceEventSchema);
|
||||||
@ -1,11 +1,7 @@
|
|||||||
import mongoose from 'mongoose';
|
import mongoose from 'mongoose';
|
||||||
import { generateId } from '../../utils.js';
|
import { generateId } from '../../utils.js';
|
||||||
const { Schema } = mongoose;
|
const { Schema } = mongoose;
|
||||||
import {
|
import { aggregateRollups, aggregateRollupsHistory, editObject } from '../../database.js';
|
||||||
aggregateRollups,
|
|
||||||
aggregateRollupsHistory,
|
|
||||||
editObject,
|
|
||||||
} from '../../database.js';
|
|
||||||
|
|
||||||
const salesOrderSchema = new Schema(
|
const salesOrderSchema = new Schema(
|
||||||
{
|
{
|
||||||
@ -19,6 +15,7 @@ const salesOrderSchema = new Schema(
|
|||||||
timestamp: { type: Date, default: Date.now },
|
timestamp: { type: Date, default: Date.now },
|
||||||
client: { type: Schema.Types.ObjectId, ref: 'client', required: true },
|
client: { type: Schema.Types.ObjectId, ref: 'client', required: true },
|
||||||
marketplace: { type: Schema.Types.ObjectId, ref: 'marketplace', required: false },
|
marketplace: { type: Schema.Types.ObjectId, ref: 'marketplace', required: false },
|
||||||
|
externalReference: { type: String, required: false },
|
||||||
state: {
|
state: {
|
||||||
type: { type: String, required: true, default: 'draft' },
|
type: { type: String, required: true, default: 'draft' },
|
||||||
},
|
},
|
||||||
@ -31,6 +28,7 @@ const salesOrderSchema = new Schema(
|
|||||||
);
|
);
|
||||||
|
|
||||||
salesOrderSchema.index({ 'state.type': 'text' });
|
salesOrderSchema.index({ 'state.type': 'text' });
|
||||||
|
salesOrderSchema.index({ marketplace: 1, externalReference: 1 }, { unique: true, sparse: true });
|
||||||
|
|
||||||
const rollupConfigs = [
|
const rollupConfigs = [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -40,7 +40,7 @@ function notificationUserFromOwner(owner, ownerType) {
|
|||||||
if (ownerType === 'user') {
|
if (ownerType === 'user') {
|
||||||
return owner;
|
return owner;
|
||||||
}
|
}
|
||||||
if (ownerType === 'host') {
|
if (ownerType === 'host' || ownerType === 'marketplace') {
|
||||||
return {
|
return {
|
||||||
_id: owner._id,
|
_id: owner._id,
|
||||||
firstName: owner.name ?? 'unknown',
|
firstName: owner.name ?? 'unknown',
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user