update equipment stuff
This commit is contained in:
@@ -7,51 +7,72 @@ const { AppError } = require('../middleware/errorHandler');
|
||||
const router = express.Router();
|
||||
|
||||
// @route GET /api/nozzles/types
|
||||
// @desc Get all nozzle types
|
||||
// @desc Get all nozzle types with flow rate data
|
||||
// @access Private
|
||||
router.get('/types', async (req, res, next) => {
|
||||
try {
|
||||
const { manufacturer, droplet_size, spray_pattern } = req.query;
|
||||
const { manufacturer, droplet_size, spray_pattern, orifice_size } = req.query;
|
||||
|
||||
let query = 'SELECT * FROM nozzle_types WHERE 1=1';
|
||||
let query = `
|
||||
SELECT nt.*,
|
||||
json_agg(
|
||||
json_build_object(
|
||||
'pressure_psi', nfr.pressure_psi,
|
||||
'flow_rate_gpm', nfr.flow_rate_gpm
|
||||
) ORDER BY nfr.pressure_psi
|
||||
) FILTER (WHERE nfr.id IS NOT NULL) as flow_rates
|
||||
FROM nozzle_types nt
|
||||
LEFT JOIN nozzle_flow_rates nfr ON nt.id = nfr.nozzle_type_id
|
||||
WHERE 1=1
|
||||
`;
|
||||
const queryParams = [];
|
||||
|
||||
if (manufacturer) {
|
||||
queryParams.push(manufacturer);
|
||||
query += ` AND manufacturer ILIKE $${queryParams.length}`;
|
||||
query += ` AND nt.manufacturer ILIKE $${queryParams.length}`;
|
||||
}
|
||||
|
||||
if (droplet_size) {
|
||||
queryParams.push(droplet_size);
|
||||
query += ` AND droplet_size = $${queryParams.length}`;
|
||||
query += ` AND nt.droplet_size = $${queryParams.length}`;
|
||||
}
|
||||
|
||||
if (spray_pattern) {
|
||||
queryParams.push(spray_pattern);
|
||||
query += ` AND spray_pattern = $${queryParams.length}`;
|
||||
query += ` AND nt.spray_pattern = $${queryParams.length}`;
|
||||
}
|
||||
|
||||
if (orifice_size) {
|
||||
queryParams.push(orifice_size);
|
||||
query += ` AND nt.orifice_size = $${queryParams.length}`;
|
||||
}
|
||||
|
||||
query += ' ORDER BY manufacturer, name';
|
||||
query += ' GROUP BY nt.id ORDER BY nt.manufacturer, nt.name';
|
||||
|
||||
const result = await pool.query(query, queryParams);
|
||||
|
||||
// Group by manufacturer for easier frontend handling
|
||||
// Group by manufacturer and droplet size for easier frontend handling
|
||||
const nozzlesByManufacturer = result.rows.reduce((acc, nozzle) => {
|
||||
const manufacturer = nozzle.manufacturer || 'Unknown';
|
||||
if (!acc[manufacturer]) {
|
||||
acc[manufacturer] = [];
|
||||
acc[manufacturer] = {};
|
||||
}
|
||||
acc[manufacturer].push({
|
||||
if (!acc[manufacturer][nozzle.droplet_size]) {
|
||||
acc[manufacturer][nozzle.droplet_size] = [];
|
||||
}
|
||||
acc[manufacturer][nozzle.droplet_size].push({
|
||||
id: nozzle.id,
|
||||
name: nozzle.name,
|
||||
manufacturer: nozzle.manufacturer,
|
||||
model: nozzle.model,
|
||||
orificeSize: nozzle.orifice_size,
|
||||
sprayAngle: nozzle.spray_angle,
|
||||
flowRateGpm: parseFloat(nozzle.flow_rate_gpm),
|
||||
dropletSize: nozzle.droplet_size,
|
||||
sprayPattern: nozzle.spray_pattern,
|
||||
pressureRangePsi: nozzle.pressure_range_psi,
|
||||
material: nozzle.material,
|
||||
threadSize: nozzle.thread_size,
|
||||
colorCode: nozzle.color_code,
|
||||
flowRates: nozzle.flow_rates || [],
|
||||
createdAt: nozzle.created_at
|
||||
});
|
||||
return acc;
|
||||
@@ -67,10 +88,12 @@ router.get('/types', async (req, res, next) => {
|
||||
model: nt.model,
|
||||
orificeSize: nt.orifice_size,
|
||||
sprayAngle: nt.spray_angle,
|
||||
flowRateGpm: parseFloat(nt.flow_rate_gpm),
|
||||
dropletSize: nt.droplet_size,
|
||||
sprayPattern: nt.spray_pattern,
|
||||
pressureRangePsi: nt.pressure_range_psi,
|
||||
material: nt.material,
|
||||
threadSize: nt.thread_size,
|
||||
colorCode: nt.color_code,
|
||||
flowRates: nt.flow_rates || [],
|
||||
createdAt: nt.created_at
|
||||
})),
|
||||
nozzlesByManufacturer
|
||||
@@ -81,6 +104,63 @@ router.get('/types', async (req, res, next) => {
|
||||
}
|
||||
});
|
||||
|
||||
// @route GET /api/nozzles/flow-rate/:nozzleTypeId/:pressurePsi
|
||||
// @desc Get flow rate for specific nozzle at specific pressure
|
||||
// @access Private
|
||||
router.get('/flow-rate/:nozzleTypeId/:pressurePsi', validateParams(idParamSchema), async (req, res, next) => {
|
||||
try {
|
||||
const { nozzleTypeId, pressurePsi } = req.params;
|
||||
|
||||
// Get exact match or interpolate between pressures
|
||||
const result = await pool.query(
|
||||
`SELECT nfr.pressure_psi, nfr.flow_rate_gpm, nt.name, nt.manufacturer
|
||||
FROM nozzle_flow_rates nfr
|
||||
JOIN nozzle_types nt ON nfr.nozzle_type_id = nt.id
|
||||
WHERE nfr.nozzle_type_id = $1
|
||||
ORDER BY ABS(nfr.pressure_psi - $2)
|
||||
LIMIT 2`,
|
||||
[nozzleTypeId, pressurePsi]
|
||||
);
|
||||
|
||||
if (result.rows.length === 0) {
|
||||
throw new AppError('Flow rate data not available for this nozzle', 404);
|
||||
}
|
||||
|
||||
let flowRate;
|
||||
if (result.rows.length === 1 || result.rows[0].pressure_psi === parseInt(pressurePsi)) {
|
||||
// Exact match or only one data point
|
||||
flowRate = parseFloat(result.rows[0].flow_rate_gpm);
|
||||
} else {
|
||||
// Interpolate between two points
|
||||
const p1 = result.rows[0];
|
||||
const p2 = result.rows[1];
|
||||
const pressure = parseInt(pressurePsi);
|
||||
|
||||
if (p1.pressure_psi === p2.pressure_psi) {
|
||||
flowRate = parseFloat(p1.flow_rate_gpm);
|
||||
} else {
|
||||
// Linear interpolation
|
||||
const slope = (p2.flow_rate_gpm - p1.flow_rate_gpm) / (p2.pressure_psi - p1.pressure_psi);
|
||||
flowRate = parseFloat(p1.flow_rate_gpm) + slope * (pressure - p1.pressure_psi);
|
||||
flowRate = Math.round(flowRate * 1000) / 1000; // Round to 3 decimal places
|
||||
}
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
nozzleTypeId: parseInt(nozzleTypeId),
|
||||
pressurePsi: parseInt(pressurePsi),
|
||||
flowRateGpm: flowRate,
|
||||
nozzleName: result.rows[0].name,
|
||||
manufacturer: result.rows[0].manufacturer
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
// @route GET /api/nozzles
|
||||
// @desc Get all user's nozzles
|
||||
// @access Private
|
||||
@@ -500,4 +580,250 @@ router.delete('/assignments/:assignmentId', validateParams(idParamSchema), async
|
||||
}
|
||||
});
|
||||
|
||||
// PUMP ASSIGNMENT ROUTES
|
||||
|
||||
// @route GET /api/nozzles/equipment/:equipmentId/pump
|
||||
// @desc Get pump assigned to sprayer
|
||||
// @access Private
|
||||
router.get('/equipment/:equipmentId/pump', validateParams(idParamSchema), async (req, res, next) => {
|
||||
try {
|
||||
const equipmentId = req.params.equipmentId;
|
||||
|
||||
// Verify equipment belongs to user
|
||||
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(
|
||||
`SELECT epa.*,
|
||||
pump_eq.custom_name as pump_name, pump_eq.manufacturer as pump_manufacturer,
|
||||
pump_eq.model as pump_model, pump_eq.max_gpm, pump_eq.max_psi
|
||||
FROM equipment_pump_assignments epa
|
||||
JOIN user_equipment pump_eq ON epa.pump_equipment_id = pump_eq.id
|
||||
WHERE epa.sprayer_equipment_id = $1 AND epa.is_active = true`,
|
||||
[equipmentId]
|
||||
);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
pumpAssignment: result.rows.length > 0 ? {
|
||||
id: result.rows[0].id,
|
||||
sprayerEquipmentId: result.rows[0].sprayer_equipment_id,
|
||||
pumpEquipmentId: result.rows[0].pump_equipment_id,
|
||||
pumpName: result.rows[0].pump_name,
|
||||
pumpManufacturer: result.rows[0].pump_manufacturer,
|
||||
pumpModel: result.rows[0].pump_model,
|
||||
maxGpm: parseFloat(result.rows[0].max_gpm) || null,
|
||||
maxPsi: parseFloat(result.rows[0].max_psi) || null,
|
||||
assignedDate: result.rows[0].assigned_date,
|
||||
notes: result.rows[0].notes,
|
||||
isActive: result.rows[0].is_active
|
||||
} : null
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
// @route POST /api/nozzles/equipment/:equipmentId/pump
|
||||
// @desc Assign pump to sprayer
|
||||
// @access Private
|
||||
router.post('/equipment/:equipmentId/pump', validateParams(idParamSchema), async (req, res, next) => {
|
||||
try {
|
||||
const sprayerEquipmentId = req.params.equipmentId;
|
||||
const { pumpEquipmentId, notes } = req.body;
|
||||
|
||||
// Verify both equipment belong to user
|
||||
const equipmentCheck = await pool.query(
|
||||
'SELECT id, category_id FROM user_equipment WHERE id IN ($1, $2) AND user_id = $3',
|
||||
[sprayerEquipmentId, pumpEquipmentId, req.user.id]
|
||||
);
|
||||
|
||||
if (equipmentCheck.rows.length !== 2) {
|
||||
throw new AppError('Equipment not found', 404);
|
||||
}
|
||||
|
||||
// Deactivate any existing pump assignments for this sprayer
|
||||
await pool.query(
|
||||
'UPDATE equipment_pump_assignments SET is_active = false WHERE sprayer_equipment_id = $1',
|
||||
[sprayerEquipmentId]
|
||||
);
|
||||
|
||||
// Create new assignment
|
||||
const result = await pool.query(
|
||||
`INSERT INTO equipment_pump_assignments
|
||||
(sprayer_equipment_id, pump_equipment_id, notes)
|
||||
VALUES ($1, $2, $3)
|
||||
RETURNING *`,
|
||||
[sprayerEquipmentId, pumpEquipmentId, notes]
|
||||
);
|
||||
|
||||
const assignment = result.rows[0];
|
||||
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
message: 'Pump assigned to sprayer successfully',
|
||||
data: {
|
||||
assignment: {
|
||||
id: assignment.id,
|
||||
sprayerEquipmentId: assignment.sprayer_equipment_id,
|
||||
pumpEquipmentId: assignment.pump_equipment_id,
|
||||
assignedDate: assignment.assigned_date,
|
||||
notes: assignment.notes,
|
||||
isActive: assignment.is_active
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
// @route DELETE /api/nozzles/equipment/:equipmentId/pump
|
||||
// @desc Remove pump assignment from sprayer
|
||||
// @access Private
|
||||
router.delete('/equipment/:equipmentId/pump', validateParams(idParamSchema), async (req, res, next) => {
|
||||
try {
|
||||
const sprayerEquipmentId = req.params.equipmentId;
|
||||
|
||||
// Verify sprayer belongs to user
|
||||
const equipmentCheck = await pool.query(
|
||||
'SELECT id FROM user_equipment WHERE id = $1 AND user_id = $2',
|
||||
[sprayerEquipmentId, req.user.id]
|
||||
);
|
||||
|
||||
if (equipmentCheck.rows.length === 0) {
|
||||
throw new AppError('Equipment not found', 404);
|
||||
}
|
||||
|
||||
await pool.query(
|
||||
'UPDATE equipment_pump_assignments SET is_active = false WHERE sprayer_equipment_id = $1',
|
||||
[sprayerEquipmentId]
|
||||
);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Pump assignment removed successfully'
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
// NOZZLE CONFIGURATION ROUTES
|
||||
|
||||
// @route GET /api/nozzles/equipment/:equipmentId/configurations
|
||||
// @desc Get nozzle configurations for equipment
|
||||
// @access Private
|
||||
router.get('/equipment/:equipmentId/configurations', validateParams(idParamSchema), async (req, res, next) => {
|
||||
try {
|
||||
const equipmentId = req.params.equipmentId;
|
||||
|
||||
// Verify equipment belongs to user
|
||||
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(
|
||||
`SELECT enc.*,
|
||||
COUNT(cna.id) as nozzle_count
|
||||
FROM equipment_nozzle_configurations enc
|
||||
LEFT JOIN configuration_nozzle_assignments cna ON enc.id = cna.configuration_id
|
||||
WHERE enc.user_equipment_id = $1
|
||||
GROUP BY enc.id
|
||||
ORDER BY enc.is_default DESC, enc.configuration_name`,
|
||||
[equipmentId]
|
||||
);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
configurations: result.rows.map(config => ({
|
||||
id: config.id,
|
||||
userEquipmentId: config.user_equipment_id,
|
||||
configurationName: config.configuration_name,
|
||||
isDefault: config.is_default,
|
||||
nozzleCount: parseInt(config.nozzle_count),
|
||||
createdAt: config.created_at,
|
||||
updatedAt: config.updated_at
|
||||
}))
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
// @route GET /api/nozzles/configurations/:configId/details
|
||||
// @desc Get detailed nozzle configuration with assignments
|
||||
// @access Private
|
||||
router.get('/configurations/:configId/details', validateParams(idParamSchema), async (req, res, next) => {
|
||||
try {
|
||||
const configId = req.params.configId;
|
||||
|
||||
// Verify configuration belongs to user's equipment
|
||||
const configCheck = await pool.query(
|
||||
`SELECT enc.* FROM equipment_nozzle_configurations enc
|
||||
JOIN user_equipment ue ON enc.user_equipment_id = ue.id
|
||||
WHERE enc.id = $1 AND ue.user_id = $2`,
|
||||
[configId, req.user.id]
|
||||
);
|
||||
|
||||
if (configCheck.rows.length === 0) {
|
||||
throw new AppError('Configuration not found', 404);
|
||||
}
|
||||
|
||||
const result = await pool.query(
|
||||
`SELECT cna.*, nt.name, nt.manufacturer, nt.orifice_size, nt.spray_angle,
|
||||
nt.droplet_size, nt.spray_pattern, nt.color_code,
|
||||
nfr.flow_rate_gpm
|
||||
FROM configuration_nozzle_assignments cna
|
||||
JOIN nozzle_types nt ON cna.nozzle_type_id = nt.id
|
||||
LEFT JOIN nozzle_flow_rates nfr ON nt.id = nfr.nozzle_type_id AND nfr.pressure_psi = cna.operating_pressure_psi
|
||||
WHERE cna.configuration_id = $1
|
||||
ORDER BY cna.position`,
|
||||
[configId]
|
||||
);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
configuration: configCheck.rows[0],
|
||||
assignments: result.rows.map(item => ({
|
||||
id: item.id,
|
||||
configurationId: item.configuration_id,
|
||||
nozzleTypeId: item.nozzle_type_id,
|
||||
position: item.position,
|
||||
quantity: item.quantity,
|
||||
operatingPressurePsi: item.operating_pressure_psi,
|
||||
notes: item.notes,
|
||||
nozzleName: item.name,
|
||||
manufacturer: item.manufacturer,
|
||||
orificeSize: item.orifice_size,
|
||||
sprayAngle: item.spray_angle,
|
||||
dropletSize: item.droplet_size,
|
||||
sprayPattern: item.spray_pattern,
|
||||
colorCode: item.color_code,
|
||||
flowRateGpm: parseFloat(item.flow_rate_gpm) || null,
|
||||
createdAt: item.created_at
|
||||
}))
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
Reference in New Issue
Block a user