287 lines
9.4 KiB
JavaScript
287 lines
9.4 KiB
JavaScript
const express = require('express');
|
|
const pool = require('../config/database');
|
|
const { validateRequest, validateParams } = require('../utils/validation');
|
|
const { AppError } = require('../middleware/errorHandler');
|
|
const Joi = require('joi');
|
|
|
|
const router = express.Router();
|
|
|
|
// Validation schemas
|
|
const spreaderSettingSchema = Joi.object({
|
|
productId: Joi.number().integer().positive().optional(),
|
|
userProductId: Joi.number().integer().positive().optional(),
|
|
equipmentId: Joi.number().integer().positive().optional(), // Link to user_equipment
|
|
// Legacy fields for backward compatibility
|
|
spreaderBrand: Joi.string().max(100).optional(),
|
|
spreaderModel: Joi.string().max(100).allow(null, '').optional(),
|
|
settingValue: Joi.string().max(20).required(),
|
|
rateDescription: Joi.string().max(200).allow(null, '').optional(),
|
|
notes: Joi.string().allow(null, '').optional()
|
|
}).xor('productId', 'userProductId')
|
|
.when('equipmentId', {
|
|
is: Joi.exist(),
|
|
then: Joi.object(), // When equipmentId exists, spreaderBrand is optional
|
|
otherwise: Joi.object({
|
|
spreaderBrand: Joi.string().max(100).required() // When no equipmentId, require spreaderBrand
|
|
})
|
|
}); // Must have either productId or userProductId, but not both
|
|
|
|
const idParamSchema = Joi.object({
|
|
id: Joi.number().integer().positive().required()
|
|
});
|
|
|
|
const userProductIdParamSchema = Joi.object({
|
|
userProductId: Joi.number().integer().positive().required()
|
|
});
|
|
|
|
// @route GET /api/product-spreader-settings/product/:productId
|
|
// @desc Get spreader settings for a specific product
|
|
// @access Private
|
|
router.get('/product/:productId', validateParams(idParamSchema), async (req, res, next) => {
|
|
try {
|
|
const productId = req.params.productId;
|
|
|
|
const result = await pool.query(
|
|
`SELECT * FROM product_spreader_settings
|
|
WHERE product_id = $1
|
|
ORDER BY spreader_brand, spreader_model NULLS LAST, setting_value`,
|
|
[productId]
|
|
);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: {
|
|
settings: result.rows.map(row => ({
|
|
id: row.id,
|
|
spreaderBrand: row.spreader_brand,
|
|
spreaderModel: row.spreader_model,
|
|
settingValue: row.setting_value,
|
|
rateDescription: row.rate_description,
|
|
notes: row.notes,
|
|
createdAt: row.created_at
|
|
}))
|
|
}
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
});
|
|
|
|
// @route GET /api/product-spreader-settings/user-product/:userProductId
|
|
// @desc Get spreader settings for a specific user product
|
|
// @access Private
|
|
router.get('/user-product/:userProductId', validateParams(userProductIdParamSchema), async (req, res, next) => {
|
|
try {
|
|
const userProductId = req.params.userProductId;
|
|
|
|
// Verify the user product belongs to the requesting user
|
|
const productCheck = await pool.query(
|
|
'SELECT id FROM user_products WHERE id = $1 AND user_id = $2',
|
|
[userProductId, req.user.id]
|
|
);
|
|
|
|
if (productCheck.rows.length === 0) {
|
|
throw new AppError('User product not found', 404);
|
|
}
|
|
|
|
const result = await pool.query(
|
|
`SELECT pss.*, ue.custom_name as equipment_name, ue.manufacturer, ue.model as equipment_model
|
|
FROM product_spreader_settings pss
|
|
LEFT JOIN user_equipment ue ON pss.equipment_id = ue.id
|
|
WHERE pss.user_product_id = $1
|
|
ORDER BY ue.custom_name NULLS LAST, pss.spreader_brand, pss.spreader_model NULLS LAST, pss.setting_value`,
|
|
[userProductId]
|
|
);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: {
|
|
settings: result.rows.map(row => ({
|
|
id: row.id,
|
|
equipmentId: row.equipment_id,
|
|
equipmentName: row.equipment_name,
|
|
equipmentManufacturer: row.manufacturer,
|
|
equipmentModel: row.equipment_model,
|
|
// Legacy fields
|
|
spreaderBrand: row.spreader_brand,
|
|
spreaderModel: row.spreader_model,
|
|
settingValue: row.setting_value,
|
|
rateDescription: row.rate_description,
|
|
notes: row.notes,
|
|
createdAt: row.created_at
|
|
}))
|
|
}
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
});
|
|
|
|
// @route POST /api/product-spreader-settings
|
|
// @desc Add spreader setting to product
|
|
// @access Private
|
|
router.post('/', validateRequest(spreaderSettingSchema), async (req, res, next) => {
|
|
try {
|
|
const { productId, userProductId, equipmentId, spreaderBrand, spreaderModel, settingValue, rateDescription, notes } = req.body;
|
|
|
|
// If it's a user product, verify ownership
|
|
if (userProductId) {
|
|
const productCheck = await pool.query(
|
|
'SELECT id FROM user_products WHERE id = $1 AND user_id = $2',
|
|
[userProductId, req.user.id]
|
|
);
|
|
|
|
if (productCheck.rows.length === 0) {
|
|
throw new AppError('User product not found', 404);
|
|
}
|
|
}
|
|
|
|
// If equipment ID is provided, verify it belongs to the user
|
|
if (equipmentId) {
|
|
const equipmentCheck = await pool.query(
|
|
'SELECT id FROM user_equipment WHERE id = $1 AND user_id = $2',
|
|
[equipmentId, req.user.id]
|
|
);
|
|
|
|
if (equipmentCheck.rows.length === 0) {
|
|
throw new AppError('Equipment not found', 404);
|
|
}
|
|
}
|
|
|
|
const result = await pool.query(
|
|
`INSERT INTO product_spreader_settings
|
|
(product_id, user_product_id, equipment_id, spreader_brand, spreader_model, setting_value, rate_description, notes)
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
|
RETURNING *`,
|
|
[productId, userProductId, equipmentId, spreaderBrand, spreaderModel, settingValue, rateDescription, notes]
|
|
);
|
|
|
|
const setting = result.rows[0];
|
|
|
|
res.status(201).json({
|
|
success: true,
|
|
message: 'Spreader setting added successfully',
|
|
data: {
|
|
setting: {
|
|
id: setting.id,
|
|
equipmentId: setting.equipment_id,
|
|
spreaderBrand: setting.spreader_brand,
|
|
spreaderModel: setting.spreader_model,
|
|
settingValue: setting.setting_value,
|
|
rateDescription: setting.rate_description,
|
|
notes: setting.notes,
|
|
createdAt: setting.created_at
|
|
}
|
|
}
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
});
|
|
|
|
// @route PUT /api/product-spreader-settings/:id
|
|
// @desc Update spreader setting
|
|
// @access Private
|
|
router.put('/:id', validateParams(idParamSchema), validateRequest(spreaderSettingSchema), async (req, res, next) => {
|
|
try {
|
|
const settingId = req.params.id;
|
|
const { productId, userProductId, equipmentId, spreaderBrand, spreaderModel, settingValue, rateDescription, notes } = req.body;
|
|
|
|
// Check if setting exists and user has permission to edit it
|
|
let checkQuery;
|
|
let checkParams;
|
|
|
|
if (userProductId) {
|
|
checkQuery = `
|
|
SELECT pss.* FROM product_spreader_settings pss
|
|
JOIN user_products up ON pss.user_product_id = up.id
|
|
WHERE pss.id = $1 AND up.user_id = $2
|
|
`;
|
|
checkParams = [settingId, req.user.id];
|
|
} else {
|
|
// For shared products, any authenticated user can edit (you might want to restrict this)
|
|
checkQuery = 'SELECT * FROM product_spreader_settings WHERE id = $1';
|
|
checkParams = [settingId];
|
|
}
|
|
|
|
const settingCheck = await pool.query(checkQuery, checkParams);
|
|
|
|
if (settingCheck.rows.length === 0) {
|
|
throw new AppError('Spreader setting not found', 404);
|
|
}
|
|
|
|
// If equipment ID is provided, verify it belongs to the user
|
|
if (equipmentId) {
|
|
const equipmentCheck = await pool.query(
|
|
'SELECT id FROM user_equipment WHERE id = $1 AND user_id = $2',
|
|
[equipmentId, req.user.id]
|
|
);
|
|
|
|
if (equipmentCheck.rows.length === 0) {
|
|
throw new AppError('Equipment not found', 404);
|
|
}
|
|
}
|
|
|
|
const result = await pool.query(
|
|
`UPDATE product_spreader_settings
|
|
SET equipment_id = $1, spreader_brand = $2, spreader_model = $3, setting_value = $4,
|
|
rate_description = $5, notes = $6
|
|
WHERE id = $7
|
|
RETURNING *`,
|
|
[equipmentId, spreaderBrand, spreaderModel, settingValue, rateDescription, notes, settingId]
|
|
);
|
|
|
|
const setting = result.rows[0];
|
|
|
|
res.json({
|
|
success: true,
|
|
message: 'Spreader setting updated successfully',
|
|
data: {
|
|
setting: {
|
|
id: setting.id,
|
|
equipmentId: setting.equipment_id,
|
|
spreaderBrand: setting.spreader_brand,
|
|
spreaderModel: setting.spreader_model,
|
|
settingValue: setting.setting_value,
|
|
rateDescription: setting.rate_description,
|
|
notes: setting.notes,
|
|
updatedAt: setting.updated_at
|
|
}
|
|
}
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
});
|
|
|
|
// @route DELETE /api/product-spreader-settings/:id
|
|
// @desc Delete spreader setting
|
|
// @access Private
|
|
router.delete('/:id', validateParams(idParamSchema), async (req, res, next) => {
|
|
try {
|
|
const settingId = req.params.id;
|
|
|
|
// Check if setting exists and user has permission to delete it
|
|
const settingCheck = await pool.query(
|
|
`SELECT pss.* FROM product_spreader_settings pss
|
|
LEFT JOIN user_products up ON pss.user_product_id = up.id
|
|
WHERE pss.id = $1 AND (pss.product_id IS NOT NULL OR up.user_id = $2)`,
|
|
[settingId, req.user.id]
|
|
);
|
|
|
|
if (settingCheck.rows.length === 0) {
|
|
throw new AppError('Spreader setting not found', 404);
|
|
}
|
|
|
|
await pool.query('DELETE FROM product_spreader_settings WHERE id = $1', [settingId]);
|
|
|
|
res.json({
|
|
success: true,
|
|
message: 'Spreader setting deleted successfully'
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
});
|
|
|
|
module.exports = router; |