Compare commits
2 Commits
bce40ce30d
...
3bd1549c85
| Author | SHA1 | Date | |
|---|---|---|---|
| 3bd1549c85 | |||
| 6623bf9bfd |
@ -15,11 +15,6 @@ 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,
|
||||
@ -37,18 +32,27 @@ 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({ name: 'text' });
|
||||
stockTransferSchema.index({ 'state.type': '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 }],
|
||||
tags: [{ type: String, required: true }],
|
||||
auditLines: { type: [stockAuditLevelLineSchema], default: [] },
|
||||
},
|
||||
{ timestamps: true }
|
||||
|
||||
@ -9,12 +9,11 @@ const listAllowedFilters = [
|
||||
'state',
|
||||
'state.type',
|
||||
'postedAt',
|
||||
'name',
|
||||
'createdAt',
|
||||
'updatedAt',
|
||||
'_reference',
|
||||
];
|
||||
const listAllowedSorters = ['name', 'createdAt', 'postedAt', 'state', 'updatedAt'];
|
||||
const listAllowedSorters = ['createdAt', 'postedAt', 'state', 'updatedAt'];
|
||||
const propertiesAllowedFilters = ['state.type'];
|
||||
import {
|
||||
listStockTransfersRouteHandler,
|
||||
|
||||
@ -30,9 +30,14 @@ 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,
|
||||
@ -91,20 +96,36 @@ async function createStock(model, newData, user) {
|
||||
return result;
|
||||
}
|
||||
|
||||
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}`);
|
||||
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}`);
|
||||
}
|
||||
|
||||
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');
|
||||
@ -129,7 +150,7 @@ async function executePostedLine(transferId, line, user) {
|
||||
);
|
||||
|
||||
await createStockEventsForLine({
|
||||
transferId,
|
||||
transferId: transfer._id,
|
||||
fromId: src._id,
|
||||
fromType: 'filamentStock',
|
||||
toId: dest._id,
|
||||
@ -145,6 +166,7 @@ async function executePostedLine(transferId, 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');
|
||||
@ -164,7 +186,7 @@ async function executePostedLine(transferId, line, user) {
|
||||
);
|
||||
|
||||
await createStockEventsForLine({
|
||||
transferId,
|
||||
transferId: transfer._id,
|
||||
fromId: src._id,
|
||||
fromType: 'partStock',
|
||||
toId: dest._id,
|
||||
@ -180,6 +202,7 @@ async function executePostedLine(transferId, 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');
|
||||
}
|
||||
@ -199,7 +222,7 @@ async function executePostedLine(transferId, line, user) {
|
||||
);
|
||||
|
||||
await createStockEventsForLine({
|
||||
transferId,
|
||||
transferId: transfer._id,
|
||||
fromId: src._id,
|
||||
fromType: 'productStock',
|
||||
toId: dest._id,
|
||||
@ -215,6 +238,13 @@ async function executePostedLine(transferId, 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,
|
||||
@ -235,11 +265,7 @@ export const listStockTransfersRouteHandler = async (
|
||||
search,
|
||||
sort,
|
||||
order,
|
||||
populate: [
|
||||
{ path: 'lines.fromStock' },
|
||||
{ path: 'lines.toStockLocation' },
|
||||
{ path: 'lines.toStock' },
|
||||
],
|
||||
populate: stockTransferPopulate,
|
||||
});
|
||||
|
||||
if (result?.error) {
|
||||
@ -263,11 +289,7 @@ export const listStockTransfersByPropertiesRouteHandler = async (
|
||||
model: stockTransferModel,
|
||||
properties,
|
||||
filter,
|
||||
populate: [
|
||||
{ path: 'lines.fromStock' },
|
||||
{ path: 'lines.toStockLocation' },
|
||||
{ path: 'lines.toStock' },
|
||||
],
|
||||
populate: stockTransferPopulate,
|
||||
masterFilter,
|
||||
});
|
||||
|
||||
@ -309,11 +331,7 @@ export const getStockTransferRouteHandler = async (req, res) => {
|
||||
const result = await getObject({
|
||||
model: stockTransferModel,
|
||||
id,
|
||||
populate: [
|
||||
{ path: 'lines.fromStock' },
|
||||
{ path: 'lines.toStockLocation' },
|
||||
{ path: 'lines.toStock' },
|
||||
],
|
||||
populate: stockTransferPopulate,
|
||||
});
|
||||
if (result?.error) {
|
||||
logger.warn(`Stock transfer not found with supplied id.`);
|
||||
@ -343,8 +361,11 @@ export const editStockTransferRouteHandler = async (req, res) => {
|
||||
const updateData = {
|
||||
lines: (req.body.lines || []).map((l) => normalizeLineInput(l)),
|
||||
};
|
||||
if (req.body.name !== undefined) {
|
||||
updateData.name = req.body.name;
|
||||
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;
|
||||
}
|
||||
|
||||
const result = await editObject({
|
||||
@ -352,11 +373,7 @@ export const editStockTransferRouteHandler = async (req, res) => {
|
||||
id,
|
||||
updateData,
|
||||
user: req.user,
|
||||
populate: [
|
||||
{ path: 'lines.fromStock' },
|
||||
{ path: 'lines.toStockLocation' },
|
||||
{ path: 'lines.toStock' },
|
||||
],
|
||||
populate: stockTransferPopulate,
|
||||
});
|
||||
|
||||
if (result.error) {
|
||||
@ -396,8 +413,9 @@ 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({
|
||||
@ -471,12 +489,19 @@ 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._id, plain, req.user);
|
||||
const { toStockType, toStock } = await executePostedLine(doc, plain, req.user);
|
||||
updatedLines.push({
|
||||
...plain,
|
||||
toStockType,
|
||||
@ -502,11 +527,7 @@ export const postStockTransferRouteHandler = async (req, res) => {
|
||||
const posted = await getObject({
|
||||
model: stockTransferModel,
|
||||
id,
|
||||
populate: [
|
||||
{ path: 'lines.fromStock' },
|
||||
{ path: 'lines.toStockLocation' },
|
||||
{ path: 'lines.toStock' },
|
||||
],
|
||||
populate: stockTransferPopulate,
|
||||
});
|
||||
|
||||
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: ['name', 'state.type', 'postedAt'],
|
||||
stockTransfer: ['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