farmcontrol-api/src/database/schemas/sales/listingvarient.schema.js
Tom Butcher 8cd8ee6f94 Add stock quantity calculation to listing variant schema and enhance tests
This commit introduces a new utility function, `toId`, to handle ID conversions in the `listingvarient.schema.js` file. The `recalculate` method is updated to compute and update the stock quantity for listing variants based on product SKU and stock location. Additionally, a new test case is added in `stockQuantity.recalculate.test.js` to verify the correct behavior of stock quantity calculations before rolling up, ensuring comprehensive test coverage for this functionality.
2026-08-24 18:33:10 +01:00

124 lines
3.8 KiB
JavaScript

import mongoose from 'mongoose';
import { generateId } from '../../utils.js';
import { aggregateRollups, editObject } from '../../database.js';
const { Schema } = mongoose;
const toId = (value) => {
if (value == null) return null;
if (typeof value === 'object' && value._id) return String(value._id);
return String(value);
};
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 },
state: {
type: {
type: String,
enum: ['draft', 'active', 'inactive', 'deleted', 'suspended', 'syncing'],
default: 'draft',
},
message: { type: String, required: false },
},
externalReference: { type: String, required: false },
price: { type: Number, required: false },
currency: { type: String, required: false },
priceTaxRate: { type: Schema.Types.ObjectId, ref: 'taxRate', required: false },
priceWithTax: { type: Number, required: false },
lastSyncedAt: { type: Date, required: false },
stockQuantity: { type: Number, required: false, default: 0 },
},
{ timestamps: true }
);
listingVarientSchema.index({ currency: 'text', 'state.type': 'text' });
listingVarientSchema.index({ listing: 1, externalReference: 1 }, { unique: true, sparse: true });
listingVarientSchema.virtual('id').get(function () {
return this._id;
});
listingVarientSchema.set('toJSON', {
virtuals: true,
transform(doc, ret) {
if (!ret.state && ret.status) {
ret.state = { type: ret.status, message: null };
}
if (ret.status) delete ret.status;
return ret;
},
});
listingVarientSchema.statics.recalculate = async function (listingVarient, user) {
const listingId = listingVarient?.listing?._id || listingVarient?.listing;
const varientId = listingVarient?._id;
const productSkuId = toId(listingVarient?.productSku);
if (varientId && productSkuId && (await this.exists({ _id: varientId }))) {
let listing = listingVarient.listing;
if (!listing?.stockLocation) {
listing = await mongoose
.model('listing')
.findById(listingId)
.select('stockLocation product')
.lean();
}
const stockLocationId = toId(listing?.stockLocation);
if (stockLocationId) {
const stockRollup = await aggregateRollups({
model: mongoose.model('productStock'),
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 = stockRollup.stockQuantity?.sum || 0;
if (listingVarient.stockQuantity !== stockQuantity) {
await editObject({
model: this,
id: varientId,
updateData: { stockQuantity },
user,
recalculate: false,
});
}
}
}
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);