Implemented Product SKU.
All checks were successful
farmcontrol/farmcontrol-api/pipeline/head This commit looks good
All checks were successful
farmcontrol/farmcontrol-api/pipeline/head This commit looks good
This commit is contained in:
parent
866c29f33f
commit
4ea168f17f
26
src/database/schemas/management/productsku.schema.js
Normal file
26
src/database/schemas/management/productsku.schema.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import mongoose from 'mongoose';
|
||||||
|
import { generateId } from '../../utils.js';
|
||||||
|
const { Schema } = mongoose;
|
||||||
|
|
||||||
|
// Define the main product SKU schema
|
||||||
|
const productSkuSchema = new Schema(
|
||||||
|
{
|
||||||
|
_reference: { type: String, default: () => generateId()() },
|
||||||
|
sku: { type: String, required: true },
|
||||||
|
product: { type: Schema.Types.ObjectId, ref: 'product', required: true },
|
||||||
|
name: { type: String, required: true },
|
||||||
|
description: { type: String, required: false },
|
||||||
|
},
|
||||||
|
{ timestamps: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add virtual id getter
|
||||||
|
productSkuSchema.virtual('id').get(function () {
|
||||||
|
return this._id;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Configure JSON serialization to include virtuals
|
||||||
|
productSkuSchema.set('toJSON', { virtuals: true });
|
||||||
|
|
||||||
|
// Create and export the model
|
||||||
|
export const productSkuModel = mongoose.model('productSku', productSkuSchema);
|
||||||
@ -5,6 +5,7 @@ import { filamentModel } from './management/filament.schema.js';
|
|||||||
import { gcodeFileModel } from './production/gcodefile.schema.js';
|
import { gcodeFileModel } from './production/gcodefile.schema.js';
|
||||||
import { partModel } from './management/part.schema.js';
|
import { partModel } from './management/part.schema.js';
|
||||||
import { productModel } from './management/product.schema.js';
|
import { productModel } from './management/product.schema.js';
|
||||||
|
import { productSkuModel } from './management/productsku.schema.js';
|
||||||
import { vendorModel } from './management/vendor.schema.js';
|
import { vendorModel } from './management/vendor.schema.js';
|
||||||
import { filamentStockModel } from './inventory/filamentstock.schema.js';
|
import { filamentStockModel } from './inventory/filamentstock.schema.js';
|
||||||
import { purchaseOrderModel } from './inventory/purchaseorder.schema.js';
|
import { purchaseOrderModel } from './inventory/purchaseorder.schema.js';
|
||||||
@ -73,6 +74,13 @@ export const models = {
|
|||||||
referenceField: '_reference',
|
referenceField: '_reference',
|
||||||
label: 'Product',
|
label: 'Product',
|
||||||
},
|
},
|
||||||
|
SKU: {
|
||||||
|
model: productSkuModel,
|
||||||
|
idField: '_id',
|
||||||
|
type: 'productSku',
|
||||||
|
referenceField: '_reference',
|
||||||
|
label: 'Product SKU',
|
||||||
|
},
|
||||||
VEN: {
|
VEN: {
|
||||||
model: vendorModel,
|
model: vendorModel,
|
||||||
idField: '_id',
|
idField: '_id',
|
||||||
|
|||||||
@ -17,6 +17,7 @@ import {
|
|||||||
spotlightRoutes,
|
spotlightRoutes,
|
||||||
partRoutes,
|
partRoutes,
|
||||||
productRoutes,
|
productRoutes,
|
||||||
|
productSkuRoutes,
|
||||||
vendorRoutes,
|
vendorRoutes,
|
||||||
materialRoutes,
|
materialRoutes,
|
||||||
partStockRoutes,
|
partStockRoutes,
|
||||||
@ -133,6 +134,7 @@ app.use('/gcodefiles', gcodeFileRoutes);
|
|||||||
app.use('/filaments', filamentRoutes);
|
app.use('/filaments', filamentRoutes);
|
||||||
app.use('/parts', partRoutes);
|
app.use('/parts', partRoutes);
|
||||||
app.use('/products', productRoutes);
|
app.use('/products', productRoutes);
|
||||||
|
app.use('/productskus', productSkuRoutes);
|
||||||
app.use('/vendors', vendorRoutes);
|
app.use('/vendors', vendorRoutes);
|
||||||
app.use('/materials', materialRoutes);
|
app.use('/materials', materialRoutes);
|
||||||
app.use('/partstocks', partStockRoutes);
|
app.use('/partstocks', partStockRoutes);
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import filamentRoutes from './management/filaments.js';
|
|||||||
import spotlightRoutes from './misc/spotlight.js';
|
import spotlightRoutes from './misc/spotlight.js';
|
||||||
import partRoutes from './management/parts.js';
|
import partRoutes from './management/parts.js';
|
||||||
import productRoutes from './management/products.js';
|
import productRoutes from './management/products.js';
|
||||||
|
import productSkuRoutes from './management/productskus.js';
|
||||||
import vendorRoutes from './management/vendors.js';
|
import vendorRoutes from './management/vendors.js';
|
||||||
import materialRoutes from './management/materials.js';
|
import materialRoutes from './management/materials.js';
|
||||||
import partStockRoutes from './inventory/partstocks.js';
|
import partStockRoutes from './inventory/partstocks.js';
|
||||||
@ -56,6 +57,7 @@ export {
|
|||||||
spotlightRoutes,
|
spotlightRoutes,
|
||||||
partRoutes,
|
partRoutes,
|
||||||
productRoutes,
|
productRoutes,
|
||||||
|
productSkuRoutes,
|
||||||
vendorRoutes,
|
vendorRoutes,
|
||||||
materialRoutes,
|
materialRoutes,
|
||||||
partStockRoutes,
|
partStockRoutes,
|
||||||
|
|||||||
59
src/routes/management/productskus.js
Normal file
59
src/routes/management/productskus.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 {
|
||||||
|
listProductSkusRouteHandler,
|
||||||
|
getProductSkuRouteHandler,
|
||||||
|
editProductSkuRouteHandler,
|
||||||
|
newProductSkuRouteHandler,
|
||||||
|
deleteProductSkuRouteHandler,
|
||||||
|
listProductSkusByPropertiesRouteHandler,
|
||||||
|
getProductSkuStatsRouteHandler,
|
||||||
|
getProductSkuHistoryRouteHandler,
|
||||||
|
} from '../../services/management/productskus.js';
|
||||||
|
|
||||||
|
router.get('/', isAuthenticated, (req, res) => {
|
||||||
|
const { page, limit, property, search, sort, order } = req.query;
|
||||||
|
const allowedFilters = ['_id', 'sku', 'product', 'product._id', 'name'];
|
||||||
|
const filter = getFilter(req.query, allowedFilters);
|
||||||
|
listProductSkusRouteHandler(req, res, page, limit, property, filter, search, sort, order);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/properties', isAuthenticated, (req, res) => {
|
||||||
|
let properties = convertPropertiesString(req.query.properties);
|
||||||
|
const allowedFilters = ['product', 'product._id'];
|
||||||
|
const filter = getFilter(req.query, allowedFilters, false);
|
||||||
|
let masterFilter = {};
|
||||||
|
if (req.query.masterFilter) {
|
||||||
|
masterFilter = JSON.parse(req.query.masterFilter);
|
||||||
|
}
|
||||||
|
listProductSkusByPropertiesRouteHandler(req, res, properties, filter, masterFilter);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post('/', isAuthenticated, (req, res) => {
|
||||||
|
newProductSkuRouteHandler(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/stats', isAuthenticated, (req, res) => {
|
||||||
|
getProductSkuStatsRouteHandler(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/history', isAuthenticated, (req, res) => {
|
||||||
|
getProductSkuHistoryRouteHandler(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/:id', isAuthenticated, (req, res) => {
|
||||||
|
getProductSkuRouteHandler(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.put('/:id', isAuthenticated, async (req, res) => {
|
||||||
|
editProductSkuRouteHandler(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.delete('/:id', isAuthenticated, async (req, res) => {
|
||||||
|
deleteProductSkuRouteHandler(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
||||||
182
src/services/management/productskus.js
Normal file
182
src/services/management/productskus.js
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
import config from '../../config.js';
|
||||||
|
import { productSkuModel } from '../../database/schemas/management/productsku.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('Product SKUs');
|
||||||
|
logger.level = config.server.logLevel;
|
||||||
|
|
||||||
|
export const listProductSkusRouteHandler = async (
|
||||||
|
req,
|
||||||
|
res,
|
||||||
|
page = 1,
|
||||||
|
limit = 25,
|
||||||
|
property = '',
|
||||||
|
filter = {},
|
||||||
|
search = '',
|
||||||
|
sort = '',
|
||||||
|
order = 'ascend'
|
||||||
|
) => {
|
||||||
|
const result = await listObjects({
|
||||||
|
model: productSkuModel,
|
||||||
|
page,
|
||||||
|
limit,
|
||||||
|
property,
|
||||||
|
filter,
|
||||||
|
search,
|
||||||
|
sort,
|
||||||
|
order,
|
||||||
|
populate: ['product'],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result?.error) {
|
||||||
|
logger.error('Error listing product SKUs.');
|
||||||
|
res.status(result.code).send(result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug(`List of product SKUs (Page ${page}, Limit ${limit}). Count: ${result.length}.`);
|
||||||
|
res.send(result);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const listProductSkusByPropertiesRouteHandler = async (
|
||||||
|
req,
|
||||||
|
res,
|
||||||
|
properties = '',
|
||||||
|
filter = {},
|
||||||
|
masterFilter = {}
|
||||||
|
) => {
|
||||||
|
const result = await listObjectsByProperties({
|
||||||
|
model: productSkuModel,
|
||||||
|
properties,
|
||||||
|
filter,
|
||||||
|
populate: ['product'],
|
||||||
|
masterFilter,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result?.error) {
|
||||||
|
logger.error('Error listing product SKUs.');
|
||||||
|
res.status(result.code).send(result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug(`List of product SKUs. Count: ${result.length}`);
|
||||||
|
res.send(result);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getProductSkuRouteHandler = async (req, res) => {
|
||||||
|
const id = req.params.id;
|
||||||
|
const result = await getObject({
|
||||||
|
model: productSkuModel,
|
||||||
|
id,
|
||||||
|
populate: ['product'],
|
||||||
|
});
|
||||||
|
if (result?.error) {
|
||||||
|
logger.warn(`Product SKU not found with supplied id.`);
|
||||||
|
return res.status(result.code).send(result);
|
||||||
|
}
|
||||||
|
logger.debug(`Retrieved product SKU with ID: ${id}`);
|
||||||
|
res.send(result);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const editProductSkuRouteHandler = async (req, res) => {
|
||||||
|
const id = new mongoose.Types.ObjectId(req.params.id);
|
||||||
|
|
||||||
|
logger.trace(`Product SKU with ID: ${id}`);
|
||||||
|
|
||||||
|
const updateData = {
|
||||||
|
updatedAt: new Date(),
|
||||||
|
sku: req.body?.sku,
|
||||||
|
product: req.body?.product,
|
||||||
|
name: req.body?.name,
|
||||||
|
description: req.body?.description,
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await editObject({
|
||||||
|
model: productSkuModel,
|
||||||
|
id,
|
||||||
|
updateData,
|
||||||
|
user: req.user,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.error) {
|
||||||
|
logger.error('Error editing product SKU:', result.error);
|
||||||
|
res.status(result.code || 500).send(result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug(`Edited product SKU with ID: ${id}`);
|
||||||
|
res.send(result);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const newProductSkuRouteHandler = async (req, res) => {
|
||||||
|
const newData = {
|
||||||
|
sku: req.body?.sku,
|
||||||
|
product: req.body?.product,
|
||||||
|
name: req.body?.name,
|
||||||
|
description: req.body?.description,
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await newObject({
|
||||||
|
model: productSkuModel,
|
||||||
|
newData,
|
||||||
|
user: req.user,
|
||||||
|
});
|
||||||
|
if (result.error) {
|
||||||
|
logger.error('No product SKU created:', result.error);
|
||||||
|
return res.status(result.code).send(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug(`New product SKU with ID: ${result._id}`);
|
||||||
|
res.send(result);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteProductSkuRouteHandler = async (req, res) => {
|
||||||
|
const id = new mongoose.Types.ObjectId(req.params.id);
|
||||||
|
|
||||||
|
logger.trace(`Product SKU with ID: ${id}`);
|
||||||
|
|
||||||
|
const result = await deleteObject({
|
||||||
|
model: productSkuModel,
|
||||||
|
id,
|
||||||
|
user: req.user,
|
||||||
|
});
|
||||||
|
if (result.error) {
|
||||||
|
logger.error('No product SKU deleted:', result.error);
|
||||||
|
return res.status(result.code).send(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug(`Deleted product SKU with ID: ${id}`);
|
||||||
|
res.send(result);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getProductSkuStatsRouteHandler = async (req, res) => {
|
||||||
|
const result = await getModelStats({ model: productSkuModel });
|
||||||
|
if (result?.error) {
|
||||||
|
logger.error('Error fetching product SKU stats:', result.error);
|
||||||
|
return res.status(result.code).send(result);
|
||||||
|
}
|
||||||
|
logger.trace('Product SKU stats:', result);
|
||||||
|
res.send(result);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getProductSkuHistoryRouteHandler = async (req, res) => {
|
||||||
|
const from = req.query.from;
|
||||||
|
const to = req.query.to;
|
||||||
|
const result = await getModelHistory({ model: productSkuModel, from, to });
|
||||||
|
if (result?.error) {
|
||||||
|
logger.error('Error fetching product SKU history:', result.error);
|
||||||
|
return res.status(result.code).send(result);
|
||||||
|
}
|
||||||
|
logger.trace('Product SKU history:', result);
|
||||||
|
res.send(result);
|
||||||
|
};
|
||||||
@ -65,6 +65,7 @@ function getModelFilterFields(objectType) {
|
|||||||
filamentStock: ['filament'],
|
filamentStock: ['filament'],
|
||||||
partStock: ['part'],
|
partStock: ['part'],
|
||||||
productStock: ['product'],
|
productStock: ['product'],
|
||||||
|
productSku: ['product'],
|
||||||
purchaseOrder: ['vendor'],
|
purchaseOrder: ['vendor'],
|
||||||
orderItem: ['order._id', 'orderType', 'item._id', 'itemType', 'shipment._id'],
|
orderItem: ['order._id', 'orderType', 'item._id', 'itemType', 'shipment._id'],
|
||||||
shipment: ['order._id', 'orderType', 'courierService._id'],
|
shipment: ['order._id', 'orderType', 'courierService._id'],
|
||||||
|
|||||||
@ -71,6 +71,7 @@ function getModelFilterFields(objectType) {
|
|||||||
filamentStock: ['filament'],
|
filamentStock: ['filament'],
|
||||||
partStock: ['part'],
|
partStock: ['part'],
|
||||||
productStock: ['product'],
|
productStock: ['product'],
|
||||||
|
productSku: ['product'],
|
||||||
purchaseOrder: ['vendor'],
|
purchaseOrder: ['vendor'],
|
||||||
orderItem: ['order._id', 'orderType', 'item._id', 'itemType', 'shipment._id'],
|
orderItem: ['order._id', 'orderType', 'item._id', 'itemType', 'shipment._id'],
|
||||||
shipment: ['order._id', 'orderType', 'courierService._id'],
|
shipment: ['order._id', 'orderType', 'courierService._id'],
|
||||||
|
|||||||
@ -345,6 +345,7 @@ function getModelFilterFields(objectType) {
|
|||||||
filamentStock: ['filament'],
|
filamentStock: ['filament'],
|
||||||
partStock: ['part'],
|
partStock: ['part'],
|
||||||
productStock: ['product'],
|
productStock: ['product'],
|
||||||
|
productSku: ['product'],
|
||||||
purchaseOrder: ['vendor'],
|
purchaseOrder: ['vendor'],
|
||||||
orderItem: ['order._id', 'orderType', 'item._id', 'itemType', 'shipment._id'],
|
orderItem: ['order._id', 'orderType', 'item._id', 'itemType', 'shipment._id'],
|
||||||
shipment: ['order._id', 'orderType', 'courierService._id'],
|
shipment: ['order._id', 'orderType', 'courierService._id'],
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user