Implemented Part SKUs.
This commit is contained in:
parent
17e46f6aee
commit
73fbb50b34
@ -11,7 +11,7 @@ const partStockSchema = new Schema(
|
||||
type: { type: String, required: true },
|
||||
progress: { type: Number, required: false },
|
||||
},
|
||||
part: { type: mongoose.Schema.Types.ObjectId, ref: 'part', required: true },
|
||||
partSku: { type: mongoose.Schema.Types.ObjectId, ref: 'partSku', required: true },
|
||||
currentQuantity: { type: Number, required: true },
|
||||
sourceType: { type: String, required: true },
|
||||
source: { type: Schema.Types.ObjectId, refPath: 'sourceType', required: true },
|
||||
|
||||
@ -5,7 +5,7 @@ import { aggregateRollups, aggregateRollupsHistory } from '../../database.js';
|
||||
|
||||
const partStockUsageSchema = new Schema({
|
||||
partStock: { type: Schema.Types.ObjectId, ref: 'partStock', required: false },
|
||||
part: { type: Schema.Types.ObjectId, ref: 'part', required: true },
|
||||
partSku: { type: Schema.Types.ObjectId, ref: 'partSku', required: true },
|
||||
quantity: { type: Number, required: true },
|
||||
});
|
||||
|
||||
@ -18,7 +18,7 @@ const productStockSchema = new Schema(
|
||||
progress: { type: Number, required: false },
|
||||
},
|
||||
postedAt: { type: Date, required: false },
|
||||
product: { type: mongoose.Schema.Types.ObjectId, ref: 'product', required: true },
|
||||
productSku: { type: mongoose.Schema.Types.ObjectId, ref: 'productSku', required: true },
|
||||
currentQuantity: { type: Number, required: true },
|
||||
partStocks: [partStockUsageSchema],
|
||||
},
|
||||
|
||||
@ -2,22 +2,13 @@ import mongoose from 'mongoose';
|
||||
import { generateId } from '../../utils.js';
|
||||
const { Schema } = mongoose;
|
||||
|
||||
// Define the main part schema
|
||||
// Define the main part schema - pricing moved to PartSku
|
||||
const partSchema = new Schema(
|
||||
{
|
||||
_reference: { type: String, default: () => generateId()() },
|
||||
name: { type: String, required: true },
|
||||
fileName: { type: String, required: false },
|
||||
priceMode: { type: String, default: 'margin' },
|
||||
price: { type: Number, required: true },
|
||||
cost: { type: Number, required: true },
|
||||
margin: { type: Number, required: false },
|
||||
vendor: { type: Schema.Types.ObjectId, ref: 'vendor', required: true },
|
||||
file: { type: mongoose.SchemaTypes.ObjectId, ref: 'file', required: false },
|
||||
priceTaxRate: { type: Schema.Types.ObjectId, ref: 'taxRate', required: false },
|
||||
costTaxRate: { type: Schema.Types.ObjectId, ref: 'taxRate', required: false },
|
||||
priceWithTax: { type: Number, required: false },
|
||||
costWithTax: { type: Number, required: false },
|
||||
},
|
||||
{ timestamps: true }
|
||||
);
|
||||
|
||||
36
src/database/schemas/management/partsku.schema.js
Normal file
36
src/database/schemas/management/partsku.schema.js
Normal file
@ -0,0 +1,36 @@
|
||||
import mongoose from 'mongoose';
|
||||
import { generateId } from '../../utils.js';
|
||||
const { Schema } = mongoose;
|
||||
|
||||
// Define the main part SKU schema - pricing lives at SKU level
|
||||
const partSkuSchema = new Schema(
|
||||
{
|
||||
_reference: { type: String, default: () => generateId()() },
|
||||
sku: { type: String, required: true },
|
||||
part: { type: Schema.Types.ObjectId, ref: 'part', required: true },
|
||||
name: { type: String, required: true },
|
||||
description: { type: String, required: false },
|
||||
priceMode: { type: String, default: 'margin' },
|
||||
price: { type: Number, required: false },
|
||||
cost: { type: Number, required: false },
|
||||
margin: { type: Number, required: false },
|
||||
amount: { type: Number, required: false },
|
||||
vendor: { type: Schema.Types.ObjectId, ref: 'vendor', required: false },
|
||||
priceTaxRate: { type: Schema.Types.ObjectId, ref: 'taxRate', required: false },
|
||||
costTaxRate: { type: Schema.Types.ObjectId, ref: 'taxRate', required: false },
|
||||
priceWithTax: { type: Number, required: false },
|
||||
costWithTax: { type: Number, required: false },
|
||||
},
|
||||
{ timestamps: true }
|
||||
);
|
||||
|
||||
// Add virtual id getter
|
||||
partSkuSchema.virtual('id').get(function () {
|
||||
return this._id;
|
||||
});
|
||||
|
||||
// Configure JSON serialization to include virtuals
|
||||
partSkuSchema.set('toJSON', { virtuals: true });
|
||||
|
||||
// Create and export the model
|
||||
export const partSkuModel = mongoose.model('partSku', partSkuSchema);
|
||||
@ -2,11 +2,6 @@ import mongoose from 'mongoose';
|
||||
import { generateId } from '../../utils.js';
|
||||
const { Schema } = mongoose;
|
||||
|
||||
const partSchema = new Schema({
|
||||
part: { type: Schema.Types.ObjectId, ref: 'part', required: true },
|
||||
quantity: { type: Number, required: true },
|
||||
});
|
||||
|
||||
// Define the main product schema
|
||||
const productSchema = new Schema(
|
||||
{
|
||||
@ -14,13 +9,7 @@ const productSchema = new Schema(
|
||||
name: { type: String, required: true },
|
||||
tags: [{ type: String }],
|
||||
version: { type: String },
|
||||
priceMode: { type: String, default: 'margin' },
|
||||
margin: { type: Number, required: false },
|
||||
amount: { type: Number, required: false },
|
||||
vendor: { type: Schema.Types.ObjectId, ref: 'vendor', required: true },
|
||||
parts: [partSchema],
|
||||
priceTaxRate: { type: Schema.Types.ObjectId, ref: 'taxRate', required: false },
|
||||
costTaxRate: { type: Schema.Types.ObjectId, ref: 'taxRate', required: false },
|
||||
},
|
||||
{ timestamps: true }
|
||||
);
|
||||
|
||||
@ -2,6 +2,11 @@ import mongoose from 'mongoose';
|
||||
import { generateId } from '../../utils.js';
|
||||
const { Schema } = mongoose;
|
||||
|
||||
const partSkuUsageSchema = new Schema({
|
||||
partSku: { type: Schema.Types.ObjectId, ref: 'partSku', required: true },
|
||||
quantity: { type: Number, required: true },
|
||||
});
|
||||
|
||||
// Define the main product SKU schema
|
||||
const productSkuSchema = new Schema(
|
||||
{
|
||||
@ -10,6 +15,17 @@ const productSkuSchema = new Schema(
|
||||
product: { type: Schema.Types.ObjectId, ref: 'product', required: true },
|
||||
name: { type: String, required: true },
|
||||
description: { type: String, required: false },
|
||||
priceMode: { type: String, default: 'margin' },
|
||||
price: { type: Number, required: false },
|
||||
cost: { type: Number, required: false },
|
||||
margin: { type: Number, required: false },
|
||||
amount: { type: Number, required: false },
|
||||
vendor: { type: Schema.Types.ObjectId, ref: 'vendor', required: false },
|
||||
parts: [partSkuUsageSchema],
|
||||
priceTaxRate: { type: Schema.Types.ObjectId, ref: 'taxRate', required: false },
|
||||
costTaxRate: { type: Schema.Types.ObjectId, ref: 'taxRate', required: false },
|
||||
priceWithTax: { type: Number, required: false },
|
||||
costWithTax: { type: Number, required: false },
|
||||
},
|
||||
{ timestamps: true }
|
||||
);
|
||||
|
||||
@ -4,6 +4,7 @@ import { printerModel } from './production/printer.schema.js';
|
||||
import { filamentModel } from './management/filament.schema.js';
|
||||
import { gcodeFileModel } from './production/gcodefile.schema.js';
|
||||
import { partModel } from './management/part.schema.js';
|
||||
import { partSkuModel } from './management/partsku.schema.js';
|
||||
import { productModel } from './management/product.schema.js';
|
||||
import { productSkuModel } from './management/productsku.schema.js';
|
||||
import { vendorModel } from './management/vendor.schema.js';
|
||||
@ -67,6 +68,13 @@ export const models = {
|
||||
referenceField: '_reference',
|
||||
label: 'Part',
|
||||
},
|
||||
PSU: {
|
||||
model: partSkuModel,
|
||||
idField: '_id',
|
||||
type: 'partSku',
|
||||
referenceField: '_reference',
|
||||
label: 'Part SKU',
|
||||
},
|
||||
PRD: {
|
||||
model: productModel,
|
||||
idField: '_id',
|
||||
|
||||
@ -16,6 +16,7 @@ import {
|
||||
filamentRoutes,
|
||||
spotlightRoutes,
|
||||
partRoutes,
|
||||
partSkuRoutes,
|
||||
productRoutes,
|
||||
productSkuRoutes,
|
||||
vendorRoutes,
|
||||
@ -133,6 +134,7 @@ app.use('/subjobs', subJobRoutes);
|
||||
app.use('/gcodefiles', gcodeFileRoutes);
|
||||
app.use('/filaments', filamentRoutes);
|
||||
app.use('/parts', partRoutes);
|
||||
app.use('/partskus', partSkuRoutes);
|
||||
app.use('/products', productRoutes);
|
||||
app.use('/productskus', productSkuRoutes);
|
||||
app.use('/vendors', vendorRoutes);
|
||||
|
||||
@ -10,6 +10,7 @@ import gcodeFileRoutes from './production/gcodefiles.js';
|
||||
import filamentRoutes from './management/filaments.js';
|
||||
import spotlightRoutes from './misc/spotlight.js';
|
||||
import partRoutes from './management/parts.js';
|
||||
import partSkuRoutes from './management/partskus.js';
|
||||
import productRoutes from './management/products.js';
|
||||
import productSkuRoutes from './management/productskus.js';
|
||||
import vendorRoutes from './management/vendors.js';
|
||||
@ -56,6 +57,7 @@ export {
|
||||
filamentRoutes,
|
||||
spotlightRoutes,
|
||||
partRoutes,
|
||||
partSkuRoutes,
|
||||
productRoutes,
|
||||
productSkuRoutes,
|
||||
vendorRoutes,
|
||||
|
||||
@ -18,7 +18,7 @@ import {
|
||||
// list of part stocks
|
||||
router.get('/', isAuthenticated, (req, res) => {
|
||||
const { page, limit, property, search, sort, order } = req.query;
|
||||
const allowedFilters = ['part', 'state', 'startingQuantity', 'currentQuantity', 'part._id'];
|
||||
const allowedFilters = ['partSku', 'state', 'startingQuantity', 'currentQuantity', 'partSku._id'];
|
||||
const filter = getFilter(req.query, allowedFilters);
|
||||
listPartStocksRouteHandler(req, res, page, limit, property, filter, search, sort, order);
|
||||
});
|
||||
|
||||
@ -18,14 +18,14 @@ import {
|
||||
|
||||
router.get('/', isAuthenticated, (req, res) => {
|
||||
const { page, limit, property, search, sort, order } = req.query;
|
||||
const allowedFilters = ['product', 'state', 'currentQuantity', 'product._id'];
|
||||
const allowedFilters = ['productSku', 'state', 'currentQuantity', 'productSku._id'];
|
||||
const filter = getFilter(req.query, allowedFilters);
|
||||
listProductStocksRouteHandler(req, res, page, limit, property, filter, search, sort, order);
|
||||
});
|
||||
|
||||
router.get('/properties', isAuthenticated, (req, res) => {
|
||||
let properties = convertPropertiesString(req.query.properties);
|
||||
const allowedFilters = ['product', 'state.type'];
|
||||
const allowedFilters = ['productSku', 'state.type'];
|
||||
const filter = getFilter(req.query, allowedFilters, false);
|
||||
var masterFilter = {};
|
||||
if (req.query.masterFilter) {
|
||||
|
||||
59
src/routes/management/partskus.js
Normal file
59
src/routes/management/partskus.js
Normal file
@ -0,0 +1,59 @@
|
||||
import express from 'express';
|
||||
import { isAuthenticated } from '../../keycloak.js';
|
||||
import { getFilter, convertPropertiesString } from '../../utils.js';
|
||||
|
||||
const router = express.Router();
|
||||
import {
|
||||
listPartSkusRouteHandler,
|
||||
getPartSkuRouteHandler,
|
||||
editPartSkuRouteHandler,
|
||||
newPartSkuRouteHandler,
|
||||
deletePartSkuRouteHandler,
|
||||
listPartSkusByPropertiesRouteHandler,
|
||||
getPartSkuStatsRouteHandler,
|
||||
getPartSkuHistoryRouteHandler,
|
||||
} from '../../services/management/partskus.js';
|
||||
|
||||
router.get('/', isAuthenticated, (req, res) => {
|
||||
const { page, limit, property, search, sort, order } = req.query;
|
||||
const allowedFilters = ['_id', 'sku', 'part', 'part._id', 'name', 'cost', 'price'];
|
||||
const filter = getFilter(req.query, allowedFilters);
|
||||
listPartSkusRouteHandler(req, res, page, limit, property, filter, search, sort, order);
|
||||
});
|
||||
|
||||
router.get('/properties', isAuthenticated, (req, res) => {
|
||||
let properties = convertPropertiesString(req.query.properties);
|
||||
const allowedFilters = ['part', 'part._id'];
|
||||
const filter = getFilter(req.query, allowedFilters, false);
|
||||
let masterFilter = {};
|
||||
if (req.query.masterFilter) {
|
||||
masterFilter = JSON.parse(req.query.masterFilter);
|
||||
}
|
||||
listPartSkusByPropertiesRouteHandler(req, res, properties, filter, masterFilter);
|
||||
});
|
||||
|
||||
router.post('/', isAuthenticated, (req, res) => {
|
||||
newPartSkuRouteHandler(req, res);
|
||||
});
|
||||
|
||||
router.get('/stats', isAuthenticated, (req, res) => {
|
||||
getPartSkuStatsRouteHandler(req, res);
|
||||
});
|
||||
|
||||
router.get('/history', isAuthenticated, (req, res) => {
|
||||
getPartSkuHistoryRouteHandler(req, res);
|
||||
});
|
||||
|
||||
router.get('/:id', isAuthenticated, (req, res) => {
|
||||
getPartSkuRouteHandler(req, res);
|
||||
});
|
||||
|
||||
router.put('/:id', isAuthenticated, async (req, res) => {
|
||||
editPartSkuRouteHandler(req, res);
|
||||
});
|
||||
|
||||
router.delete('/:id', isAuthenticated, async (req, res) => {
|
||||
deletePartSkuRouteHandler(req, res);
|
||||
});
|
||||
|
||||
export default router;
|
||||
@ -16,7 +16,7 @@ import {
|
||||
|
||||
router.get('/', isAuthenticated, (req, res) => {
|
||||
const { page, limit, property, search, sort, order } = req.query;
|
||||
const allowedFilters = ['_id', 'sku', 'product', 'product._id', 'name'];
|
||||
const allowedFilters = ['_id', 'sku', 'product', 'product._id', 'name', 'cost', 'price'];
|
||||
const filter = getFilter(req.query, allowedFilters);
|
||||
listProductSkusRouteHandler(req, res, page, limit, property, filter, search, sort, order);
|
||||
});
|
||||
|
||||
@ -36,7 +36,7 @@ export const listPartStocksRouteHandler = async (
|
||||
search,
|
||||
sort,
|
||||
order,
|
||||
populate: [{ path: 'part' }],
|
||||
populate: [{ path: 'partSku' }],
|
||||
});
|
||||
|
||||
if (result?.error) {
|
||||
@ -60,7 +60,7 @@ export const listPartStocksByPropertiesRouteHandler = async (
|
||||
model: partStockModel,
|
||||
properties,
|
||||
filter,
|
||||
populate: ['part'],
|
||||
populate: ['partSku'],
|
||||
masterFilter,
|
||||
});
|
||||
|
||||
@ -79,7 +79,7 @@ export const getPartStockRouteHandler = async (req, res) => {
|
||||
const result = await getObject({
|
||||
model: partStockModel,
|
||||
id,
|
||||
populate: [{ path: 'part' }],
|
||||
populate: [{ path: 'partSku' }],
|
||||
});
|
||||
if (result?.error) {
|
||||
logger.warn(`Part Stock not found with supplied id.`);
|
||||
@ -146,7 +146,7 @@ export const newPartStockRouteHandler = async (req, res) => {
|
||||
updatedAt: new Date(),
|
||||
startingQuantity: req.body.startingQuantity,
|
||||
currentQuantity: req.body.currentQuantity,
|
||||
part: req.body.part,
|
||||
partSku: req.body.partSku,
|
||||
state: req.body.state,
|
||||
};
|
||||
const result = await newObject({
|
||||
|
||||
@ -14,7 +14,7 @@ import {
|
||||
getModelHistory,
|
||||
checkStates,
|
||||
} from '../../database/database.js';
|
||||
import { productModel } from '../../database/schemas/management/product.schema.js';
|
||||
import { productSkuModel } from '../../database/schemas/management/productsku.schema.js';
|
||||
const logger = log4js.getLogger('Product Stocks');
|
||||
logger.level = config.server.logLevel;
|
||||
|
||||
@ -38,7 +38,7 @@ export const listProductStocksRouteHandler = async (
|
||||
search,
|
||||
sort,
|
||||
order,
|
||||
populate: [{ path: 'product' }, { path: 'partStocks.partStock' }],
|
||||
populate: [{ path: 'productSku' }, { path: 'partStocks.partStock' }],
|
||||
});
|
||||
|
||||
if (result?.error) {
|
||||
@ -62,7 +62,7 @@ export const listProductStocksByPropertiesRouteHandler = async (
|
||||
model: productStockModel,
|
||||
properties,
|
||||
filter,
|
||||
populate: ['product', 'partStocks.partStock'],
|
||||
populate: ['productSku', 'partStocks.partStock'],
|
||||
masterFilter,
|
||||
});
|
||||
|
||||
@ -81,7 +81,7 @@ export const getProductStockRouteHandler = async (req, res) => {
|
||||
const result = await getObject({
|
||||
model: productStockModel,
|
||||
id,
|
||||
populate: [{ path: 'partStocks.part' }, { path: 'partStocks.partStock' }, { path: 'product' }],
|
||||
populate: [{ path: 'partStocks.partSku' }, { path: 'partStocks.partStock' }, { path: 'productSku' }],
|
||||
});
|
||||
if (result?.error) {
|
||||
logger.warn(`Product Stock not found with supplied id.`);
|
||||
@ -114,6 +114,7 @@ export const editProductStockRouteHandler = async (req, res) => {
|
||||
partStocks: req.body?.partStocks?.map((partStock) => ({
|
||||
quantity: partStock.quantity,
|
||||
partStock: partStock.partStock,
|
||||
partSku: partStock.partSku,
|
||||
})),
|
||||
};
|
||||
|
||||
@ -162,18 +163,18 @@ export const editMultipleProductStocksRouteHandler = async (req, res) => {
|
||||
};
|
||||
|
||||
export const newProductStockRouteHandler = async (req, res) => {
|
||||
const productId = new mongoose.Types.ObjectId(req.body.product?._id);
|
||||
const product = await getObject({
|
||||
model: productModel,
|
||||
id: productId,
|
||||
const productSkuId = new mongoose.Types.ObjectId(req.body.productSku?._id);
|
||||
const productSku = await getObject({
|
||||
model: productSkuModel,
|
||||
id: productSkuId,
|
||||
});
|
||||
const newData = {
|
||||
updatedAt: new Date(),
|
||||
currentQuantity: req.body.currentQuantity,
|
||||
product: req.body.product,
|
||||
productSku: req.body.productSku,
|
||||
state: req.body.state ?? { type: 'draft' },
|
||||
partStocks: product.parts.map((part) => ({
|
||||
part: part.part,
|
||||
partStocks: (productSku.parts || []).map((part) => ({
|
||||
partSku: part.partSku,
|
||||
quantity: part.quantity,
|
||||
partStock: undefined,
|
||||
})),
|
||||
|
||||
@ -35,7 +35,7 @@ export const listPartsRouteHandler = async (
|
||||
search,
|
||||
sort,
|
||||
order,
|
||||
populate: ['vendor'],
|
||||
populate: [],
|
||||
});
|
||||
|
||||
if (result?.error) {
|
||||
@ -53,20 +53,7 @@ export const listPartsByPropertiesRouteHandler = async (req, res, properties = '
|
||||
model: partModel,
|
||||
properties,
|
||||
filter,
|
||||
populate: [
|
||||
{
|
||||
path: 'vendor',
|
||||
from: 'vendors',
|
||||
},
|
||||
{
|
||||
path: 'priceTaxRate',
|
||||
from: 'taxrates',
|
||||
},
|
||||
{
|
||||
path: 'costTaxRate',
|
||||
from: 'taxrates',
|
||||
},
|
||||
],
|
||||
populate: [],
|
||||
});
|
||||
|
||||
if (result?.error) {
|
||||
@ -84,7 +71,7 @@ export const getPartRouteHandler = async (req, res) => {
|
||||
const result = await getObject({
|
||||
model: partModel,
|
||||
id,
|
||||
populate: ['vendor', 'priceTaxRate', 'costTaxRate'],
|
||||
populate: [],
|
||||
});
|
||||
if (result?.error) {
|
||||
logger.warn(`Part not found with supplied id.`);
|
||||
@ -103,16 +90,8 @@ export const editPartRouteHandler = async (req, res) => {
|
||||
const updateData = {
|
||||
updatedAt: new Date(),
|
||||
name: req.body?.name,
|
||||
fileName: req.body?.fileName,
|
||||
file: req.body?.file,
|
||||
vendor: req.body?.vendor,
|
||||
margin: req.body?.margin,
|
||||
price: req.body?.price,
|
||||
cost: req.body?.cost,
|
||||
priceMode: req.body?.priceMode,
|
||||
priceTaxRate: req.body?.priceTaxRate,
|
||||
costTaxRate: req.body?.costTaxRate,
|
||||
priceWithTax: req.body?.priceWithTax,
|
||||
costWithTax: req.body?.costWithTax,
|
||||
};
|
||||
// Create audit log before updating
|
||||
const result = await editObject({
|
||||
@ -137,16 +116,8 @@ export const newPartRouteHandler = async (req, res) => {
|
||||
const newData = {
|
||||
updatedAt: new Date(),
|
||||
name: req.body?.name,
|
||||
fileName: req.body?.fileName,
|
||||
file: req.body?.file,
|
||||
vendor: req.body?.vendor,
|
||||
margin: req.body?.margin,
|
||||
price: req.body?.price,
|
||||
cost: req.body?.cost,
|
||||
priceMode: req.body?.priceMode,
|
||||
priceTaxRate: req.body?.priceTaxRate,
|
||||
costTaxRate: req.body?.costTaxRate,
|
||||
priceWithTax: req.body?.priceWithTax,
|
||||
costWithTax: req.body?.costWithTax,
|
||||
};
|
||||
|
||||
const result = await newObject({
|
||||
|
||||
202
src/services/management/partskus.js
Normal file
202
src/services/management/partskus.js
Normal file
@ -0,0 +1,202 @@
|
||||
import config from '../../config.js';
|
||||
import { partSkuModel } from '../../database/schemas/management/partsku.schema.js';
|
||||
import log4js from 'log4js';
|
||||
import mongoose from 'mongoose';
|
||||
import {
|
||||
deleteObject,
|
||||
listObjects,
|
||||
getObject,
|
||||
editObject,
|
||||
newObject,
|
||||
listObjectsByProperties,
|
||||
getModelStats,
|
||||
getModelHistory,
|
||||
} from '../../database/database.js';
|
||||
const logger = log4js.getLogger('Part SKUs');
|
||||
logger.level = config.server.logLevel;
|
||||
|
||||
export const listPartSkusRouteHandler = async (
|
||||
req,
|
||||
res,
|
||||
page = 1,
|
||||
limit = 25,
|
||||
property = '',
|
||||
filter = {},
|
||||
search = '',
|
||||
sort = '',
|
||||
order = 'ascend'
|
||||
) => {
|
||||
const result = await listObjects({
|
||||
model: partSkuModel,
|
||||
page,
|
||||
limit,
|
||||
property,
|
||||
filter,
|
||||
search,
|
||||
sort,
|
||||
order,
|
||||
populate: ['part', 'vendor', 'priceTaxRate', 'costTaxRate'],
|
||||
});
|
||||
|
||||
if (result?.error) {
|
||||
logger.error('Error listing part SKUs.');
|
||||
res.status(result.code).send(result);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug(`List of part SKUs (Page ${page}, Limit ${limit}). Count: ${result.length}.`);
|
||||
res.send(result);
|
||||
};
|
||||
|
||||
export const listPartSkusByPropertiesRouteHandler = async (
|
||||
req,
|
||||
res,
|
||||
properties = '',
|
||||
filter = {},
|
||||
masterFilter = {}
|
||||
) => {
|
||||
const result = await listObjectsByProperties({
|
||||
model: partSkuModel,
|
||||
properties,
|
||||
filter,
|
||||
populate: ['part', 'vendor', 'priceTaxRate', 'costTaxRate'],
|
||||
masterFilter,
|
||||
});
|
||||
|
||||
if (result?.error) {
|
||||
logger.error('Error listing part SKUs.');
|
||||
res.status(result.code).send(result);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug(`List of part SKUs. Count: ${result.length}`);
|
||||
res.send(result);
|
||||
};
|
||||
|
||||
export const getPartSkuRouteHandler = async (req, res) => {
|
||||
const id = req.params.id;
|
||||
const result = await getObject({
|
||||
model: partSkuModel,
|
||||
id,
|
||||
populate: ['part', 'vendor', 'priceTaxRate', 'costTaxRate'],
|
||||
});
|
||||
if (result?.error) {
|
||||
logger.warn(`Part SKU not found with supplied id.`);
|
||||
return res.status(result.code).send(result);
|
||||
}
|
||||
logger.debug(`Retrieved part SKU with ID: ${id}`);
|
||||
res.send(result);
|
||||
};
|
||||
|
||||
export const editPartSkuRouteHandler = async (req, res) => {
|
||||
const id = new mongoose.Types.ObjectId(req.params.id);
|
||||
|
||||
logger.trace(`Part SKU with ID: ${id}`);
|
||||
|
||||
const updateData = {
|
||||
updatedAt: new Date(),
|
||||
sku: req.body?.sku,
|
||||
part: req.body?.part,
|
||||
name: req.body?.name,
|
||||
description: req.body?.description,
|
||||
priceMode: req.body?.priceMode,
|
||||
price: req.body?.price,
|
||||
cost: req.body?.cost,
|
||||
margin: req.body?.margin,
|
||||
amount: req.body?.amount,
|
||||
vendor: req.body?.vendor,
|
||||
priceTaxRate: req.body?.priceTaxRate,
|
||||
costTaxRate: req.body?.costTaxRate,
|
||||
priceWithTax: req.body?.priceWithTax,
|
||||
costWithTax: req.body?.costWithTax,
|
||||
};
|
||||
|
||||
const result = await editObject({
|
||||
model: partSkuModel,
|
||||
id,
|
||||
updateData,
|
||||
user: req.user,
|
||||
});
|
||||
|
||||
if (result.error) {
|
||||
logger.error('Error editing part SKU:', result.error);
|
||||
res.status(result.code || 500).send(result);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug(`Edited part SKU with ID: ${id}`);
|
||||
res.send(result);
|
||||
};
|
||||
|
||||
export const newPartSkuRouteHandler = async (req, res) => {
|
||||
const newData = {
|
||||
sku: req.body?.sku,
|
||||
part: req.body?.part,
|
||||
name: req.body?.name,
|
||||
description: req.body?.description,
|
||||
priceMode: req.body?.priceMode,
|
||||
price: req.body?.price,
|
||||
cost: req.body?.cost,
|
||||
margin: req.body?.margin,
|
||||
amount: req.body?.amount,
|
||||
vendor: req.body?.vendor,
|
||||
priceTaxRate: req.body?.priceTaxRate,
|
||||
costTaxRate: req.body?.costTaxRate,
|
||||
priceWithTax: req.body?.priceWithTax,
|
||||
costWithTax: req.body?.costWithTax,
|
||||
};
|
||||
|
||||
const result = await newObject({
|
||||
model: partSkuModel,
|
||||
newData,
|
||||
user: req.user,
|
||||
});
|
||||
if (result.error) {
|
||||
logger.error('No part SKU created:', result.error);
|
||||
return res.status(result.code).send(result);
|
||||
}
|
||||
|
||||
logger.debug(`New part SKU with ID: ${result._id}`);
|
||||
res.send(result);
|
||||
};
|
||||
|
||||
export const deletePartSkuRouteHandler = async (req, res) => {
|
||||
const id = new mongoose.Types.ObjectId(req.params.id);
|
||||
|
||||
logger.trace(`Part SKU with ID: ${id}`);
|
||||
|
||||
const result = await deleteObject({
|
||||
model: partSkuModel,
|
||||
id,
|
||||
user: req.user,
|
||||
});
|
||||
if (result.error) {
|
||||
logger.error('No part SKU deleted:', result.error);
|
||||
return res.status(result.code).send(result);
|
||||
}
|
||||
|
||||
logger.debug(`Deleted part SKU with ID: ${id}`);
|
||||
res.send(result);
|
||||
};
|
||||
|
||||
export const getPartSkuStatsRouteHandler = async (req, res) => {
|
||||
const result = await getModelStats({ model: partSkuModel });
|
||||
if (result?.error) {
|
||||
logger.error('Error fetching part SKU stats:', result.error);
|
||||
return res.status(result.code).send(result);
|
||||
}
|
||||
logger.trace('Part SKU stats:', result);
|
||||
res.send(result);
|
||||
};
|
||||
|
||||
export const getPartSkuHistoryRouteHandler = async (req, res) => {
|
||||
const from = req.query.from;
|
||||
const to = req.query.to;
|
||||
const result = await getModelHistory({ model: partSkuModel, from, to });
|
||||
if (result?.error) {
|
||||
logger.error('Error fetching part SKU history:', result.error);
|
||||
return res.status(result.code).send(result);
|
||||
}
|
||||
logger.trace('Part SKU history:', result);
|
||||
res.send(result);
|
||||
};
|
||||
@ -76,7 +76,7 @@ export const getProductRouteHandler = async (req, res) => {
|
||||
const result = await getObject({
|
||||
model: productModel,
|
||||
id,
|
||||
populate: ['vendor', 'parts.part'],
|
||||
populate: ['vendor'],
|
||||
});
|
||||
if (result?.error) {
|
||||
logger.warn(`Product not found with supplied id.`);
|
||||
@ -97,11 +97,7 @@ export const editProductRouteHandler = async (req, res) => {
|
||||
name: req.body?.name,
|
||||
tags: req.body?.tags,
|
||||
version: req.body?.version,
|
||||
margin: req.body.margin,
|
||||
amount: req.body.amount,
|
||||
priceMode: req.body.priceMode,
|
||||
vendor: req.body.vendor,
|
||||
parts: req.body.parts,
|
||||
};
|
||||
// Create audit log before updating
|
||||
const result = await editObject({
|
||||
@ -128,11 +124,7 @@ export const newProductRouteHandler = async (req, res) => {
|
||||
name: req.body?.name,
|
||||
tags: req.body?.tags,
|
||||
version: req.body?.version,
|
||||
margin: req.body.margin,
|
||||
amount: req.body.amount,
|
||||
priceMode: req.body.priceMode,
|
||||
vendor: req.body.vendor,
|
||||
parts: req.body.parts,
|
||||
};
|
||||
|
||||
const result = await newObject({
|
||||
|
||||
@ -35,7 +35,7 @@ export const listProductSkusRouteHandler = async (
|
||||
search,
|
||||
sort,
|
||||
order,
|
||||
populate: ['product'],
|
||||
populate: ['product', 'vendor', 'priceTaxRate', 'costTaxRate', 'parts.partSku'],
|
||||
});
|
||||
|
||||
if (result?.error) {
|
||||
@ -59,7 +59,7 @@ export const listProductSkusByPropertiesRouteHandler = async (
|
||||
model: productSkuModel,
|
||||
properties,
|
||||
filter,
|
||||
populate: ['product'],
|
||||
populate: ['product', 'vendor', 'priceTaxRate', 'costTaxRate', 'parts.partSku'],
|
||||
masterFilter,
|
||||
});
|
||||
|
||||
@ -78,7 +78,7 @@ export const getProductSkuRouteHandler = async (req, res) => {
|
||||
const result = await getObject({
|
||||
model: productSkuModel,
|
||||
id,
|
||||
populate: ['product'],
|
||||
populate: ['product', 'vendor', 'priceTaxRate', 'costTaxRate', 'parts.partSku'],
|
||||
});
|
||||
if (result?.error) {
|
||||
logger.warn(`Product SKU not found with supplied id.`);
|
||||
@ -99,6 +99,17 @@ export const editProductSkuRouteHandler = async (req, res) => {
|
||||
product: req.body?.product,
|
||||
name: req.body?.name,
|
||||
description: req.body?.description,
|
||||
priceMode: req.body?.priceMode,
|
||||
price: req.body?.price,
|
||||
cost: req.body?.cost,
|
||||
margin: req.body?.margin,
|
||||
amount: req.body?.amount,
|
||||
vendor: req.body?.vendor,
|
||||
parts: req.body?.parts,
|
||||
priceTaxRate: req.body?.priceTaxRate,
|
||||
costTaxRate: req.body?.costTaxRate,
|
||||
priceWithTax: req.body?.priceWithTax,
|
||||
costWithTax: req.body?.costWithTax,
|
||||
};
|
||||
|
||||
const result = await editObject({
|
||||
@ -124,6 +135,17 @@ export const newProductSkuRouteHandler = async (req, res) => {
|
||||
product: req.body?.product,
|
||||
name: req.body?.name,
|
||||
description: req.body?.description,
|
||||
priceMode: req.body?.priceMode,
|
||||
price: req.body?.price,
|
||||
cost: req.body?.cost,
|
||||
margin: req.body?.margin,
|
||||
amount: req.body?.amount,
|
||||
vendor: req.body?.vendor,
|
||||
parts: req.body?.parts,
|
||||
priceTaxRate: req.body?.priceTaxRate,
|
||||
costTaxRate: req.body?.costTaxRate,
|
||||
priceWithTax: req.body?.priceWithTax,
|
||||
costWithTax: req.body?.costWithTax,
|
||||
};
|
||||
|
||||
const result = await newObject({
|
||||
|
||||
@ -63,9 +63,10 @@ function getModelFilterFields(objectType) {
|
||||
job: ['printer', 'gcodeFile'],
|
||||
subJob: ['job'],
|
||||
filamentStock: ['filament'],
|
||||
partStock: ['part'],
|
||||
productStock: ['product'],
|
||||
productSku: ['product'],
|
||||
partStock: ['partSku'],
|
||||
partSku: ['part', 'vendor', 'priceTaxRate', 'costTaxRate'],
|
||||
productStock: ['productSku'],
|
||||
productSku: ['product', 'vendor', 'priceTaxRate', 'costTaxRate'],
|
||||
purchaseOrder: ['vendor'],
|
||||
orderItem: ['order._id', 'orderType', 'item._id', 'itemType', 'shipment._id'],
|
||||
shipment: ['order._id', 'orderType', 'courierService._id'],
|
||||
|
||||
@ -69,9 +69,10 @@ function getModelFilterFields(objectType) {
|
||||
job: ['printer', 'gcodeFile'],
|
||||
subJob: ['job'],
|
||||
filamentStock: ['filament'],
|
||||
partStock: ['part'],
|
||||
productStock: ['product'],
|
||||
productSku: ['product'],
|
||||
partStock: ['partSku'],
|
||||
partSku: ['part', 'vendor', 'priceTaxRate', 'costTaxRate'],
|
||||
productStock: ['productSku'],
|
||||
productSku: ['product', 'vendor', 'priceTaxRate', 'costTaxRate'],
|
||||
purchaseOrder: ['vendor'],
|
||||
orderItem: ['order._id', 'orderType', 'item._id', 'itemType', 'shipment._id'],
|
||||
shipment: ['order._id', 'orderType', 'courierService._id'],
|
||||
|
||||
@ -343,9 +343,10 @@ function getModelFilterFields(objectType) {
|
||||
job: ['printer', 'gcodeFile'],
|
||||
subJob: ['job'],
|
||||
filamentStock: ['filament'],
|
||||
partStock: ['part'],
|
||||
productStock: ['product'],
|
||||
productSku: ['product'],
|
||||
partStock: ['partSku'],
|
||||
partSku: ['part', 'vendor', 'priceTaxRate', 'costTaxRate'],
|
||||
productStock: ['productSku'],
|
||||
productSku: ['product', 'vendor', 'priceTaxRate', 'costTaxRate'],
|
||||
purchaseOrder: ['vendor'],
|
||||
orderItem: ['order._id', 'orderType', 'item._id', 'itemType', 'shipment._id'],
|
||||
shipment: ['order._id', 'orderType', 'courierService._id'],
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user