@20052507/dab-enterprise-bws 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ERD_DFD_Documentation.md +330 -0
- package/README.md +307 -0
- package/backend-project/config/database.js +13 -0
- package/backend-project/controllers/authController.js +67 -0
- package/backend-project/controllers/productController.js +104 -0
- package/backend-project/controllers/salesController.js +208 -0
- package/backend-project/controllers/stockController.js +76 -0
- package/backend-project/middleware/authMiddleware.js +9 -0
- package/backend-project/package.json +23 -0
- package/backend-project/routes/authRoutes.js +9 -0
- package/backend-project/routes/productRoutes.js +18 -0
- package/backend-project/routes/salesRoutes.js +22 -0
- package/backend-project/routes/stockRoutes.js +14 -0
- package/backend-project/server.js +133 -0
- package/frontend-project/package.json +41 -0
- package/frontend-project/postcss.config.js +6 -0
- package/frontend-project/public/index.html +14 -0
- package/frontend-project/src/App.js +77 -0
- package/frontend-project/src/components/Navbar.js +47 -0
- package/frontend-project/src/index.css +12 -0
- package/frontend-project/src/index.js +11 -0
- package/frontend-project/src/pages/Dashboard.js +122 -0
- package/frontend-project/src/pages/Login.js +88 -0
- package/frontend-project/src/pages/Products.js +208 -0
- package/frontend-project/src/pages/Reports.js +226 -0
- package/frontend-project/src/pages/Sales.js +239 -0
- package/frontend-project/src/pages/Stock.js +99 -0
- package/frontend-project/tailwind.config.js +10 -0
- package/package.json +17 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
const pool = require('../config/database');
|
|
2
|
+
|
|
3
|
+
const getAllProducts = async (req, res) => {
|
|
4
|
+
try {
|
|
5
|
+
const [products] = await pool.query('SELECT * FROM products ORDER BY CreatedAt DESC');
|
|
6
|
+
res.json(products);
|
|
7
|
+
} catch (error) {
|
|
8
|
+
console.error('Error fetching products:', error);
|
|
9
|
+
res.status(500).json({ message: 'Server error' });
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const getProductById = async (req, res) => {
|
|
14
|
+
try {
|
|
15
|
+
const { id } = req.params;
|
|
16
|
+
const [products] = await pool.query('SELECT * FROM products WHERE ProductID = ?', [id]);
|
|
17
|
+
|
|
18
|
+
if (products.length === 0) {
|
|
19
|
+
return res.status(404).json({ message: 'Product not found' });
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
res.json(products[0]);
|
|
23
|
+
} catch (error) {
|
|
24
|
+
console.error('Error fetching product:', error);
|
|
25
|
+
res.status(500).json({ message: 'Server error' });
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const createProduct = async (req, res) => {
|
|
30
|
+
try {
|
|
31
|
+
const { ProductName, Category, Quantity, UnitPrice } = req.body;
|
|
32
|
+
|
|
33
|
+
if (!ProductName || !Category || Quantity === undefined || !UnitPrice) {
|
|
34
|
+
return res.status(400).json({ message: 'All fields are required' });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const [result] = await pool.query(
|
|
38
|
+
'INSERT INTO products (ProductName, Category, Quantity, UnitPrice) VALUES (?, ?, ?, ?)',
|
|
39
|
+
[ProductName, Category, Quantity, UnitPrice]
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
// Create or update stock status for this product
|
|
43
|
+
await pool.query(
|
|
44
|
+
'INSERT INTO stockstatus (ProductID, AvailableQuantity, SoldQuantity) VALUES (?, ?, 0) ON DUPLICATE KEY UPDATE AvailableQuantity = ?',
|
|
45
|
+
[result.insertId, Quantity, Quantity]
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
res.status(201).json({
|
|
49
|
+
message: 'Product created successfully',
|
|
50
|
+
productId: result.insertId
|
|
51
|
+
});
|
|
52
|
+
} catch (error) {
|
|
53
|
+
console.error('Error creating product:', error);
|
|
54
|
+
res.status(500).json({ message: 'Server error' });
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const updateProduct = async (req, res) => {
|
|
59
|
+
try {
|
|
60
|
+
const { id } = req.params;
|
|
61
|
+
const { ProductName, Category, Quantity, UnitPrice } = req.body;
|
|
62
|
+
|
|
63
|
+
if (!ProductName || !Category || Quantity === undefined || !UnitPrice) {
|
|
64
|
+
return res.status(400).json({ message: 'All fields are required' });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
await pool.query(
|
|
68
|
+
'UPDATE products SET ProductName = ?, Category = ?, Quantity = ?, UnitPrice = ? WHERE ProductID = ?',
|
|
69
|
+
[ProductName, Category, Quantity, UnitPrice, id]
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
// Update stock status
|
|
73
|
+
await pool.query(
|
|
74
|
+
'UPDATE stockstatus SET AvailableQuantity = ? WHERE ProductID = ?',
|
|
75
|
+
[Quantity, id]
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
res.json({ message: 'Product updated successfully' });
|
|
79
|
+
} catch (error) {
|
|
80
|
+
console.error('Error updating product:', error);
|
|
81
|
+
res.status(500).json({ message: 'Server error' });
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const deleteProduct = async (req, res) => {
|
|
86
|
+
try {
|
|
87
|
+
const { id } = req.params;
|
|
88
|
+
|
|
89
|
+
await pool.query('DELETE FROM products WHERE ProductID = ?', [id]);
|
|
90
|
+
|
|
91
|
+
res.json({ message: 'Product deleted successfully' });
|
|
92
|
+
} catch (error) {
|
|
93
|
+
console.error('Error deleting product:', error);
|
|
94
|
+
res.status(500).json({ message: 'Server error' });
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
module.exports = {
|
|
99
|
+
getAllProducts,
|
|
100
|
+
getProductById,
|
|
101
|
+
createProduct,
|
|
102
|
+
updateProduct,
|
|
103
|
+
deleteProduct
|
|
104
|
+
};
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
const pool = require('../config/database');
|
|
2
|
+
|
|
3
|
+
const getAllSales = async (req, res) => {
|
|
4
|
+
try {
|
|
5
|
+
const [sales] = await pool.query(`
|
|
6
|
+
SELECT s.*, p.ProductName, p.Category
|
|
7
|
+
FROM sales s
|
|
8
|
+
JOIN products p ON s.ProductID = p.ProductID
|
|
9
|
+
ORDER BY s.SalesDate DESC, s.CreatedAt DESC
|
|
10
|
+
`);
|
|
11
|
+
res.json(sales);
|
|
12
|
+
} catch (error) {
|
|
13
|
+
console.error('Error fetching sales:', error);
|
|
14
|
+
res.status(500).json({ message: 'Server error' });
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const getSalesById = async (req, res) => {
|
|
19
|
+
try {
|
|
20
|
+
const { id } = req.params;
|
|
21
|
+
const [sales] = await pool.query(`
|
|
22
|
+
SELECT s.*, p.ProductName, p.Category
|
|
23
|
+
FROM sales s
|
|
24
|
+
JOIN products p ON s.ProductID = p.ProductID
|
|
25
|
+
WHERE s.SalesID = ?
|
|
26
|
+
`, [id]);
|
|
27
|
+
|
|
28
|
+
if (sales.length === 0) {
|
|
29
|
+
return res.status(404).json({ message: 'Sale not found' });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
res.json(sales[0]);
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.error('Error fetching sale:', error);
|
|
35
|
+
res.status(500).json({ message: 'Server error' });
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const createSale = async (req, res) => {
|
|
40
|
+
const connection = await pool.getConnection();
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
await connection.beginTransaction();
|
|
44
|
+
|
|
45
|
+
const { ProductID, SoldQuantity, SoldUnitPrice, SalesDate } = req.body;
|
|
46
|
+
|
|
47
|
+
if (!ProductID || !SoldQuantity || !SoldUnitPrice || !SalesDate) {
|
|
48
|
+
await connection.rollback();
|
|
49
|
+
return res.status(400).json({ message: 'All fields are required' });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Check if product exists and has enough stock
|
|
53
|
+
const [products] = await connection.query(
|
|
54
|
+
'SELECT * FROM products WHERE ProductID = ?',
|
|
55
|
+
[ProductID]
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
if (products.length === 0) {
|
|
59
|
+
await connection.rollback();
|
|
60
|
+
return res.status(404).json({ message: 'Product not found' });
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const product = products[0];
|
|
64
|
+
|
|
65
|
+
if (product.Quantity < SoldQuantity) {
|
|
66
|
+
await connection.rollback();
|
|
67
|
+
return res.status(400).json({ message: 'Insufficient stock' });
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Create sale record
|
|
71
|
+
const [result] = await connection.query(
|
|
72
|
+
'INSERT INTO sales (ProductID, SoldQuantity, SoldUnitPrice, SalesDate) VALUES (?, ?, ?, ?)',
|
|
73
|
+
[ProductID, SoldQuantity, SoldUnitPrice, SalesDate]
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
// Update product quantity
|
|
77
|
+
await connection.query(
|
|
78
|
+
'UPDATE products SET Quantity = Quantity - ? WHERE ProductID = ?',
|
|
79
|
+
[SoldQuantity, ProductID]
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
// Update stock status
|
|
83
|
+
await connection.query(
|
|
84
|
+
`UPDATE stockstatus
|
|
85
|
+
SET AvailableQuantity = AvailableQuantity - ?,
|
|
86
|
+
SoldQuantity = SoldQuantity + ?
|
|
87
|
+
WHERE ProductID = ?`,
|
|
88
|
+
[SoldQuantity, SoldQuantity, ProductID]
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
await connection.commit();
|
|
92
|
+
|
|
93
|
+
res.status(201).json({
|
|
94
|
+
message: 'Sale created successfully',
|
|
95
|
+
salesId: result.insertId
|
|
96
|
+
});
|
|
97
|
+
} catch (error) {
|
|
98
|
+
await connection.rollback();
|
|
99
|
+
console.error('Error creating sale:', error);
|
|
100
|
+
res.status(500).json({ message: 'Server error' });
|
|
101
|
+
} finally {
|
|
102
|
+
connection.release();
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const updateSale = async (req, res) => {
|
|
107
|
+
try {
|
|
108
|
+
const { id } = req.params;
|
|
109
|
+
const { ProductID, SoldQuantity, SoldUnitPrice, SalesDate } = req.body;
|
|
110
|
+
|
|
111
|
+
if (!ProductID || !SoldQuantity || !SoldUnitPrice || !SalesDate) {
|
|
112
|
+
return res.status(400).json({ message: 'All fields are required' });
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
await pool.query(
|
|
116
|
+
'UPDATE sales SET ProductID = ?, SoldQuantity = ?, SoldUnitPrice = ?, SalesDate = ? WHERE SalesID = ?',
|
|
117
|
+
[ProductID, SoldQuantity, SoldUnitPrice, SalesDate, id]
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
res.json({ message: 'Sale updated successfully' });
|
|
121
|
+
} catch (error) {
|
|
122
|
+
console.error('Error updating sale:', error);
|
|
123
|
+
res.status(500).json({ message: 'Server error' });
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const deleteSale = async (req, res) => {
|
|
128
|
+
try {
|
|
129
|
+
const { id } = req.params;
|
|
130
|
+
|
|
131
|
+
await pool.query('DELETE FROM sales WHERE SalesID = ?', [id]);
|
|
132
|
+
|
|
133
|
+
res.json({ message: 'Sale deleted successfully' });
|
|
134
|
+
} catch (error) {
|
|
135
|
+
console.error('Error deleting sale:', error);
|
|
136
|
+
res.status(500).json({ message: 'Server error' });
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const getDailySalesReport = async (req, res) => {
|
|
141
|
+
try {
|
|
142
|
+
const { date } = req.query;
|
|
143
|
+
|
|
144
|
+
let query = `
|
|
145
|
+
SELECT
|
|
146
|
+
DATE(SalesDate) as Date,
|
|
147
|
+
COUNT(*) as TotalSales,
|
|
148
|
+
SUM(SoldQuantity) as TotalQuantity,
|
|
149
|
+
SUM(SoldTotalPrice) as TotalRevenue
|
|
150
|
+
FROM sales
|
|
151
|
+
`;
|
|
152
|
+
|
|
153
|
+
let params = [];
|
|
154
|
+
|
|
155
|
+
if (date) {
|
|
156
|
+
query += ' WHERE DATE(SalesDate) = ?';
|
|
157
|
+
params.push(date);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
query += ' GROUP BY DATE(SalesDate) ORDER BY Date DESC';
|
|
161
|
+
|
|
162
|
+
const [report] = await pool.query(query, params);
|
|
163
|
+
res.json(report);
|
|
164
|
+
} catch (error) {
|
|
165
|
+
console.error('Error generating daily sales report:', error);
|
|
166
|
+
res.status(500).json({ message: 'Server error' });
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const getDailySalesDetails = async (req, res) => {
|
|
171
|
+
try {
|
|
172
|
+
const { date } = req.query;
|
|
173
|
+
|
|
174
|
+
let query = `
|
|
175
|
+
SELECT
|
|
176
|
+
s.*,
|
|
177
|
+
p.ProductName,
|
|
178
|
+
p.Category
|
|
179
|
+
FROM sales s
|
|
180
|
+
JOIN products p ON s.ProductID = p.ProductID
|
|
181
|
+
`;
|
|
182
|
+
|
|
183
|
+
let params = [];
|
|
184
|
+
|
|
185
|
+
if (date) {
|
|
186
|
+
query += ' WHERE DATE(s.SalesDate) = ?';
|
|
187
|
+
params.push(date);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
query += ' ORDER BY s.SalesDate DESC, s.CreatedAt DESC';
|
|
191
|
+
|
|
192
|
+
const [details] = await pool.query(query, params);
|
|
193
|
+
res.json(details);
|
|
194
|
+
} catch (error) {
|
|
195
|
+
console.error('Error fetching daily sales details:', error);
|
|
196
|
+
res.status(500).json({ message: 'Server error' });
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
module.exports = {
|
|
201
|
+
getAllSales,
|
|
202
|
+
getSalesById,
|
|
203
|
+
createSale,
|
|
204
|
+
updateSale,
|
|
205
|
+
deleteSale,
|
|
206
|
+
getDailySalesReport,
|
|
207
|
+
getDailySalesDetails
|
|
208
|
+
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
const pool = require('../config/database');
|
|
2
|
+
|
|
3
|
+
const getAllStockStatus = async (req, res) => {
|
|
4
|
+
try {
|
|
5
|
+
const [stock] = await pool.query(`
|
|
6
|
+
SELECT
|
|
7
|
+
ss.*,
|
|
8
|
+
p.ProductName,
|
|
9
|
+
p.Category,
|
|
10
|
+
p.UnitPrice,
|
|
11
|
+
p.TotalPrice
|
|
12
|
+
FROM stockstatus ss
|
|
13
|
+
JOIN products p ON ss.ProductID = p.ProductID
|
|
14
|
+
ORDER BY p.ProductName
|
|
15
|
+
`);
|
|
16
|
+
res.json(stock);
|
|
17
|
+
} catch (error) {
|
|
18
|
+
console.error('Error fetching stock status:', error);
|
|
19
|
+
res.status(500).json({ message: 'Server error' });
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const getStockStatusByProductId = async (req, res) => {
|
|
24
|
+
try {
|
|
25
|
+
const { id } = req.params;
|
|
26
|
+
const [stock] = await pool.query(`
|
|
27
|
+
SELECT
|
|
28
|
+
ss.*,
|
|
29
|
+
p.ProductName,
|
|
30
|
+
p.Category,
|
|
31
|
+
p.UnitPrice,
|
|
32
|
+
p.TotalPrice
|
|
33
|
+
FROM stockstatus ss
|
|
34
|
+
JOIN products p ON ss.ProductID = p.ProductID
|
|
35
|
+
WHERE ss.ProductID = ?
|
|
36
|
+
`, [id]);
|
|
37
|
+
|
|
38
|
+
if (stock.length === 0) {
|
|
39
|
+
return res.status(404).json({ message: 'Stock status not found' });
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
res.json(stock[0]);
|
|
43
|
+
} catch (error) {
|
|
44
|
+
console.error('Error fetching stock status:', error);
|
|
45
|
+
res.status(500).json({ message: 'Server error' });
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const getStockStatusReport = async (req, res) => {
|
|
50
|
+
try {
|
|
51
|
+
const [report] = await pool.query(`
|
|
52
|
+
SELECT
|
|
53
|
+
p.ProductName,
|
|
54
|
+
p.Category,
|
|
55
|
+
ss.AvailableQuantity as StoredQuantity,
|
|
56
|
+
ss.SoldQuantity,
|
|
57
|
+
ss.RemainingQuantity,
|
|
58
|
+
p.UnitPrice,
|
|
59
|
+
p.TotalPrice,
|
|
60
|
+
ss.LastUpdated
|
|
61
|
+
FROM stockstatus ss
|
|
62
|
+
JOIN products p ON ss.ProductID = p.ProductID
|
|
63
|
+
ORDER BY p.ProductName
|
|
64
|
+
`);
|
|
65
|
+
res.json(report);
|
|
66
|
+
} catch (error) {
|
|
67
|
+
console.error('Error generating stock status report:', error);
|
|
68
|
+
res.status(500).json({ message: 'Server error' });
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
module.exports = {
|
|
73
|
+
getAllStockStatus,
|
|
74
|
+
getStockStatusByProductId,
|
|
75
|
+
getStockStatusReport
|
|
76
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "backend-project",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "DAB Enterprise Ltd Backend API",
|
|
5
|
+
"main": "server.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"start": "node server.js",
|
|
8
|
+
"dev": "nodemon server.js"
|
|
9
|
+
},
|
|
10
|
+
"keywords": ["express", "mysql", "api"],
|
|
11
|
+
"author": "",
|
|
12
|
+
"license": "ISC",
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"bcryptjs": "^2.4.3",
|
|
15
|
+
"cors": "^2.8.5",
|
|
16
|
+
"express": "^4.18.2",
|
|
17
|
+
"express-session": "^1.17.3",
|
|
18
|
+
"mysql2": "^3.6.0"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"nodemon": "^3.0.1"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
const { login, logout, checkAuth } = require('../controllers/authController');
|
|
4
|
+
|
|
5
|
+
router.post('/login', login);
|
|
6
|
+
router.post('/logout', logout);
|
|
7
|
+
router.get('/check', checkAuth);
|
|
8
|
+
|
|
9
|
+
module.exports = router;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
const authMiddleware = require('../middleware/authMiddleware');
|
|
4
|
+
const {
|
|
5
|
+
getAllProducts,
|
|
6
|
+
getProductById,
|
|
7
|
+
createProduct,
|
|
8
|
+
updateProduct,
|
|
9
|
+
deleteProduct
|
|
10
|
+
} = require('../controllers/productController');
|
|
11
|
+
|
|
12
|
+
router.get('/', authMiddleware, getAllProducts);
|
|
13
|
+
router.get('/:id', authMiddleware, getProductById);
|
|
14
|
+
router.post('/', authMiddleware, createProduct);
|
|
15
|
+
router.put('/:id', authMiddleware, updateProduct);
|
|
16
|
+
router.delete('/:id', authMiddleware, deleteProduct);
|
|
17
|
+
|
|
18
|
+
module.exports = router;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
const authMiddleware = require('../middleware/authMiddleware');
|
|
4
|
+
const {
|
|
5
|
+
getAllSales,
|
|
6
|
+
getSalesById,
|
|
7
|
+
createSale,
|
|
8
|
+
updateSale,
|
|
9
|
+
deleteSale,
|
|
10
|
+
getDailySalesReport,
|
|
11
|
+
getDailySalesDetails
|
|
12
|
+
} = require('../controllers/salesController');
|
|
13
|
+
|
|
14
|
+
router.get('/', authMiddleware, getAllSales);
|
|
15
|
+
router.get('/:id', authMiddleware, getSalesById);
|
|
16
|
+
router.post('/', authMiddleware, createSale);
|
|
17
|
+
router.put('/:id', authMiddleware, updateSale);
|
|
18
|
+
router.delete('/:id', authMiddleware, deleteSale);
|
|
19
|
+
router.get('/report/daily', authMiddleware, getDailySalesReport);
|
|
20
|
+
router.get('/report/daily/details', authMiddleware, getDailySalesDetails);
|
|
21
|
+
|
|
22
|
+
module.exports = router;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
const authMiddleware = require('../middleware/authMiddleware');
|
|
4
|
+
const {
|
|
5
|
+
getAllStockStatus,
|
|
6
|
+
getStockStatusByProductId,
|
|
7
|
+
getStockStatusReport
|
|
8
|
+
} = require('../controllers/stockController');
|
|
9
|
+
|
|
10
|
+
router.get('/', authMiddleware, getAllStockStatus);
|
|
11
|
+
router.get('/:id', authMiddleware, getStockStatusByProductId);
|
|
12
|
+
router.get('/report/status', authMiddleware, getStockStatusReport);
|
|
13
|
+
|
|
14
|
+
module.exports = router;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const cors = require('cors');
|
|
3
|
+
const session = require('express-session');
|
|
4
|
+
const mysql = require('mysql2/promise');
|
|
5
|
+
|
|
6
|
+
const app = express();
|
|
7
|
+
const PORT = process.env.PORT || 5000;
|
|
8
|
+
|
|
9
|
+
// Middleware
|
|
10
|
+
app.use(cors({
|
|
11
|
+
origin: 'http://localhost:3000',
|
|
12
|
+
credentials: true
|
|
13
|
+
}));
|
|
14
|
+
app.use(express.json());
|
|
15
|
+
app.use(express.urlencoded({ extended: true }));
|
|
16
|
+
|
|
17
|
+
// Session configuration
|
|
18
|
+
app.use(session({
|
|
19
|
+
secret: 'dab-enterprise-secret-key-2026',
|
|
20
|
+
resave: false,
|
|
21
|
+
saveUninitialized: false,
|
|
22
|
+
cookie: {
|
|
23
|
+
secure: false,
|
|
24
|
+
maxAge: 24 * 60 * 60 * 1000 // 24 hours
|
|
25
|
+
}
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
// Routes
|
|
29
|
+
const authRoutes = require('./routes/authRoutes');
|
|
30
|
+
const productRoutes = require('./routes/productRoutes');
|
|
31
|
+
const salesRoutes = require('./routes/salesRoutes');
|
|
32
|
+
const stockRoutes = require('./routes/stockRoutes');
|
|
33
|
+
|
|
34
|
+
app.use('/api/auth', authRoutes);
|
|
35
|
+
app.use('/api/products', productRoutes);
|
|
36
|
+
app.use('/api/sales', salesRoutes);
|
|
37
|
+
app.use('/api/stock', stockRoutes);
|
|
38
|
+
|
|
39
|
+
// Health check
|
|
40
|
+
app.get('/api/health', (req, res) => {
|
|
41
|
+
res.json({ status: 'OK', message: 'DAB Enterprise API is running' });
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Initialize database
|
|
45
|
+
const initializeDatabase = async () => {
|
|
46
|
+
try {
|
|
47
|
+
const connection = await mysql.createConnection({
|
|
48
|
+
host: 'localhost',
|
|
49
|
+
user: 'root',
|
|
50
|
+
password: ''
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Create database
|
|
54
|
+
await connection.query('CREATE DATABASE IF NOT EXISTS db');
|
|
55
|
+
await connection.query('USE db');
|
|
56
|
+
|
|
57
|
+
// Create users table
|
|
58
|
+
await connection.query(`
|
|
59
|
+
CREATE TABLE IF NOT EXISTS users (
|
|
60
|
+
UserID INT AUTO_INCREMENT PRIMARY KEY,
|
|
61
|
+
Username VARCHAR(50) UNIQUE NOT NULL,
|
|
62
|
+
Password VARCHAR(255) NOT NULL,
|
|
63
|
+
CreatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
64
|
+
)
|
|
65
|
+
`);
|
|
66
|
+
|
|
67
|
+
// Create products table
|
|
68
|
+
await connection.query(`
|
|
69
|
+
CREATE TABLE IF NOT EXISTS products (
|
|
70
|
+
ProductID INT AUTO_INCREMENT PRIMARY KEY,
|
|
71
|
+
ProductName VARCHAR(100) NOT NULL,
|
|
72
|
+
Category VARCHAR(50) NOT NULL,
|
|
73
|
+
Quantity INT NOT NULL DEFAULT 0,
|
|
74
|
+
UnitPrice DECIMAL(10, 2) NOT NULL,
|
|
75
|
+
TotalPrice DECIMAL(10, 2) GENERATED ALWAYS AS (Quantity * UnitPrice) STORED,
|
|
76
|
+
CreatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
77
|
+
)
|
|
78
|
+
`);
|
|
79
|
+
|
|
80
|
+
// Create sales table
|
|
81
|
+
await connection.query(`
|
|
82
|
+
CREATE TABLE IF NOT EXISTS sales (
|
|
83
|
+
SalesID INT AUTO_INCREMENT PRIMARY KEY,
|
|
84
|
+
ProductID INT NOT NULL,
|
|
85
|
+
SoldQuantity INT NOT NULL,
|
|
86
|
+
SoldUnitPrice DECIMAL(10, 2) NOT NULL,
|
|
87
|
+
SoldTotalPrice DECIMAL(10, 2) GENERATED ALWAYS AS (SoldQuantity * SoldUnitPrice) STORED,
|
|
88
|
+
SalesDate DATE NOT NULL,
|
|
89
|
+
CreatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
90
|
+
FOREIGN KEY (ProductID) REFERENCES products(ProductID) ON DELETE CASCADE
|
|
91
|
+
)
|
|
92
|
+
`);
|
|
93
|
+
|
|
94
|
+
// Create stockstatus table
|
|
95
|
+
await connection.query(`
|
|
96
|
+
CREATE TABLE IF NOT EXISTS stockstatus (
|
|
97
|
+
StockID INT AUTO_INCREMENT PRIMARY KEY,
|
|
98
|
+
ProductID INT UNIQUE NOT NULL,
|
|
99
|
+
AvailableQuantity INT NOT NULL DEFAULT 0,
|
|
100
|
+
SoldQuantity INT NOT NULL DEFAULT 0,
|
|
101
|
+
RemainingQuantity INT GENERATED ALWAYS AS (AvailableQuantity - SoldQuantity) STORED,
|
|
102
|
+
LastUpdated TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
103
|
+
FOREIGN KEY (ProductID) REFERENCES products(ProductID) ON DELETE CASCADE
|
|
104
|
+
)
|
|
105
|
+
`);
|
|
106
|
+
|
|
107
|
+
// Check if admin user exists, if not create one
|
|
108
|
+
const [users] = await connection.query('SELECT * FROM users WHERE Username = ?', ['admin']);
|
|
109
|
+
|
|
110
|
+
if (users.length === 0) {
|
|
111
|
+
const bcrypt = require('bcryptjs');
|
|
112
|
+
const hashedPassword = await bcrypt.hash('Admin@123', 10);
|
|
113
|
+
await connection.query(
|
|
114
|
+
'INSERT INTO users (Username, Password) VALUES (?, ?)',
|
|
115
|
+
['admin', hashedPassword]
|
|
116
|
+
);
|
|
117
|
+
console.log('Default admin user created: Username: admin, Password: Admin@123');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
await connection.end();
|
|
121
|
+
console.log('Database initialized successfully');
|
|
122
|
+
} catch (error) {
|
|
123
|
+
console.error('Database initialization error:', error);
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
// Start server
|
|
128
|
+
app.listen(PORT, async () => {
|
|
129
|
+
console.log(`Server is running on port ${PORT}`);
|
|
130
|
+
await initializeDatabase();
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
module.exports = app;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "frontend-project",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "DAB Enterprise Ltd Frontend",
|
|
5
|
+
"private": true,
|
|
6
|
+
"dependencies": {
|
|
7
|
+
"axios": "^1.5.0",
|
|
8
|
+
"react": "^18.2.0",
|
|
9
|
+
"react-dom": "^18.2.0",
|
|
10
|
+
"react-router-dom": "^6.16.0",
|
|
11
|
+
"react-scripts": "5.0.1"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"start": "react-scripts start",
|
|
15
|
+
"build": "react-scripts build",
|
|
16
|
+
"test": "react-scripts test",
|
|
17
|
+
"eject": "react-scripts eject"
|
|
18
|
+
},
|
|
19
|
+
"eslintConfig": {
|
|
20
|
+
"extends": [
|
|
21
|
+
"react-app"
|
|
22
|
+
]
|
|
23
|
+
},
|
|
24
|
+
"browserslist": {
|
|
25
|
+
"production": [
|
|
26
|
+
">0.2%",
|
|
27
|
+
"not dead",
|
|
28
|
+
"not op_mini all"
|
|
29
|
+
],
|
|
30
|
+
"development": [
|
|
31
|
+
"last 1 chrome version",
|
|
32
|
+
"last 1 firefox version",
|
|
33
|
+
"last 1 safari version"
|
|
34
|
+
]
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"tailwindcss": "^3.3.3",
|
|
38
|
+
"autoprefixer": "^10.4.15",
|
|
39
|
+
"postcss": "^8.4.29"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<meta name="theme-color" content="#000000" />
|
|
7
|
+
<meta name="description" content="DAB Enterprise Ltd - Business Web Solution" />
|
|
8
|
+
<title>DAB Enterprise Ltd - BWS</title>
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
<noscript>You need to enable JavaScript to run this app.</noscript>
|
|
12
|
+
<div id="root"></div>
|
|
13
|
+
</body>
|
|
14
|
+
</html>
|