diff --git a/inventory-server/scripts/reset-db.js b/inventory-server/scripts/reset-db.js index e85417b..de044e6 100644 --- a/inventory-server/scripts/reset-db.js +++ b/inventory-server/scripts/reset-db.js @@ -35,7 +35,12 @@ const CORE_TABLES = [ // Config tables that must be created const CONFIG_TABLES = [ - 'stock_thresholds' + 'stock_thresholds', + 'lead_time_thresholds', + 'sales_velocity_config', + 'abc_classification_config', + 'safety_stock_config', + 'turnover_config' ]; // Split SQL into individual statements diff --git a/inventory-server/scripts/reset-metrics.js b/inventory-server/scripts/reset-metrics.js index aa47bbb..e3bf014 100644 --- a/inventory-server/scripts/reset-metrics.js +++ b/inventory-server/scripts/reset-metrics.js @@ -26,7 +26,12 @@ const METRICS_TABLES = [ // Config tables that must exist const CONFIG_TABLES = [ - 'stock_thresholds' + 'stock_thresholds', + 'lead_time_thresholds', + 'sales_velocity_config', + 'abc_classification_config', + 'safety_stock_config', + 'turnover_config' ]; // Core tables that must exist diff --git a/inventory-server/src/routes/config.js b/inventory-server/src/routes/config.js new file mode 100644 index 0000000..2d7f881 --- /dev/null +++ b/inventory-server/src/routes/config.js @@ -0,0 +1,172 @@ +const express = require('express'); +const router = express.Router(); + +// Debug middleware +router.use((req, res, next) => { + console.log(`[Config Route] ${req.method} ${req.path}`); + next(); +}); + +// Get all configuration values +router.get('/', async (req, res) => { + const pool = req.app.locals.pool; + try { + console.log('[Config Route] Fetching configuration values...'); + + const [stockThresholds] = await pool.query('SELECT * FROM stock_thresholds WHERE id = 1'); + console.log('[Config Route] Stock thresholds:', stockThresholds); + + const [leadTimeThresholds] = await pool.query('SELECT * FROM lead_time_thresholds WHERE id = 1'); + console.log('[Config Route] Lead time thresholds:', leadTimeThresholds); + + const [salesVelocityConfig] = await pool.query('SELECT * FROM sales_velocity_config WHERE id = 1'); + console.log('[Config Route] Sales velocity config:', salesVelocityConfig); + + const [abcConfig] = await pool.query('SELECT * FROM abc_classification_config WHERE id = 1'); + console.log('[Config Route] ABC config:', abcConfig); + + const [safetyStockConfig] = await pool.query('SELECT * FROM safety_stock_config WHERE id = 1'); + console.log('[Config Route] Safety stock config:', safetyStockConfig); + + const [turnoverConfig] = await pool.query('SELECT * FROM turnover_config WHERE id = 1'); + console.log('[Config Route] Turnover config:', turnoverConfig); + + const response = { + stockThresholds: stockThresholds[0], + leadTimeThresholds: leadTimeThresholds[0], + salesVelocityConfig: salesVelocityConfig[0], + abcConfig: abcConfig[0], + safetyStockConfig: safetyStockConfig[0], + turnoverConfig: turnoverConfig[0] + }; + + console.log('[Config Route] Sending response:', response); + res.json(response); + } catch (error) { + console.error('[Config Route] Error fetching configuration:', error); + res.status(500).json({ error: 'Failed to fetch configuration', details: error.message }); + } +}); + +// Update stock thresholds +router.put('/stock-thresholds/:id', async (req, res) => { + const pool = req.app.locals.pool; + try { + const { critical_days, reorder_days, overstock_days, low_stock_threshold, min_reorder_quantity } = req.body; + const [result] = await pool.query( + `UPDATE stock_thresholds + SET critical_days = ?, + reorder_days = ?, + overstock_days = ?, + low_stock_threshold = ?, + min_reorder_quantity = ? + WHERE id = ?`, + [critical_days, reorder_days, overstock_days, low_stock_threshold, min_reorder_quantity, req.params.id] + ); + res.json({ success: true }); + } catch (error) { + console.error('[Config Route] Error updating stock thresholds:', error); + res.status(500).json({ error: 'Failed to update stock thresholds' }); + } +}); + +// Update lead time thresholds +router.put('/lead-time-thresholds/:id', async (req, res) => { + const pool = req.app.locals.pool; + try { + const { target_days, warning_days, critical_days } = req.body; + const [result] = await pool.query( + `UPDATE lead_time_thresholds + SET target_days = ?, + warning_days = ?, + critical_days = ? + WHERE id = ?`, + [target_days, warning_days, critical_days, req.params.id] + ); + res.json({ success: true }); + } catch (error) { + console.error('[Config Route] Error updating lead time thresholds:', error); + res.status(500).json({ error: 'Failed to update lead time thresholds' }); + } +}); + +// Update sales velocity config +router.put('/sales-velocity/:id', async (req, res) => { + const pool = req.app.locals.pool; + try { + const { daily_window_days, weekly_window_days, monthly_window_days } = req.body; + const [result] = await pool.query( + `UPDATE sales_velocity_config + SET daily_window_days = ?, + weekly_window_days = ?, + monthly_window_days = ? + WHERE id = ?`, + [daily_window_days, weekly_window_days, monthly_window_days, req.params.id] + ); + res.json({ success: true }); + } catch (error) { + console.error('[Config Route] Error updating sales velocity config:', error); + res.status(500).json({ error: 'Failed to update sales velocity config' }); + } +}); + +// Update ABC classification config +router.put('/abc-classification/:id', async (req, res) => { + const pool = req.app.locals.pool; + try { + const { a_threshold, b_threshold, classification_period_days } = req.body; + const [result] = await pool.query( + `UPDATE abc_classification_config + SET a_threshold = ?, + b_threshold = ?, + classification_period_days = ? + WHERE id = ?`, + [a_threshold, b_threshold, classification_period_days, req.params.id] + ); + res.json({ success: true }); + } catch (error) { + console.error('[Config Route] Error updating ABC classification config:', error); + res.status(500).json({ error: 'Failed to update ABC classification config' }); + } +}); + +// Update safety stock config +router.put('/safety-stock/:id', async (req, res) => { + const pool = req.app.locals.pool; + try { + const { coverage_days, service_level } = req.body; + const [result] = await pool.query( + `UPDATE safety_stock_config + SET coverage_days = ?, + service_level = ? + WHERE id = ?`, + [coverage_days, service_level, req.params.id] + ); + res.json({ success: true }); + } catch (error) { + console.error('[Config Route] Error updating safety stock config:', error); + res.status(500).json({ error: 'Failed to update safety stock config' }); + } +}); + +// Update turnover config +router.put('/turnover/:id', async (req, res) => { + const pool = req.app.locals.pool; + try { + const { calculation_period_days, target_rate } = req.body; + const [result] = await pool.query( + `UPDATE turnover_config + SET calculation_period_days = ?, + target_rate = ? + WHERE id = ?`, + [calculation_period_days, target_rate, req.params.id] + ); + res.json({ success: true }); + } catch (error) { + console.error('[Config Route] Error updating turnover config:', error); + res.status(500).json({ error: 'Failed to update turnover config' }); + } +}); + +// Export the router +module.exports = router; \ No newline at end of file diff --git a/inventory-server/src/server.js b/inventory-server/src/server.js index 18f5a74..66a751f 100755 --- a/inventory-server/src/server.js +++ b/inventory-server/src/server.js @@ -12,6 +12,7 @@ const ordersRouter = require('./routes/orders'); const csvRouter = require('./routes/csv'); const analyticsRouter = require('./routes/analytics'); const purchaseOrdersRouter = require('./routes/purchase-orders'); +const configRouter = require('./routes/config'); // Get the absolute path to the .env file const envPath = path.resolve(process.cwd(), '.env'); @@ -83,6 +84,7 @@ app.use('/api/orders', ordersRouter); app.use('/api/csv', csvRouter); app.use('/api/analytics', analyticsRouter); app.use('/api/purchase-orders', purchaseOrdersRouter); +app.use('/api/config', configRouter); // Basic health check route app.get('/health', (req, res) => { diff --git a/inventory/src/components/settings/CalculationSettings.tsx b/inventory/src/components/settings/CalculationSettings.tsx index 2ae4a89..433f8fe 100644 --- a/inventory/src/components/settings/CalculationSettings.tsx +++ b/inventory/src/components/settings/CalculationSettings.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useState, useEffect } from 'react'; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; @@ -25,6 +25,24 @@ export function CalculationSettings() { monthly_window_days: 90 }); + useEffect(() => { + const loadConfig = async () => { + try { + const response = await fetch(`${config.apiUrl}/config`, { + credentials: 'include' + }); + if (!response.ok) { + throw new Error('Failed to load configuration'); + } + const data = await response.json(); + setSalesVelocityConfig(data.salesVelocityConfig); + } catch (error) { + toast.error(`Failed to load configuration: ${error instanceof Error ? error.message : 'Unknown error'}`); + } + }; + loadConfig(); + }, []); + const handleUpdateSalesVelocityConfig = async () => { try { const response = await fetch(`${config.apiUrl}/config/sales-velocity/1`, { diff --git a/inventory/src/components/settings/Configuration.tsx b/inventory/src/components/settings/Configuration.tsx index 071e51f..0d7a319 100644 --- a/inventory/src/components/settings/Configuration.tsx +++ b/inventory/src/components/settings/Configuration.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useState, useEffect } from 'react'; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; @@ -114,6 +114,29 @@ export function Configuration() { target_rate: 1.0 }); + useEffect(() => { + const loadConfig = async () => { + try { + const response = await fetch(`${config.apiUrl}/config`, { + credentials: 'include' + }); + if (!response.ok) { + throw new Error('Failed to load configuration'); + } + const data = await response.json(); + setStockThresholds(data.stockThresholds); + setLeadTimeThresholds(data.leadTimeThresholds); + setSalesVelocityConfig(data.salesVelocityConfig); + setAbcConfig(data.abcConfig); + setSafetyStockConfig(data.safetyStockConfig); + setTurnoverConfig(data.turnoverConfig); + } catch (error) { + toast.error(`Failed to load configuration: ${error instanceof Error ? error.message : 'Unknown error'}`); + } + }; + loadConfig(); + }, []); + const handleUpdateStockThresholds = async () => { try { const response = await fetch(`${config.apiUrl}/config/stock-thresholds/1`, { diff --git a/inventory/src/components/settings/PerformanceMetrics.tsx b/inventory/src/components/settings/PerformanceMetrics.tsx index f6fd17a..f3b95f4 100644 --- a/inventory/src/components/settings/PerformanceMetrics.tsx +++ b/inventory/src/components/settings/PerformanceMetrics.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useState, useEffect } from 'react'; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; @@ -55,6 +55,26 @@ export function PerformanceMetrics() { target_rate: 1.0 }); + useEffect(() => { + const loadConfig = async () => { + try { + const response = await fetch(`${config.apiUrl}/config`, { + credentials: 'include' + }); + if (!response.ok) { + throw new Error('Failed to load configuration'); + } + const data = await response.json(); + setLeadTimeThresholds(data.leadTimeThresholds); + setAbcConfig(data.abcConfig); + setTurnoverConfig(data.turnoverConfig); + } catch (error) { + toast.error(`Failed to load configuration: ${error instanceof Error ? error.message : 'Unknown error'}`); + } + }; + loadConfig(); + }, []); + const handleUpdateLeadTimeThresholds = async () => { try { const response = await fetch(`${config.apiUrl}/config/lead-time-thresholds/1`, { diff --git a/inventory/src/components/settings/StockManagement.tsx b/inventory/src/components/settings/StockManagement.tsx index 53f9625..3c13487 100644 --- a/inventory/src/components/settings/StockManagement.tsx +++ b/inventory/src/components/settings/StockManagement.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useState, useEffect } from 'react'; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; @@ -45,6 +45,25 @@ export function StockManagement() { service_level: 95.0 }); + useEffect(() => { + const loadConfig = async () => { + try { + const response = await fetch(`${config.apiUrl}/config`, { + credentials: 'include' + }); + if (!response.ok) { + throw new Error('Failed to load configuration'); + } + const data = await response.json(); + setStockThresholds(data.stockThresholds); + setSafetyStockConfig(data.safetyStockConfig); + } catch (error) { + toast.error(`Failed to load configuration: ${error instanceof Error ? error.message : 'Unknown error'}`); + } + }; + loadConfig(); + }, []); + const handleUpdateStockThresholds = async () => { try { const response = await fetch(`${config.apiUrl}/config/stock-thresholds/1`, {