ADD: Warehouse inventory management system
- Add WarehouseItem model with bilingual names (FA/EN) and bilingual counting units - Add WarehouseItemTransaction model for IN/OUT movements with reason and actor - Add WarehouseItemStock model for denormalized per-(item×warehouse) running balance - Add warehouseItemService with full CRUD, atomic stock updates via $inc, negative stock guard, and aggregation queries for per-item/per-warehouse/total stock - Add warehouseItemController and REST routes at /api/warehouse-items - Add warehouseService, warehouseController, warehouseRoutes for warehouse location CRUD at /api/warehouses - Register both route groups in routerEndpoints.js
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
const catchAsync = require('../../globalErrorHandler/catchAsync')
|
||||
const warehouseService = require('./warehouseService')
|
||||
|
||||
exports.createWarehouse = catchAsync(async (req, res, next) => {
|
||||
const warehouse = await warehouseService.createWarehouse(req.body)
|
||||
res.status(201).json({ status: 'success', data: warehouse })
|
||||
})
|
||||
|
||||
exports.listWarehouses = catchAsync(async (req, res, next) => {
|
||||
const warehouses = await warehouseService.listWarehouses(req.query)
|
||||
res.status(200).json({ status: 'success', results: warehouses.length, data: warehouses })
|
||||
})
|
||||
|
||||
exports.getWarehouse = catchAsync(async (req, res, next) => {
|
||||
const warehouse = await warehouseService.getWarehouse(req.params.warehouseId)
|
||||
res.status(200).json({ status: 'success', data: warehouse })
|
||||
})
|
||||
|
||||
exports.updateWarehouse = catchAsync(async (req, res, next) => {
|
||||
const warehouse = await warehouseService.updateWarehouse(req.params.warehouseId, req.body)
|
||||
res.status(200).json({ status: 'success', data: warehouse })
|
||||
})
|
||||
|
||||
exports.deleteWarehouse = catchAsync(async (req, res, next) => {
|
||||
await warehouseService.deleteWarehouse(req.params.warehouseId)
|
||||
res.status(200).json({ status: 'success', data: null })
|
||||
})
|
||||
@@ -0,0 +1,34 @@
|
||||
const mongoose = require('mongoose')
|
||||
const ObjectID = mongoose.Schema.Types.ObjectId
|
||||
|
||||
const warehouseSchema = new mongoose.Schema(
|
||||
{
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
code: {
|
||||
type: String,
|
||||
unique: true,
|
||||
},
|
||||
location: {
|
||||
address: String,
|
||||
coordinates: [Number], // [long, lat]
|
||||
},
|
||||
manager: {
|
||||
type: ObjectID,
|
||||
ref: 'User',
|
||||
},
|
||||
description: String,
|
||||
isActive: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
}
|
||||
)
|
||||
|
||||
const Warehouse = mongoose.model('Warehouse', warehouseSchema)
|
||||
module.exports = Warehouse
|
||||
@@ -0,0 +1,15 @@
|
||||
const express = require('express')
|
||||
const warehouseController = require('./warehouseController')
|
||||
const loginCheck = require('../../middlewares/loginCheck')
|
||||
|
||||
const router = new express.Router()
|
||||
|
||||
router.use(loginCheck.loginProtect)
|
||||
|
||||
router.get('/admin/get-all', warehouseController.listWarehouses)
|
||||
router.post('/admin/create', warehouseController.createWarehouse)
|
||||
router.get('/admin/get-one/:warehouseId', warehouseController.getWarehouse)
|
||||
router.patch('/admin/update/:warehouseId', warehouseController.updateWarehouse)
|
||||
router.delete('/admin/delete/:warehouseId', warehouseController.deleteWarehouse)
|
||||
|
||||
module.exports = router
|
||||
@@ -0,0 +1,62 @@
|
||||
const Warehouse = require('./warehouseModel')
|
||||
const AppError = require('../../globalErrorHandler/AppError')
|
||||
|
||||
const createWarehouse = async (data) => {
|
||||
const warehouse = await Warehouse.create({
|
||||
name: data.name,
|
||||
code: data.code || undefined,
|
||||
location: data.location,
|
||||
manager: data.manager,
|
||||
description: data.description,
|
||||
})
|
||||
return warehouse
|
||||
}
|
||||
|
||||
const listWarehouses = async (query = {}) => {
|
||||
const filter = {}
|
||||
if (query.isActive !== undefined) filter.isActive = query.isActive === 'true'
|
||||
if (query.search) {
|
||||
filter.$or = [
|
||||
{ name: { $regex: query.search, $options: 'i' } },
|
||||
{ code: { $regex: query.search, $options: 'i' } },
|
||||
]
|
||||
}
|
||||
|
||||
const warehouses = await Warehouse.find(filter)
|
||||
.populate('manager', 'name email')
|
||||
.sort({ createdAt: -1 })
|
||||
|
||||
return warehouses
|
||||
}
|
||||
|
||||
const getWarehouse = async (warehouseId) => {
|
||||
const warehouse = await Warehouse.findById(warehouseId).populate('manager', 'name email')
|
||||
if (!warehouse) throw new AppError('انبار یافت نشد', 404, {})
|
||||
return warehouse
|
||||
}
|
||||
|
||||
const updateWarehouse = async (warehouseId, data) => {
|
||||
const allowed = ['name', 'code', 'location', 'manager', 'description', 'isActive']
|
||||
const update = {}
|
||||
allowed.forEach((field) => {
|
||||
if (data[field] !== undefined) update[field] = data[field]
|
||||
})
|
||||
|
||||
const warehouse = await Warehouse.findByIdAndUpdate(warehouseId, update, { new: true, runValidators: true })
|
||||
if (!warehouse) throw new AppError('انبار یافت نشد', 404, {})
|
||||
return warehouse
|
||||
}
|
||||
|
||||
const deleteWarehouse = async (warehouseId) => {
|
||||
const warehouse = await Warehouse.findByIdAndUpdate(warehouseId, { isActive: false }, { new: true })
|
||||
if (!warehouse) throw new AppError('انبار یافت نشد', 404, {})
|
||||
return warehouse
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createWarehouse,
|
||||
listWarehouses,
|
||||
getWarehouse,
|
||||
updateWarehouse,
|
||||
deleteWarehouse,
|
||||
}
|
||||
Reference in New Issue
Block a user