Add/update inital try of order components and add csv update script + update import script

This commit is contained in:
2025-01-10 00:01:43 -05:00
parent afe8510751
commit 8bdd188dfe
17 changed files with 38513 additions and 37881 deletions

View File

@@ -0,0 +1,35 @@
const express = require('express');
const cors = require('cors');
const mysql = require('mysql2/promise');
const productsRouter = require('./routes/products');
const dashboardRouter = require('./routes/dashboard');
const ordersRouter = require('./routes/orders');
const app = express();
app.use(cors());
app.use(express.json());
// Database connection
const pool = mysql.createPool({
host: 'localhost',
user: 'root',
password: '',
database: 'inventory',
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0
});
// Make db pool available in routes
app.locals.pool = pool;
// Routes
app.use('/api/products', productsRouter);
app.use('/api/dashboard', dashboardRouter);
app.use('/api/orders', ordersRouter);
const PORT = process.env.PORT || 3001;
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});

View File

@@ -0,0 +1,255 @@
const express = require('express');
const router = express.Router();
// Get all orders with pagination, filtering, and sorting
router.get('/', async (req, res) => {
const pool = req.app.locals.pool;
try {
const page = parseInt(req.query.page) || 1;
const limit = parseInt(req.query.limit) || 50;
const offset = (page - 1) * limit;
const search = req.query.search || '';
const status = req.query.status || 'all';
const fromDate = req.query.fromDate ? new Date(req.query.fromDate) : null;
const toDate = req.query.toDate ? new Date(req.query.toDate) : null;
const minAmount = parseFloat(req.query.minAmount) || 0;
const maxAmount = req.query.maxAmount ? parseFloat(req.query.maxAmount) : null;
const sortColumn = req.query.sortColumn || 'date';
const sortDirection = req.query.sortDirection === 'desc' ? 'DESC' : 'ASC';
// Build the WHERE clause
const conditions = ['o1.canceled = false'];
const params = [];
if (search) {
conditions.push('(o1.order_number LIKE ? OR o1.customer LIKE ?)');
params.push(`%${search}%`, `%${search}%`);
}
if (status !== 'all') {
conditions.push('o1.status = ?');
params.push(status);
}
if (fromDate) {
conditions.push('DATE(o1.date) >= DATE(?)');
params.push(fromDate.toISOString());
}
if (toDate) {
conditions.push('DATE(o1.date) <= DATE(?)');
params.push(toDate.toISOString());
}
if (minAmount > 0) {
conditions.push('total_amount >= ?');
params.push(minAmount);
}
if (maxAmount) {
conditions.push('total_amount <= ?');
params.push(maxAmount);
}
// Get total count for pagination
const [countResult] = await pool.query(`
SELECT COUNT(DISTINCT o1.order_number) as total
FROM orders o1
LEFT JOIN (
SELECT order_number, SUM(price * quantity) as total_amount
FROM orders
GROUP BY order_number
) totals ON o1.order_number = totals.order_number
WHERE ${conditions.join(' AND ')}
`, params);
const total = countResult[0].total;
// Get paginated results
const query = `
SELECT
o1.order_number,
o1.customer,
o1.date,
o1.status,
o1.payment_method,
o1.shipping_method,
COUNT(o2.product_id) as items_count,
SUM(o2.price * o2.quantity) as total_amount
FROM orders o1
JOIN orders o2 ON o1.order_number = o2.order_number
WHERE ${conditions.join(' AND ')}
GROUP BY
o1.order_number,
o1.customer,
o1.date,
o1.status,
o1.payment_method,
o1.shipping_method
ORDER BY ${
sortColumn === 'items_count' || sortColumn === 'total_amount'
? `${sortColumn} ${sortDirection}`
: `o1.${sortColumn} ${sortDirection}`
}
LIMIT ? OFFSET ?
`;
const [rows] = await pool.query(query, [...params, limit, offset]);
// Get order statistics
const [stats] = await pool.query(`
WITH CurrentStats AS (
SELECT
COUNT(DISTINCT order_number) as total_orders,
SUM(price * quantity) as total_revenue
FROM orders
WHERE canceled = false
AND DATE(date) >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)
),
PreviousStats AS (
SELECT
COUNT(DISTINCT order_number) as prev_orders,
SUM(price * quantity) as prev_revenue
FROM orders
WHERE canceled = false
AND DATE(date) BETWEEN DATE_SUB(CURDATE(), INTERVAL 60 DAY) AND DATE_SUB(CURDATE(), INTERVAL 30 DAY)
),
OrderValues AS (
SELECT
order_number,
SUM(price * quantity) as order_value
FROM orders
WHERE canceled = false
AND DATE(date) >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)
GROUP BY order_number
)
SELECT
cs.total_orders,
cs.total_revenue,
CASE
WHEN ps.prev_orders > 0
THEN ((cs.total_orders - ps.prev_orders) / ps.prev_orders * 100)
ELSE 0
END as order_growth,
CASE
WHEN ps.prev_revenue > 0
THEN ((cs.total_revenue - ps.prev_revenue) / ps.prev_revenue * 100)
ELSE 0
END as revenue_growth,
CASE
WHEN cs.total_orders > 0
THEN (cs.total_revenue / cs.total_orders)
ELSE 0
END as average_order_value,
CASE
WHEN ps.prev_orders > 0
THEN (ps.prev_revenue / ps.prev_orders)
ELSE 0
END as prev_average_order_value
FROM CurrentStats cs
CROSS JOIN PreviousStats ps
`);
const orderStats = stats[0];
res.json({
orders: rows.map(row => ({
...row,
total_amount: parseFloat(row.total_amount) || 0,
items_count: parseInt(row.items_count) || 0,
date: row.date
})),
pagination: {
total,
pages: Math.ceil(total / limit),
currentPage: page,
limit
},
stats: {
totalOrders: parseInt(orderStats.total_orders) || 0,
totalRevenue: parseFloat(orderStats.total_revenue) || 0,
orderGrowth: parseFloat(orderStats.order_growth) || 0,
revenueGrowth: parseFloat(orderStats.revenue_growth) || 0,
averageOrderValue: parseFloat(orderStats.average_order_value) || 0,
aovGrowth: orderStats.prev_average_order_value > 0
? ((orderStats.average_order_value - orderStats.prev_average_order_value) / orderStats.prev_average_order_value * 100)
: 0,
conversionRate: 2.5, // Placeholder - would need actual visitor data
conversionGrowth: 0.5 // Placeholder - would need actual visitor data
}
});
} catch (error) {
console.error('Error fetching orders:', error);
res.status(500).json({ error: 'Failed to fetch orders' });
}
});
// Get a single order with its items
router.get('/:orderNumber', async (req, res) => {
const pool = req.app.locals.pool;
try {
// Get order details
const [orderRows] = await pool.query(`
SELECT DISTINCT
o1.order_number,
o1.customer,
o1.date,
o1.status,
o1.payment_method,
o1.shipping_method,
o1.shipping_address,
o1.billing_address,
COUNT(o2.product_id) as items_count,
SUM(o2.price * o2.quantity) as total_amount
FROM orders o1
JOIN orders o2 ON o1.order_number = o2.order_number
WHERE o1.order_number = ? AND o1.canceled = false
GROUP BY
o1.order_number,
o1.customer,
o1.date,
o1.status,
o1.payment_method,
o1.shipping_method,
o1.shipping_address,
o1.billing_address
`, [req.params.orderNumber]);
if (orderRows.length === 0) {
return res.status(404).json({ error: 'Order not found' });
}
// Get order items
const [itemRows] = await pool.query(`
SELECT
o.product_id,
p.title,
p.sku,
o.quantity,
o.price,
(o.price * o.quantity) as total
FROM orders o
JOIN products p ON o.product_id = p.product_id
WHERE o.order_number = ? AND o.canceled = false
`, [req.params.orderNumber]);
const order = {
...orderRows[0],
total_amount: parseFloat(orderRows[0].total_amount) || 0,
items_count: parseInt(orderRows[0].items_count) || 0,
items: itemRows.map(item => ({
...item,
price: parseFloat(item.price) || 0,
total: parseFloat(item.total) || 0,
quantity: parseInt(item.quantity) || 0
}))
};
res.json(order);
} catch (error) {
console.error('Error fetching order:', error);
res.status(500).json({ error: 'Failed to fetch order' });
}
});
module.exports = router;

View File

@@ -36,6 +36,7 @@ const cors = require('cors');
const mysql = require('mysql2/promise');
const productsRouter = require('./routes/products');
const dashboardRouter = require('./routes/dashboard');
const ordersRouter = require('./routes/orders');
// Ensure required directories exist
['logs', 'uploads'].forEach(dir => {
@@ -111,6 +112,7 @@ pool.getConnection()
// Routes
app.use('/api/products', productsRouter);
app.use('/api/dashboard', dashboardRouter);
app.use('/api/orders', ordersRouter);
// Basic health check route
app.get('/health', (req, res) => {