project-startup 1.1.0 → 1.1.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/package.json
CHANGED
|
@@ -2,24 +2,19 @@ const db = require("../db");
|
|
|
2
2
|
|
|
3
3
|
// GET /api/parts
|
|
4
4
|
exports.getAllParts = async (req, res) => {
|
|
5
|
-
const [parts] = await db.query(
|
|
6
|
-
"SELECT * FROM spare_parts ORDER BY name ASC"
|
|
7
|
-
);
|
|
5
|
+
const [parts] = await db.query("SELECT * FROM spare_parts ORDER BY name ASC");
|
|
8
6
|
res.json(parts);
|
|
9
7
|
};
|
|
10
8
|
|
|
11
9
|
// GET /api/parts/:id
|
|
12
10
|
exports.getPartById = async (req, res) => {
|
|
13
|
-
const [
|
|
14
|
-
"SELECT * FROM spare_parts WHERE id = ?",
|
|
15
|
-
[req.params.id]
|
|
16
|
-
);
|
|
11
|
+
const [parts] = await db.query("SELECT * FROM spare_parts WHERE id = ?", [req.params.id]);
|
|
17
12
|
|
|
18
|
-
if (
|
|
19
|
-
return res.status(404).json({ error: "Spare part not found
|
|
13
|
+
if (parts.length === 0) {
|
|
14
|
+
return res.status(404).json({ error: "Spare part not found" });
|
|
20
15
|
}
|
|
21
16
|
|
|
22
|
-
res.json(
|
|
17
|
+
res.json(parts[0]);
|
|
23
18
|
};
|
|
24
19
|
|
|
25
20
|
// POST /api/parts
|
|
@@ -27,11 +22,11 @@ exports.createPart = async (req, res) => {
|
|
|
27
22
|
const { name, quantity, unitPrice } = req.body;
|
|
28
23
|
|
|
29
24
|
if (!name || quantity == null || !unitPrice) {
|
|
30
|
-
return res.status(400).json({ error: "name, quantity and unitPrice
|
|
25
|
+
return res.status(400).json({ error: "name, quantity and unitPrice required" });
|
|
31
26
|
}
|
|
32
27
|
|
|
33
28
|
if (quantity < 0) {
|
|
34
|
-
return res.status(400).json({ error: "Quantity cannot be negative
|
|
29
|
+
return res.status(400).json({ error: "Quantity cannot be negative" });
|
|
35
30
|
}
|
|
36
31
|
|
|
37
32
|
const [result] = await db.query(
|
|
@@ -39,66 +34,39 @@ exports.createPart = async (req, res) => {
|
|
|
39
34
|
[name.trim(), quantity, unitPrice]
|
|
40
35
|
);
|
|
41
36
|
|
|
42
|
-
const [
|
|
43
|
-
|
|
44
|
-
[result.insertId]
|
|
45
|
-
);
|
|
46
|
-
|
|
47
|
-
res.status(201).json(rows[0]);
|
|
37
|
+
const [parts] = await db.query("SELECT * FROM spare_parts WHERE id = ?", [result.insertId]);
|
|
38
|
+
res.status(201).json(parts[0]);
|
|
48
39
|
};
|
|
49
40
|
|
|
50
41
|
// PUT /api/parts/:id
|
|
51
42
|
exports.updatePart = async (req, res) => {
|
|
52
|
-
const [
|
|
53
|
-
"SELECT * FROM spare_parts WHERE id = ?",
|
|
54
|
-
[req.params.id]
|
|
55
|
-
);
|
|
43
|
+
const [parts] = await db.query("SELECT * FROM spare_parts WHERE id = ?", [req.params.id]);
|
|
56
44
|
|
|
57
|
-
if (
|
|
58
|
-
return res.status(404).json({ error: "Spare part not found
|
|
45
|
+
if (parts.length === 0) {
|
|
46
|
+
return res.status(404).json({ error: "Spare part not found" });
|
|
59
47
|
}
|
|
60
48
|
|
|
61
49
|
const { name, unitPrice } = req.body;
|
|
62
|
-
// Note: quantity is managed by stock-in / stock-out, not edited directly
|
|
63
50
|
|
|
64
51
|
await db.query(
|
|
65
52
|
`UPDATE spare_parts SET
|
|
66
|
-
name
|
|
53
|
+
name = COALESCE(?, name),
|
|
67
54
|
unit_price = COALESCE(?, unit_price)
|
|
68
55
|
WHERE id = ?`,
|
|
69
56
|
[name || null, unitPrice || null, req.params.id]
|
|
70
57
|
);
|
|
71
58
|
|
|
72
|
-
const [updated] = await db.query(
|
|
73
|
-
"SELECT * FROM spare_parts WHERE id = ?",
|
|
74
|
-
[req.params.id]
|
|
75
|
-
);
|
|
76
|
-
|
|
59
|
+
const [updated] = await db.query("SELECT * FROM spare_parts WHERE id = ?", [req.params.id]);
|
|
77
60
|
res.json(updated[0]);
|
|
78
61
|
};
|
|
79
62
|
|
|
80
|
-
// DELETE /api/parts/:id
|
|
63
|
+
// DELETE /api/parts/:id — SIMPLIFIED (removed stock-in check)
|
|
81
64
|
exports.deletePart = async (req, res) => {
|
|
82
|
-
|
|
83
|
-
const [stockIns] = await db.query(
|
|
84
|
-
"SELECT id FROM stock_in WHERE spare_part_id = ?",
|
|
85
|
-
[req.params.id]
|
|
86
|
-
);
|
|
87
|
-
|
|
88
|
-
if (stockIns.length > 0) {
|
|
89
|
-
return res.status(400).json({
|
|
90
|
-
error: "Cannot delete a part that has stock-in records.",
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const [result] = await db.query(
|
|
95
|
-
"DELETE FROM spare_parts WHERE id = ?",
|
|
96
|
-
[req.params.id]
|
|
97
|
-
);
|
|
65
|
+
const [result] = await db.query("DELETE FROM spare_parts WHERE id = ?", [req.params.id]);
|
|
98
66
|
|
|
99
67
|
if (result.affectedRows === 0) {
|
|
100
|
-
return res.status(404).json({ error: "Spare part not found
|
|
68
|
+
return res.status(404).json({ error: "Spare part not found" });
|
|
101
69
|
}
|
|
102
70
|
|
|
103
|
-
res.json({ message: "Spare part deleted
|
|
104
|
-
};
|
|
71
|
+
res.json({ message: "Spare part deleted" });
|
|
72
|
+
};
|
|
@@ -1,18 +1,10 @@
|
|
|
1
1
|
const db = require("../db");
|
|
2
2
|
|
|
3
3
|
// GET /api/stock-in
|
|
4
|
-
// Returns all import records joined with part name
|
|
5
4
|
exports.getAllStockIn = async (req, res) => {
|
|
6
5
|
const [rows] = await db.query(
|
|
7
|
-
`SELECT
|
|
8
|
-
|
|
9
|
-
si.quantity,
|
|
10
|
-
si.remaining_qty,
|
|
11
|
-
si.unit_price,
|
|
12
|
-
si.total_price,
|
|
13
|
-
si.imported_at,
|
|
14
|
-
sp.id AS spare_part_id,
|
|
15
|
-
sp.name AS spare_part_name
|
|
6
|
+
`SELECT si.id, si.quantity, si.remaining_qty, si.unit_price, si.total_price,
|
|
7
|
+
si.imported_at, sp.id AS spare_part_id, sp.name AS spare_part_name
|
|
16
8
|
FROM stock_in si
|
|
17
9
|
JOIN spare_parts sp ON sp.id = si.spare_part_id
|
|
18
10
|
ORDER BY si.imported_at DESC`
|
|
@@ -23,9 +15,7 @@ exports.getAllStockIn = async (req, res) => {
|
|
|
23
15
|
// GET /api/stock-in/:id
|
|
24
16
|
exports.getStockInById = async (req, res) => {
|
|
25
17
|
const [rows] = await db.query(
|
|
26
|
-
`SELECT
|
|
27
|
-
si.*,
|
|
28
|
-
sp.name AS spare_part_name
|
|
18
|
+
`SELECT si.*, sp.name AS spare_part_name
|
|
29
19
|
FROM stock_in si
|
|
30
20
|
JOIN spare_parts sp ON sp.id = si.spare_part_id
|
|
31
21
|
WHERE si.id = ?`,
|
|
@@ -33,127 +23,74 @@ exports.getStockInById = async (req, res) => {
|
|
|
33
23
|
);
|
|
34
24
|
|
|
35
25
|
if (rows.length === 0) {
|
|
36
|
-
return res.status(404).json({ error: "Stock-in record not found
|
|
26
|
+
return res.status(404).json({ error: "Stock-in record not found" });
|
|
37
27
|
}
|
|
38
28
|
|
|
39
29
|
res.json(rows[0]);
|
|
40
30
|
};
|
|
41
31
|
|
|
42
|
-
// POST /api/stock-in
|
|
43
|
-
// Creates a new import batch and increments spare_parts.quantity
|
|
32
|
+
// POST /api/stock-in — SIMPLIFIED (no transaction)
|
|
44
33
|
exports.createStockIn = async (req, res) => {
|
|
45
34
|
const { sparePartId, quantity, unitPrice } = req.body;
|
|
46
35
|
|
|
47
36
|
if (!sparePartId || !quantity || !unitPrice) {
|
|
48
|
-
return res.status(400).json({
|
|
49
|
-
error: "sparePartId, quantity and unitPrice are required.",
|
|
50
|
-
});
|
|
37
|
+
return res.status(400).json({ error: "sparePartId, quantity and unitPrice required" });
|
|
51
38
|
}
|
|
52
39
|
|
|
53
40
|
if (quantity < 1) {
|
|
54
|
-
return res.status(400).json({ error: "Quantity must be at least 1
|
|
41
|
+
return res.status(400).json({ error: "Quantity must be at least 1" });
|
|
55
42
|
}
|
|
56
43
|
|
|
57
|
-
// Verify
|
|
58
|
-
const [
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
);
|
|
62
|
-
|
|
63
|
-
if (partRows.length === 0) {
|
|
64
|
-
return res.status(404).json({ error: "Spare part not found." });
|
|
44
|
+
// Verify part exists
|
|
45
|
+
const [parts] = await db.query("SELECT * FROM spare_parts WHERE id = ?", [sparePartId]);
|
|
46
|
+
if (parts.length === 0) {
|
|
47
|
+
return res.status(404).json({ error: "Spare part not found" });
|
|
65
48
|
}
|
|
66
49
|
|
|
67
50
|
const totalPrice = quantity * unitPrice;
|
|
68
51
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
// remaining_qty starts equal to quantity — decremented by stock-out later
|
|
76
|
-
const [result] = await conn.query(
|
|
77
|
-
`INSERT INTO stock_in
|
|
78
|
-
(spare_part_id, quantity, remaining_qty, unit_price, total_price)
|
|
79
|
-
VALUES (?, ?, ?, ?, ?)`,
|
|
80
|
-
[sparePartId, quantity, quantity, unitPrice, totalPrice]
|
|
81
|
-
);
|
|
82
|
-
|
|
83
|
-
// Increment the part's total stock quantity
|
|
84
|
-
await conn.query(
|
|
85
|
-
"UPDATE spare_parts SET quantity = quantity + ? WHERE id = ?",
|
|
86
|
-
[quantity, sparePartId]
|
|
87
|
-
);
|
|
88
|
-
|
|
89
|
-
await conn.commit();
|
|
90
|
-
|
|
91
|
-
// Return the full record with part name
|
|
92
|
-
const [rows] = await conn.query(
|
|
93
|
-
`SELECT si.*, sp.name AS spare_part_name
|
|
94
|
-
FROM stock_in si
|
|
95
|
-
JOIN spare_parts sp ON sp.id = si.spare_part_id
|
|
96
|
-
WHERE si.id = ?`,
|
|
97
|
-
[result.insertId]
|
|
98
|
-
);
|
|
99
|
-
|
|
100
|
-
res.status(201).json(rows[0]);
|
|
101
|
-
|
|
102
|
-
} catch (err) {
|
|
103
|
-
await conn.rollback();
|
|
104
|
-
throw err;
|
|
105
|
-
} finally {
|
|
106
|
-
conn.release();
|
|
107
|
-
}
|
|
108
|
-
};
|
|
52
|
+
// Insert stock-in batch
|
|
53
|
+
const [result] = await db.query(
|
|
54
|
+
`INSERT INTO stock_in (spare_part_id, quantity, remaining_qty, unit_price, total_price)
|
|
55
|
+
VALUES (?, ?, ?, ?, ?)`,
|
|
56
|
+
[sparePartId, quantity, quantity, unitPrice, totalPrice]
|
|
57
|
+
);
|
|
109
58
|
|
|
110
|
-
//
|
|
111
|
-
|
|
112
|
-
|
|
59
|
+
// Increment part's total stock
|
|
60
|
+
await db.query(
|
|
61
|
+
"UPDATE spare_parts SET quantity = quantity + ? WHERE id = ?",
|
|
62
|
+
[quantity, sparePartId]
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
// Return full record with part name
|
|
113
66
|
const [rows] = await db.query(
|
|
114
|
-
|
|
115
|
-
|
|
67
|
+
`SELECT si.*, sp.name AS spare_part_name
|
|
68
|
+
FROM stock_in si
|
|
69
|
+
JOIN spare_parts sp ON sp.id = si.spare_part_id
|
|
70
|
+
WHERE si.id = ?`,
|
|
71
|
+
[result.insertId]
|
|
116
72
|
);
|
|
117
73
|
|
|
74
|
+
res.status(201).json(rows[0]);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// DELETE /api/stock-in/:id — SIMPLIFIED (no transaction, no stock-out check)
|
|
78
|
+
exports.deleteStockIn = async (req, res) => {
|
|
79
|
+
const [rows] = await db.query("SELECT * FROM stock_in WHERE id = ?", [req.params.id]);
|
|
80
|
+
|
|
118
81
|
if (rows.length === 0) {
|
|
119
|
-
return res.status(404).json({ error: "Stock-in record not found
|
|
82
|
+
return res.status(404).json({ error: "Stock-in record not found" });
|
|
120
83
|
}
|
|
121
84
|
|
|
122
85
|
const stockIn = rows[0];
|
|
123
86
|
|
|
124
|
-
//
|
|
125
|
-
|
|
126
|
-
"
|
|
127
|
-
[
|
|
87
|
+
// Give quantity back to spare part
|
|
88
|
+
await db.query(
|
|
89
|
+
"UPDATE spare_parts SET quantity = quantity - ? WHERE id = ?",
|
|
90
|
+
[stockIn.quantity, stockIn.spare_part_id]
|
|
128
91
|
);
|
|
129
92
|
|
|
130
|
-
|
|
131
|
-
return res.status(400).json({
|
|
132
|
-
error: "Cannot delete a batch that has stock-out records.",
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
const conn = await db.getConnection();
|
|
137
|
-
|
|
138
|
-
try {
|
|
139
|
-
await conn.beginTransaction();
|
|
93
|
+
await db.query("DELETE FROM stock_in WHERE id = ?", [req.params.id]);
|
|
140
94
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
"UPDATE spare_parts SET quantity = quantity - ? WHERE id = ?",
|
|
144
|
-
[stockIn.quantity, stockIn.spare_part_id]
|
|
145
|
-
);
|
|
146
|
-
|
|
147
|
-
await conn.query("DELETE FROM stock_in WHERE id = ?", [req.params.id]);
|
|
148
|
-
|
|
149
|
-
await conn.commit();
|
|
150
|
-
|
|
151
|
-
res.json({ message: "Stock-in record deleted and quantity reversed." });
|
|
152
|
-
|
|
153
|
-
} catch (err) {
|
|
154
|
-
await conn.rollback();
|
|
155
|
-
throw err;
|
|
156
|
-
} finally {
|
|
157
|
-
conn.release();
|
|
158
|
-
}
|
|
159
|
-
};
|
|
95
|
+
res.json({ message: "Stock-in deleted and quantity reversed" });
|
|
96
|
+
};
|
|
@@ -3,19 +3,11 @@ const db = require("../db");
|
|
|
3
3
|
// GET /api/stock-out
|
|
4
4
|
exports.getAllStockOut = async (req, res) => {
|
|
5
5
|
const [rows] = await db.query(
|
|
6
|
-
`SELECT
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
so.unit_price,
|
|
10
|
-
so.total_price,
|
|
11
|
-
so.sold_at,
|
|
12
|
-
sp.id AS spare_part_id,
|
|
13
|
-
sp.name AS spare_part_name,
|
|
14
|
-
si.id AS stock_in_id,
|
|
15
|
-
si.unit_price AS import_unit_price,
|
|
16
|
-
si.imported_at
|
|
6
|
+
`SELECT so.id, so.quantity, so.unit_price, so.total_price, so.sold_at,
|
|
7
|
+
sp.id AS spare_part_id, sp.name AS spare_part_name,
|
|
8
|
+
si.id AS stock_in_id, si.unit_price AS import_unit_price, si.imported_at
|
|
17
9
|
FROM stock_out so
|
|
18
|
-
JOIN stock_in
|
|
10
|
+
JOIN stock_in si ON si.id = so.stock_in_id
|
|
19
11
|
JOIN spare_parts sp ON sp.id = so.spare_part_id
|
|
20
12
|
ORDER BY so.sold_at DESC`
|
|
21
13
|
);
|
|
@@ -25,159 +17,112 @@ exports.getAllStockOut = async (req, res) => {
|
|
|
25
17
|
// GET /api/stock-out/:id
|
|
26
18
|
exports.getStockOutById = async (req, res) => {
|
|
27
19
|
const [rows] = await db.query(
|
|
28
|
-
`SELECT
|
|
29
|
-
so.*,
|
|
30
|
-
sp.name AS spare_part_name,
|
|
31
|
-
si.remaining_qty,
|
|
32
|
-
si.unit_price AS import_unit_price
|
|
20
|
+
`SELECT so.*, sp.name AS spare_part_name, si.remaining_qty, si.unit_price AS import_unit_price
|
|
33
21
|
FROM stock_out so
|
|
34
|
-
JOIN stock_in
|
|
22
|
+
JOIN stock_in si ON si.id = so.stock_in_id
|
|
35
23
|
JOIN spare_parts sp ON sp.id = so.spare_part_id
|
|
36
24
|
WHERE so.id = ?`,
|
|
37
25
|
[req.params.id]
|
|
38
26
|
);
|
|
39
27
|
|
|
40
28
|
if (rows.length === 0) {
|
|
41
|
-
return res.status(404).json({ error: "Stock-out record not found
|
|
29
|
+
return res.status(404).json({ error: "Stock-out record not found" });
|
|
42
30
|
}
|
|
43
31
|
|
|
44
32
|
res.json(rows[0]);
|
|
45
33
|
};
|
|
46
34
|
|
|
47
|
-
// POST /api/stock-out
|
|
48
|
-
// Validates against stock_in.remaining_qty, then decrements both
|
|
49
|
-
// stock_in.remaining_qty and spare_parts.quantity
|
|
35
|
+
// POST /api/stock-out — SIMPLIFIED (no transaction, no FOR UPDATE lock)
|
|
50
36
|
exports.createStockOut = async (req, res) => {
|
|
51
37
|
const { stockInId, quantity, unitPrice } = req.body;
|
|
52
38
|
|
|
53
39
|
if (!stockInId || !quantity || !unitPrice) {
|
|
54
|
-
return res.status(400).json({
|
|
55
|
-
error: "stockInId, quantity and unitPrice are required.",
|
|
56
|
-
});
|
|
40
|
+
return res.status(400).json({ error: "stockInId, quantity and unitPrice required" });
|
|
57
41
|
}
|
|
58
42
|
|
|
59
43
|
if (quantity < 1) {
|
|
60
|
-
return res.status(400).json({ error: "Quantity must be at least 1
|
|
44
|
+
return res.status(400).json({ error: "Quantity must be at least 1" });
|
|
61
45
|
}
|
|
62
46
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
FOR UPDATE`,
|
|
75
|
-
[stockInId]
|
|
76
|
-
);
|
|
77
|
-
|
|
78
|
-
if (siRows.length === 0) {
|
|
79
|
-
await conn.rollback();
|
|
80
|
-
return res.status(404).json({ error: "Stock-in batch not found." });
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const batch = siRows[0];
|
|
84
|
-
|
|
85
|
-
// Quantity sold must not exceed what remains in this specific batch
|
|
86
|
-
if (quantity > batch.remaining_qty) {
|
|
87
|
-
await conn.rollback();
|
|
88
|
-
return res.status(400).json({
|
|
89
|
-
error: `Only ${batch.remaining_qty} unit(s) remaining in this batch.`,
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const totalPrice = quantity * unitPrice;
|
|
94
|
-
|
|
95
|
-
// Insert the sale record
|
|
96
|
-
const [result] = await conn.query(
|
|
97
|
-
`INSERT INTO stock_out
|
|
98
|
-
(stock_in_id, spare_part_id, quantity, unit_price, total_price)
|
|
99
|
-
VALUES (?, ?, ?, ?, ?)`,
|
|
100
|
-
[stockInId, batch.spare_part_id, quantity, unitPrice, totalPrice]
|
|
101
|
-
);
|
|
102
|
-
|
|
103
|
-
// Decrement remaining units in this import batch
|
|
104
|
-
await conn.query(
|
|
105
|
-
"UPDATE stock_in SET remaining_qty = remaining_qty - ? WHERE id = ?",
|
|
106
|
-
[quantity, stockInId]
|
|
107
|
-
);
|
|
108
|
-
|
|
109
|
-
// Decrement the part's total stock
|
|
110
|
-
await conn.query(
|
|
111
|
-
"UPDATE spare_parts SET quantity = quantity - ? WHERE id = ?",
|
|
112
|
-
[quantity, batch.spare_part_id]
|
|
113
|
-
);
|
|
114
|
-
|
|
115
|
-
await conn.commit();
|
|
116
|
-
|
|
117
|
-
// Return full record with names attached
|
|
118
|
-
const [rows] = await conn.query(
|
|
119
|
-
`SELECT
|
|
120
|
-
so.*,
|
|
121
|
-
sp.name AS spare_part_name,
|
|
122
|
-
si.unit_price AS import_unit_price,
|
|
123
|
-
si.remaining_qty
|
|
124
|
-
FROM stock_out so
|
|
125
|
-
JOIN stock_in si ON si.id = so.stock_in_id
|
|
126
|
-
JOIN spare_parts sp ON sp.id = so.spare_part_id
|
|
127
|
-
WHERE so.id = ?`,
|
|
128
|
-
[result.insertId]
|
|
129
|
-
);
|
|
130
|
-
|
|
131
|
-
res.status(201).json(rows[0]);
|
|
132
|
-
|
|
133
|
-
} catch (err) {
|
|
134
|
-
await conn.rollback();
|
|
135
|
-
throw err;
|
|
136
|
-
} finally {
|
|
137
|
-
conn.release();
|
|
47
|
+
// Get the stock-in batch
|
|
48
|
+
const [siRows] = await db.query(
|
|
49
|
+
`SELECT si.*, sp.id AS part_id
|
|
50
|
+
FROM stock_in si
|
|
51
|
+
JOIN spare_parts sp ON sp.id = si.spare_part_id
|
|
52
|
+
WHERE si.id = ?`,
|
|
53
|
+
[stockInId]
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
if (siRows.length === 0) {
|
|
57
|
+
return res.status(404).json({ error: "Stock-in batch not found" });
|
|
138
58
|
}
|
|
139
|
-
};
|
|
140
59
|
|
|
141
|
-
|
|
142
|
-
// Reverses the sale: restores remaining_qty on the batch and quantity on the part
|
|
143
|
-
exports.deleteStockOut = async (req, res) => {
|
|
144
|
-
const [rows] = await db.query(
|
|
145
|
-
"SELECT * FROM stock_out WHERE id = ?",
|
|
146
|
-
[req.params.id]
|
|
147
|
-
);
|
|
60
|
+
const batch = siRows[0];
|
|
148
61
|
|
|
149
|
-
|
|
150
|
-
|
|
62
|
+
// Check remaining quantity (simple check, no lock)
|
|
63
|
+
if (quantity > batch.remaining_qty) {
|
|
64
|
+
return res.status(400).json({
|
|
65
|
+
error: `Only ${batch.remaining_qty} unit(s) remaining in this batch`
|
|
66
|
+
});
|
|
151
67
|
}
|
|
152
68
|
|
|
153
|
-
const
|
|
154
|
-
const conn = await db.getConnection();
|
|
69
|
+
const totalPrice = quantity * unitPrice;
|
|
155
70
|
|
|
156
|
-
|
|
157
|
-
|
|
71
|
+
// Insert sale record
|
|
72
|
+
const [result] = await db.query(
|
|
73
|
+
`INSERT INTO stock_out (stock_in_id, spare_part_id, quantity, unit_price, total_price)
|
|
74
|
+
VALUES (?, ?, ?, ?, ?)`,
|
|
75
|
+
[stockInId, batch.part_id, quantity, unitPrice, totalPrice]
|
|
76
|
+
);
|
|
158
77
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
78
|
+
// Decrement remaining units in batch
|
|
79
|
+
await db.query(
|
|
80
|
+
"UPDATE stock_in SET remaining_qty = remaining_qty - ? WHERE id = ?",
|
|
81
|
+
[quantity, stockInId]
|
|
82
|
+
);
|
|
164
83
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
84
|
+
// Decrement part's total stock
|
|
85
|
+
await db.query(
|
|
86
|
+
"UPDATE spare_parts SET quantity = quantity - ? WHERE id = ?",
|
|
87
|
+
[quantity, batch.part_id]
|
|
88
|
+
);
|
|
170
89
|
|
|
171
|
-
|
|
90
|
+
// Return full record
|
|
91
|
+
const [rows] = await db.query(
|
|
92
|
+
`SELECT so.*, sp.name AS spare_part_name, si.unit_price AS import_unit_price, si.remaining_qty
|
|
93
|
+
FROM stock_out so
|
|
94
|
+
JOIN stock_in si ON si.id = so.stock_in_id
|
|
95
|
+
JOIN spare_parts sp ON sp.id = so.spare_part_id
|
|
96
|
+
WHERE so.id = ?`,
|
|
97
|
+
[result.insertId]
|
|
98
|
+
);
|
|
172
99
|
|
|
173
|
-
|
|
100
|
+
res.status(201).json(rows[0]);
|
|
101
|
+
};
|
|
174
102
|
|
|
175
|
-
|
|
103
|
+
// DELETE /api/stock-out/:id — SIMPLIFIED (no transaction)
|
|
104
|
+
exports.deleteStockOut = async (req, res) => {
|
|
105
|
+
const [rows] = await db.query("SELECT * FROM stock_out WHERE id = ?", [req.params.id]);
|
|
176
106
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
throw err;
|
|
180
|
-
} finally {
|
|
181
|
-
conn.release();
|
|
107
|
+
if (rows.length === 0) {
|
|
108
|
+
return res.status(404).json({ error: "Stock-out record not found" });
|
|
182
109
|
}
|
|
183
|
-
|
|
110
|
+
|
|
111
|
+
const sale = rows[0];
|
|
112
|
+
|
|
113
|
+
// Restore batch's remaining quantity
|
|
114
|
+
await db.query(
|
|
115
|
+
"UPDATE stock_in SET remaining_qty = remaining_qty + ? WHERE id = ?",
|
|
116
|
+
[sale.quantity, sale.stock_in_id]
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
// Restore part's total stock
|
|
120
|
+
await db.query(
|
|
121
|
+
"UPDATE spare_parts SET quantity = quantity + ? WHERE id = ?",
|
|
122
|
+
[sale.quantity, sale.spare_part_id]
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
await db.query("DELETE FROM stock_out WHERE id = ?", [sale.id]);
|
|
126
|
+
|
|
127
|
+
res.json({ message: "Stock-out deleted and quantity restored" });
|
|
128
|
+
};
|