linking stuff
This commit is contained in:
@@ -7,6 +7,37 @@ const { calculateApplication } = require('../utils/applicationCalculations');
|
|||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
|
// Helper function to get spreader settings for equipment and product
|
||||||
|
async function getSpreaderSettingsForEquipment(equipmentId, productId, userProductId, userId) {
|
||||||
|
// Get spreader settings for the specific product and equipment combination
|
||||||
|
let query, params;
|
||||||
|
|
||||||
|
if (userProductId) {
|
||||||
|
query = `
|
||||||
|
SELECT pss.*, ue.custom_name as equipment_name, ue.manufacturer, ue.model as equipment_model
|
||||||
|
FROM product_spreader_settings pss
|
||||||
|
JOIN user_equipment ue ON pss.equipment_id = ue.id
|
||||||
|
WHERE pss.user_product_id = $1 AND pss.equipment_id = $2 AND ue.user_id = $3
|
||||||
|
ORDER BY pss.created_at DESC
|
||||||
|
LIMIT 1
|
||||||
|
`;
|
||||||
|
params = [userProductId, equipmentId, userId];
|
||||||
|
} else {
|
||||||
|
query = `
|
||||||
|
SELECT pss.*, ue.custom_name as equipment_name, ue.manufacturer, ue.model as equipment_model
|
||||||
|
FROM product_spreader_settings pss
|
||||||
|
JOIN user_equipment ue ON pss.equipment_id = ue.id
|
||||||
|
WHERE pss.product_id = $1 AND pss.equipment_id = $2 AND ue.user_id = $3
|
||||||
|
ORDER BY pss.created_at DESC
|
||||||
|
LIMIT 1
|
||||||
|
`;
|
||||||
|
params = [productId, equipmentId, userId];
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await pool.query(query, params);
|
||||||
|
return result.rows[0] || null;
|
||||||
|
}
|
||||||
|
|
||||||
// @route GET /api/applications/plans
|
// @route GET /api/applications/plans
|
||||||
// @desc Get all application plans for current user
|
// @desc Get all application plans for current user
|
||||||
// @access Private
|
// @access Private
|
||||||
@@ -902,4 +933,67 @@ router.get('/stats', async (req, res, next) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// @route GET /api/applications/spreader-settings/:equipmentId/:productId
|
||||||
|
// @desc Get recommended spreader settings for specific equipment and product combination
|
||||||
|
// @access Private
|
||||||
|
router.get('/spreader-settings/:equipmentId/:productId', async (req, res, next) => {
|
||||||
|
try {
|
||||||
|
const { equipmentId, productId } = req.params;
|
||||||
|
const { isUserProduct } = req.query; // Indicates if productId refers to user_products table
|
||||||
|
|
||||||
|
// Verify equipment belongs to user
|
||||||
|
const equipmentCheck = await pool.query(
|
||||||
|
'SELECT id, custom_name, manufacturer, model 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 equipment = equipmentCheck.rows[0];
|
||||||
|
|
||||||
|
// Get spreader settings
|
||||||
|
const spreaderSetting = await getSpreaderSettingsForEquipment(
|
||||||
|
parseInt(equipmentId),
|
||||||
|
isUserProduct === 'true' ? null : parseInt(productId),
|
||||||
|
isUserProduct === 'true' ? parseInt(productId) : null,
|
||||||
|
req.user.id
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!spreaderSetting) {
|
||||||
|
return res.json({
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
hasSettings: false,
|
||||||
|
message: 'No spreader settings found for this equipment and product combination',
|
||||||
|
equipment: {
|
||||||
|
id: equipment.id,
|
||||||
|
name: equipment.custom_name || `${equipment.manufacturer} ${equipment.model}`.trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
hasSettings: true,
|
||||||
|
setting: {
|
||||||
|
id: spreaderSetting.id,
|
||||||
|
settingValue: spreaderSetting.setting_value,
|
||||||
|
rateDescription: spreaderSetting.rate_description,
|
||||||
|
notes: spreaderSetting.notes
|
||||||
|
},
|
||||||
|
equipment: {
|
||||||
|
id: equipment.id,
|
||||||
|
name: equipment.custom_name || `${equipment.manufacturer} ${equipment.model}`.trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
@@ -182,6 +182,43 @@ router.get('/', async (req, res, next) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// @route GET /api/equipment/spreaders
|
||||||
|
// @desc Get all spreader equipment for current user (for product spreader settings)
|
||||||
|
// @access Private
|
||||||
|
router.get('/spreaders', async (req, res, next) => {
|
||||||
|
try {
|
||||||
|
const result = await pool.query(
|
||||||
|
`SELECT ue.id, ue.custom_name, ue.manufacturer, ue.model, ue.spreader_type,
|
||||||
|
ue.capacity_lbs, ue.spread_width, ec.name as category_name
|
||||||
|
FROM user_equipment ue
|
||||||
|
LEFT JOIN equipment_categories ec ON ue.category_id = ec.id
|
||||||
|
WHERE ue.user_id = $1
|
||||||
|
AND ue.is_active = true
|
||||||
|
AND (ec.name ILIKE '%spreader%' OR ue.spreader_type IS NOT NULL)
|
||||||
|
ORDER BY ue.custom_name, ue.manufacturer, ue.model`,
|
||||||
|
[req.user.id]
|
||||||
|
);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
spreaders: result.rows.map(item => ({
|
||||||
|
id: item.id,
|
||||||
|
name: item.custom_name || `${item.manufacturer} ${item.model}`.trim(),
|
||||||
|
manufacturer: item.manufacturer,
|
||||||
|
model: item.model,
|
||||||
|
spreaderType: item.spreader_type,
|
||||||
|
capacityLbs: item.capacity_lbs ? parseFloat(item.capacity_lbs) : null,
|
||||||
|
spreadWidth: item.spread_width ? parseFloat(item.spread_width) : null,
|
||||||
|
categoryName: item.category_name
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// @route GET /api/equipment/:id
|
// @route GET /api/equipment/:id
|
||||||
// @desc Get single equipment item
|
// @desc Get single equipment item
|
||||||
// @access Private
|
// @access Private
|
||||||
|
|||||||
@@ -351,13 +351,34 @@ router.post('/user', validateRequest(userProductSchema), async (req, res, next)
|
|||||||
if (spreaderSettings && Array.isArray(spreaderSettings) && productType === 'granular') {
|
if (spreaderSettings && Array.isArray(spreaderSettings) && productType === 'granular') {
|
||||||
// Add spreader settings for this user product
|
// Add spreader settings for this user product
|
||||||
for (const setting of spreaderSettings) {
|
for (const setting of spreaderSettings) {
|
||||||
await pool.query(
|
// Check if equipment exists and belongs to user if equipmentId is provided
|
||||||
`INSERT INTO product_spreader_settings
|
if (setting.equipmentId) {
|
||||||
(user_product_id, spreader_brand, spreader_model, setting_value, rate_description, notes)
|
const equipmentCheck = await pool.query(
|
||||||
VALUES ($1, $2, $3, $4, $5, $6)`,
|
'SELECT id FROM user_equipment WHERE id = $1 AND user_id = $2',
|
||||||
[userProduct.id, setting.spreaderBrand, setting.spreaderModel, setting.settingValue,
|
[setting.equipmentId, req.user.id]
|
||||||
setting.rateDescription, setting.notes]
|
);
|
||||||
);
|
|
||||||
|
if (equipmentCheck.rows.length === 0) {
|
||||||
|
throw new AppError(`Equipment with id ${setting.equipmentId} not found`, 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
await pool.query(
|
||||||
|
`INSERT INTO product_spreader_settings
|
||||||
|
(user_product_id, equipment_id, setting_value, rate_description, notes)
|
||||||
|
VALUES ($1, $2, $3, $4, $5)`,
|
||||||
|
[userProduct.id, setting.equipmentId, setting.settingValue,
|
||||||
|
setting.rateDescription, setting.notes]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Fall back to legacy brand/model approach
|
||||||
|
await pool.query(
|
||||||
|
`INSERT INTO product_spreader_settings
|
||||||
|
(user_product_id, spreader_brand, spreader_model, setting_value, rate_description, notes)
|
||||||
|
VALUES ($1, $2, $3, $4, $5, $6)`,
|
||||||
|
[userProduct.id, setting.spreaderBrand, setting.spreaderModel, setting.settingValue,
|
||||||
|
setting.rateDescription, setting.notes]
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -412,17 +433,27 @@ router.get('/user/:id', validateParams(idParamSchema), async (req, res, next) =>
|
|||||||
|
|
||||||
const userProduct = result.rows[0];
|
const userProduct = result.rows[0];
|
||||||
|
|
||||||
// Get spreader settings for this user product
|
// Get spreader settings for this user product with equipment details
|
||||||
let spreaderSettings = [];
|
let spreaderSettings = [];
|
||||||
const settingsResult = await pool.query(
|
const settingsResult = await pool.query(
|
||||||
`SELECT * FROM product_spreader_settings
|
`SELECT pss.*, ue.custom_name as equipment_name, ue.manufacturer, ue.model as equipment_model,
|
||||||
WHERE user_product_id = $1
|
ue.spreader_type, ue.capacity_lbs
|
||||||
ORDER BY spreader_brand, spreader_model NULLS LAST, setting_value`,
|
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]
|
[userProductId]
|
||||||
);
|
);
|
||||||
|
|
||||||
spreaderSettings = settingsResult.rows.map(row => ({
|
spreaderSettings = settingsResult.rows.map(row => ({
|
||||||
id: row.id,
|
id: row.id,
|
||||||
|
equipmentId: row.equipment_id,
|
||||||
|
equipmentName: row.equipment_name,
|
||||||
|
equipmentManufacturer: row.manufacturer,
|
||||||
|
equipmentModel: row.equipment_model,
|
||||||
|
equipmentType: row.spreader_type,
|
||||||
|
equipmentCapacity: row.capacity_lbs ? parseFloat(row.capacity_lbs) : null,
|
||||||
|
// Legacy fields
|
||||||
spreaderBrand: row.spreader_brand,
|
spreaderBrand: row.spreader_brand,
|
||||||
spreaderModel: row.spreader_model,
|
spreaderModel: row.spreader_model,
|
||||||
settingValue: row.setting_value,
|
settingValue: row.setting_value,
|
||||||
|
|||||||
@@ -91,8 +91,10 @@ const userProductSchema = Joi.object({
|
|||||||
// Spreader settings for granular products
|
// Spreader settings for granular products
|
||||||
spreaderSettings: Joi.array().items(
|
spreaderSettings: Joi.array().items(
|
||||||
Joi.object({
|
Joi.object({
|
||||||
id: Joi.number().optional(), // For frontend temporary IDs
|
id: Joi.number().optional(), // For existing settings
|
||||||
spreaderBrand: Joi.string().max(100).required(),
|
equipmentId: Joi.number().integer().positive().optional(), // Link to user_equipment
|
||||||
|
// Legacy fields for backward compatibility
|
||||||
|
spreaderBrand: Joi.string().max(100).optional(),
|
||||||
spreaderModel: Joi.alternatives().try(
|
spreaderModel: Joi.alternatives().try(
|
||||||
Joi.string().max(100).allow(''),
|
Joi.string().max(100).allow(''),
|
||||||
Joi.allow(null)
|
Joi.allow(null)
|
||||||
@@ -106,7 +108,7 @@ const userProductSchema = Joi.object({
|
|||||||
Joi.string().allow(''),
|
Joi.string().allow(''),
|
||||||
Joi.allow(null)
|
Joi.allow(null)
|
||||||
).optional()
|
).optional()
|
||||||
})
|
}).or('equipmentId', 'spreaderBrand') // Must have either equipment reference or brand
|
||||||
).optional()
|
).optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
14
database/migrations/link_spreader_settings_to_equipment.sql
Normal file
14
database/migrations/link_spreader_settings_to_equipment.sql
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
-- Link spreader settings to actual user equipment instead of storing text
|
||||||
|
-- This allows better integration and recommendations
|
||||||
|
|
||||||
|
-- Add equipment reference to product_spreader_settings
|
||||||
|
ALTER TABLE product_spreader_settings
|
||||||
|
ADD COLUMN IF NOT EXISTS equipment_id INTEGER REFERENCES user_equipment(id) ON DELETE CASCADE;
|
||||||
|
|
||||||
|
-- Create index for the new relationship
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_product_spreader_settings_equipment ON product_spreader_settings(equipment_id);
|
||||||
|
|
||||||
|
-- We'll keep the old columns for backward compatibility during transition
|
||||||
|
-- They can be removed later once all data is migrated
|
||||||
|
|
||||||
|
SELECT 'Added equipment reference to spreader settings!' as migration_status;
|
||||||
Reference in New Issue
Block a user