diff --git a/src/database/schemas/__tests__/stockQuantity.recalculate.test.js b/src/database/schemas/__tests__/stockQuantity.recalculate.test.js index 942b4d2..d36c558 100644 --- a/src/database/schemas/__tests__/stockQuantity.recalculate.test.js +++ b/src/database/schemas/__tests__/stockQuantity.recalculate.test.js @@ -39,6 +39,7 @@ describe('listingVarient.recalculate', () => { beforeEach(() => { aggregateRollups.mockReset(); editObject.mockReset(); + jest.restoreAllMocks(); }); it('sums sibling listing varient stock quantities onto the listing', async () => { @@ -61,6 +62,52 @@ describe('listingVarient.recalculate', () => { recalculate: false, }); }); + + it('writes the product sku stock total onto the listing varient before rolling up', async () => { + jest.spyOn(listingVarientModel, 'exists').mockResolvedValue({ _id: varientId }); + aggregateRollups.mockImplementation(async ({ model }) => { + if (model === productStockModel) { + return { stockQuantity: { sum: 9 } }; + } + return { stockQuantity: { sum: 12 } }; + }); + editObject.mockResolvedValue({}); + + await listingVarientModel.recalculate( + { + _id: varientId, + listing: { _id: listingId, stockLocation: stockLocationId, product: productId }, + product: productId, + productSku: productSkuId, + stockQuantity: 0, + }, + 'user-1' + ); + + expect(aggregateRollups).toHaveBeenCalledWith( + expect.objectContaining({ + model: productStockModel, + baseFilter: { + productSku: productSkuId, + stockLocation: stockLocationId, + }, + }) + ); + expect(editObject).toHaveBeenCalledWith({ + model: listingVarientModel, + id: varientId, + updateData: { stockQuantity: 9 }, + user: 'user-1', + recalculate: false, + }); + expect(editObject).toHaveBeenCalledWith({ + model: listingModel, + id: listingId, + updateData: { stockQuantity: 12 }, + user: 'user-1', + recalculate: false, + }); + }); }); describe('productStock.recalculate', () => { diff --git a/src/database/schemas/sales/listingvarient.schema.js b/src/database/schemas/sales/listingvarient.schema.js index 113f7dc..2010b5e 100644 --- a/src/database/schemas/sales/listingvarient.schema.js +++ b/src/database/schemas/sales/listingvarient.schema.js @@ -3,6 +3,12 @@ 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()() }, @@ -48,6 +54,46 @@ listingVarientSchema.set('toJSON', { 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; }