Compare commits
No commits in common. "3bd1549c85ec43b8dbbc3710ecb388eac8e1d757" and "bce40ce30d5798723f613b8cf98aadb6d818c7bb" have entirely different histories.
3bd1549c85
...
bce40ce30d
@ -15,6 +15,11 @@ const stockTransferLineSchema = new Schema(
|
||||
required: true,
|
||||
},
|
||||
quantity: { type: Number, required: true },
|
||||
toStockLocation: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'stockLocation',
|
||||
required: true,
|
||||
},
|
||||
toStockType: {
|
||||
type: String,
|
||||
required: false,
|
||||
@ -32,27 +37,18 @@ const stockTransferLineSchema = new Schema(
|
||||
const stockTransferSchema = new Schema(
|
||||
{
|
||||
_reference: { type: String, default: () => generateId()() },
|
||||
name: { type: String, required: true },
|
||||
state: {
|
||||
type: { type: String, required: true, default: 'draft' },
|
||||
progress: { type: Number, required: false },
|
||||
},
|
||||
postedAt: { type: Date, required: false },
|
||||
fromLocation: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'stockLocation',
|
||||
required: true,
|
||||
},
|
||||
toLocation: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'stockLocation',
|
||||
required: true,
|
||||
},
|
||||
lines: { type: [stockTransferLineSchema], default: [] },
|
||||
},
|
||||
{ timestamps: true }
|
||||
);
|
||||
|
||||
stockTransferSchema.index({ 'state.type': 'text' });
|
||||
stockTransferSchema.index({ name: 'text' });
|
||||
|
||||
stockTransferSchema.statics.stats = async function () {
|
||||
const [draft, posted] = await Promise.all([
|
||||
|
||||
@ -13,8 +13,8 @@ const toId = (value) => {
|
||||
export function normalizeAuditLevelLine(line) {
|
||||
const allItems = Boolean(line?.allItems);
|
||||
const allSkus = allItems ? true : Boolean(line?.allSkus);
|
||||
const item = allItems ? null : (line?.item?._id ?? line?.item ?? null);
|
||||
const itemSku = allItems || allSkus ? null : (line?.itemSku?._id ?? line?.itemSku ?? null);
|
||||
const item = allItems ? null : line?.item?._id ?? line?.item ?? null;
|
||||
const itemSku = allItems || allSkus ? null : line?.itemSku?._id ?? line?.itemSku ?? null;
|
||||
|
||||
return {
|
||||
_id: line?._id,
|
||||
@ -62,7 +62,7 @@ const stockAuditLevelSchema = new Schema(
|
||||
{
|
||||
_reference: { type: String, default: () => generateId()() },
|
||||
name: { type: String, required: true },
|
||||
tags: [{ type: String, required: true }],
|
||||
tags: [{ type: String }],
|
||||
auditLines: { type: [stockAuditLevelLineSchema], default: [] },
|
||||
},
|
||||
{ timestamps: true }
|
||||
|
||||
@ -9,11 +9,12 @@ const listAllowedFilters = [
|
||||
'state',
|
||||
'state.type',
|
||||
'postedAt',
|
||||
'name',
|
||||
'createdAt',
|
||||
'updatedAt',
|
||||
'_reference',
|
||||
];
|
||||
const listAllowedSorters = ['createdAt', 'postedAt', 'state', 'updatedAt'];
|
||||
const listAllowedSorters = ['name', 'createdAt', 'postedAt', 'state', 'updatedAt'];
|
||||
const propertiesAllowedFilters = ['state.type'];
|
||||
import {
|
||||
listStockTransfersRouteHandler,
|
||||
|
||||
@ -30,14 +30,9 @@ const normalizeLineInput = (l) => ({
|
||||
fromStockType: l.fromStockType,
|
||||
fromStock: l.fromStock?._id ?? l.fromStock,
|
||||
quantity: Number(l.quantity),
|
||||
toStockLocation: l.toStockLocation?._id ?? l.toStockLocation,
|
||||
});
|
||||
|
||||
const toId = (value) => {
|
||||
if (value == null) return null;
|
||||
if (typeof value === 'object' && value._id) return String(value._id);
|
||||
return String(value);
|
||||
};
|
||||
|
||||
async function createStockEvent(newData, user) {
|
||||
const result = await newObject({
|
||||
model: stockEventModel,
|
||||
@ -96,36 +91,20 @@ async function createStock(model, newData, user) {
|
||||
return result;
|
||||
}
|
||||
|
||||
async function executePostedLine(transfer, line, user) {
|
||||
const fromLocId = transfer.fromLocation;
|
||||
const toLocId = transfer.toLocation;
|
||||
|
||||
const [fromLoc, toLoc] = await Promise.all([
|
||||
stockLocationModel.findById(fromLocId).lean(),
|
||||
stockLocationModel.findById(toLocId).lean(),
|
||||
]);
|
||||
|
||||
if (!fromLoc) {
|
||||
throw new Error(`Unknown from location: ${fromLocId}`);
|
||||
}
|
||||
if (!toLoc) {
|
||||
throw new Error(`Unknown to location: ${toLocId}`);
|
||||
async function executePostedLine(transferId, line, user) {
|
||||
const toLocId = line.toStockLocation;
|
||||
const loc = await stockLocationModel.findById(toLocId).lean();
|
||||
if (!loc) {
|
||||
throw new Error(`Unknown stock location: ${toLocId}`);
|
||||
}
|
||||
|
||||
if (!(line.quantity > 0)) {
|
||||
throw new Error('Line quantity must be positive');
|
||||
}
|
||||
|
||||
const assertStockAtFromLocation = (stock) => {
|
||||
if (toId(stock?.stockLocation) !== toId(fromLocId)) {
|
||||
throw new Error('From stock must be at the transfer from location');
|
||||
}
|
||||
};
|
||||
|
||||
if (line.fromStockType === 'filamentStock') {
|
||||
const src = await filamentStockModel.findById(line.fromStock);
|
||||
if (!src) throw new Error('From filament stock not found');
|
||||
assertStockAtFromLocation(src);
|
||||
const netAvail = src.currentWeight?.net ?? 0;
|
||||
if (line.quantity > netAvail) {
|
||||
throw new Error('Filament transfer quantity exceeds available net weight');
|
||||
@ -150,7 +129,7 @@ async function executePostedLine(transfer, line, user) {
|
||||
);
|
||||
|
||||
await createStockEventsForLine({
|
||||
transferId: transfer._id,
|
||||
transferId,
|
||||
fromId: src._id,
|
||||
fromType: 'filamentStock',
|
||||
toId: dest._id,
|
||||
@ -166,7 +145,6 @@ async function executePostedLine(transfer, line, user) {
|
||||
if (line.fromStockType === 'partStock') {
|
||||
const src = await partStockModel.findById(line.fromStock);
|
||||
if (!src) throw new Error('From part stock not found');
|
||||
assertStockAtFromLocation(src);
|
||||
const currentQuantity = src.currentQuantity;
|
||||
if (line.quantity > currentQuantity) {
|
||||
throw new Error('Part transfer quantity exceeds current quantity');
|
||||
@ -186,7 +164,7 @@ async function executePostedLine(transfer, line, user) {
|
||||
);
|
||||
|
||||
await createStockEventsForLine({
|
||||
transferId: transfer._id,
|
||||
transferId,
|
||||
fromId: src._id,
|
||||
fromType: 'partStock',
|
||||
toId: dest._id,
|
||||
@ -202,7 +180,6 @@ async function executePostedLine(transfer, line, user) {
|
||||
if (line.fromStockType === 'productStock') {
|
||||
const src = await productStockModel.findById(line.fromStock);
|
||||
if (!src) throw new Error('From product stock not found');
|
||||
assertStockAtFromLocation(src);
|
||||
if (line.quantity > src.currentQuantity) {
|
||||
throw new Error('Product transfer quantity exceeds current quantity');
|
||||
}
|
||||
@ -222,7 +199,7 @@ async function executePostedLine(transfer, line, user) {
|
||||
);
|
||||
|
||||
await createStockEventsForLine({
|
||||
transferId: transfer._id,
|
||||
transferId,
|
||||
fromId: src._id,
|
||||
fromType: 'productStock',
|
||||
toId: dest._id,
|
||||
@ -238,13 +215,6 @@ async function executePostedLine(transfer, line, user) {
|
||||
throw new Error(`Unsupported from stock type: ${line.fromStockType}`);
|
||||
}
|
||||
|
||||
const stockTransferPopulate = [
|
||||
{ path: 'fromLocation' },
|
||||
{ path: 'toLocation' },
|
||||
{ path: 'lines.fromStock' },
|
||||
{ path: 'lines.toStock' },
|
||||
];
|
||||
|
||||
export const listStockTransfersRouteHandler = async (
|
||||
req,
|
||||
res,
|
||||
@ -265,7 +235,11 @@ export const listStockTransfersRouteHandler = async (
|
||||
search,
|
||||
sort,
|
||||
order,
|
||||
populate: stockTransferPopulate,
|
||||
populate: [
|
||||
{ path: 'lines.fromStock' },
|
||||
{ path: 'lines.toStockLocation' },
|
||||
{ path: 'lines.toStock' },
|
||||
],
|
||||
});
|
||||
|
||||
if (result?.error) {
|
||||
@ -289,7 +263,11 @@ export const listStockTransfersByPropertiesRouteHandler = async (
|
||||
model: stockTransferModel,
|
||||
properties,
|
||||
filter,
|
||||
populate: stockTransferPopulate,
|
||||
populate: [
|
||||
{ path: 'lines.fromStock' },
|
||||
{ path: 'lines.toStockLocation' },
|
||||
{ path: 'lines.toStock' },
|
||||
],
|
||||
masterFilter,
|
||||
});
|
||||
|
||||
@ -331,7 +309,11 @@ export const getStockTransferRouteHandler = async (req, res) => {
|
||||
const result = await getObject({
|
||||
model: stockTransferModel,
|
||||
id,
|
||||
populate: stockTransferPopulate,
|
||||
populate: [
|
||||
{ path: 'lines.fromStock' },
|
||||
{ path: 'lines.toStockLocation' },
|
||||
{ path: 'lines.toStock' },
|
||||
],
|
||||
});
|
||||
if (result?.error) {
|
||||
logger.warn(`Stock transfer not found with supplied id.`);
|
||||
@ -361,11 +343,8 @@ export const editStockTransferRouteHandler = async (req, res) => {
|
||||
const updateData = {
|
||||
lines: (req.body.lines || []).map((l) => normalizeLineInput(l)),
|
||||
};
|
||||
if (req.body.fromLocation !== undefined) {
|
||||
updateData.fromLocation = req.body.fromLocation?._id ?? req.body.fromLocation;
|
||||
}
|
||||
if (req.body.toLocation !== undefined) {
|
||||
updateData.toLocation = req.body.toLocation?._id ?? req.body.toLocation;
|
||||
if (req.body.name !== undefined) {
|
||||
updateData.name = req.body.name;
|
||||
}
|
||||
|
||||
const result = await editObject({
|
||||
@ -373,7 +352,11 @@ export const editStockTransferRouteHandler = async (req, res) => {
|
||||
id,
|
||||
updateData,
|
||||
user: req.user,
|
||||
populate: stockTransferPopulate,
|
||||
populate: [
|
||||
{ path: 'lines.fromStock' },
|
||||
{ path: 'lines.toStockLocation' },
|
||||
{ path: 'lines.toStock' },
|
||||
],
|
||||
});
|
||||
|
||||
if (result.error) {
|
||||
@ -413,9 +396,8 @@ export const editMultipleStockTransfersRouteHandler = async (req, res) => {
|
||||
|
||||
export const newStockTransferRouteHandler = async (req, res) => {
|
||||
const newData = {
|
||||
name: req.body.name,
|
||||
state: req.body.state ?? { type: 'draft' },
|
||||
fromLocation: req.body.fromLocation?._id ?? req.body.fromLocation,
|
||||
toLocation: req.body.toLocation?._id ?? req.body.toLocation,
|
||||
lines: (req.body.lines || []).map((l) => normalizeLineInput(l)),
|
||||
};
|
||||
const result = await newObject({
|
||||
@ -489,19 +471,12 @@ export const postStockTransferRouteHandler = async (req, res) => {
|
||||
return res.status(400).send({ error: 'Stock transfer has no lines.', code: 400 });
|
||||
}
|
||||
|
||||
if (!doc.fromLocation || !doc.toLocation) {
|
||||
return res.status(400).send({
|
||||
error: 'Stock transfer must have from and to locations.',
|
||||
code: 400,
|
||||
});
|
||||
}
|
||||
|
||||
const updatedLines = [];
|
||||
|
||||
try {
|
||||
for (const line of doc.lines) {
|
||||
const plain = line.toObject();
|
||||
const { toStockType, toStock } = await executePostedLine(doc, plain, req.user);
|
||||
const { toStockType, toStock } = await executePostedLine(doc._id, plain, req.user);
|
||||
updatedLines.push({
|
||||
...plain,
|
||||
toStockType,
|
||||
@ -527,7 +502,11 @@ export const postStockTransferRouteHandler = async (req, res) => {
|
||||
const posted = await getObject({
|
||||
model: stockTransferModel,
|
||||
id,
|
||||
populate: stockTransferPopulate,
|
||||
populate: [
|
||||
{ path: 'lines.fromStock' },
|
||||
{ path: 'lines.toStockLocation' },
|
||||
{ path: 'lines.toStock' },
|
||||
],
|
||||
});
|
||||
|
||||
if (posted?.error) {
|
||||
|
||||
@ -35,7 +35,7 @@ export const EXPORT_FILTER_BY_TYPE = {
|
||||
shipment: ['order._id', 'orderType', 'courierService._id'],
|
||||
stockEvent: ['parent._id', 'parentType', 'owner._id', 'ownerType'],
|
||||
stockLocation: ['name', 'address'],
|
||||
stockTransfer: ['state.type', 'postedAt'],
|
||||
stockTransfer: ['name', 'state.type', 'postedAt'],
|
||||
stockAudit: ['auditLevel._id', 'stockLocation._id', 'state.type', 'postedAt'],
|
||||
stockAuditLevel: ['name', 'tags'],
|
||||
documentJob: ['documentTemplate', 'documentPrinter', 'object._id', 'objectType'],
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user