Hiệu chỉnh xóa batch mềm

This commit is contained in:
Truong Vo 2026-04-03 10:53:48 +07:00
parent def35fceff
commit d7bf701d0e
1 changed files with 82 additions and 50 deletions

132
server.js
View File

@ -24,17 +24,41 @@ const db = new sqlite3.Database('./products.db', (err) => {
console.log('Connected to products database.'); console.log('Connected to products database.');
}); });
// Create tables function runInitAsync(sql, params = []) {
db.serialize(() => { return new Promise((resolve, reject) => {
// Batches table db.run(sql, params, (err) => {
db.run(`CREATE TABLE IF NOT EXISTS batches ( if (err) reject(err);
else resolve();
});
});
}
function allInitAsync(sql, params = []) {
return new Promise((resolve, reject) => {
db.all(sql, params, (err, rows) => {
if (err) reject(err);
else resolve(rows);
});
});
}
async function initializeDatabase() {
await runInitAsync(`CREATE TABLE IF NOT EXISTS batches (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
batch_name TEXT NOT NULL UNIQUE, batch_name TEXT NOT NULL UNIQUE,
createdAt DATETIME DEFAULT CURRENT_TIMESTAMP createdAt DATETIME DEFAULT CURRENT_TIMESTAMP,
status INTEGER NOT NULL DEFAULT 0
)`); )`);
// Items table (for valid items) // Migration for existing databases: add status column if missing
db.run(`CREATE TABLE IF NOT EXISTS items ( const columns = await allInitAsync(`PRAGMA table_info(batches)`);
const hasStatusColumn = columns.some((col) => col.name === 'status');
if (!hasStatusColumn) {
await runInitAsync(`ALTER TABLE batches ADD COLUMN status INTEGER NOT NULL DEFAULT 0`);
console.log('Added status column to batches table.');
}
await runInitAsync(`CREATE TABLE IF NOT EXISTS items (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
batch_id INTEGER NOT NULL, batch_id INTEGER NOT NULL,
brand TEXT, brand TEXT,
@ -45,8 +69,7 @@ db.serialize(() => {
FOREIGN KEY (batch_id) REFERENCES batches(id) ON DELETE CASCADE FOREIGN KEY (batch_id) REFERENCES batches(id) ON DELETE CASCADE
)`); )`);
// Items_mix table (for mixed MPN items) await runInitAsync(`CREATE TABLE IF NOT EXISTS items_mix (
db.run(`CREATE TABLE IF NOT EXISTS items_mix (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
batch_id INTEGER NOT NULL, batch_id INTEGER NOT NULL,
brand TEXT, brand TEXT,
@ -58,13 +81,14 @@ db.serialize(() => {
)`); )`);
// Create indexes for better performance // Create indexes for better performance
db.run(`CREATE INDEX IF NOT EXISTS idx_items_batch_id ON items(batch_id)`); await runInitAsync(`CREATE INDEX IF NOT EXISTS idx_items_batch_id ON items(batch_id)`);
db.run(`CREATE INDEX IF NOT EXISTS idx_items_mix_batch_id ON items_mix(batch_id)`); await runInitAsync(`CREATE INDEX IF NOT EXISTS idx_items_mix_batch_id ON items_mix(batch_id)`);
db.run(`CREATE INDEX IF NOT EXISTS idx_items_mpn ON items(mpn)`); await runInitAsync(`CREATE INDEX IF NOT EXISTS idx_batches_status ON batches(status)`);
db.run(`CREATE INDEX IF NOT EXISTS idx_items_sn ON items(sn)`); await runInitAsync(`CREATE INDEX IF NOT EXISTS idx_items_mpn ON items(mpn)`);
db.run(`CREATE INDEX IF NOT EXISTS idx_items_brand ON items(brand)`); await runInitAsync(`CREATE INDEX IF NOT EXISTS idx_items_sn ON items(sn)`);
db.run(`CREATE INDEX IF NOT EXISTS idx_items_mix_brand ON items_mix(brand)`); await runInitAsync(`CREATE INDEX IF NOT EXISTS idx_items_brand ON items(brand)`);
}); await runInitAsync(`CREATE INDEX IF NOT EXISTS idx_items_mix_brand ON items_mix(brand)`);
}
// ==================== BATCH API ROUTES ==================== // ==================== BATCH API ROUTES ====================
@ -220,43 +244,44 @@ app.get('/api/batch/get-all', (req, res) => {
const column = validColumns.includes(sortBy) ? sortBy : 'id'; const column = validColumns.includes(sortBy) ? sortBy : 'id';
const order = sortOrder.toUpperCase() === 'ASC' ? 'ASC' : 'DESC'; const order = sortOrder.toUpperCase() === 'ASC' ? 'ASC' : 'DESC';
let query = 'SELECT * FROM batches'; const whereConditions = ['status = 0'];
let countQuery = 'SELECT COUNT(*) as total FROM batches'; let searchParams = [];
let params = [];
if (search) { if (search) {
const searchCondition = ` const searchCondition = `
WHERE (
batch_name LIKE ? batch_name LIKE ?
OR id LIKE ? OR id LIKE ?
OR EXISTS ( OR EXISTS (
SELECT 1 FROM items SELECT 1 FROM items
WHERE items.batch_id = batches.id WHERE items.batch_id = batches.id
AND sn LIKE ? AND sn LIKE ?
)
OR EXISTS (
SELECT 1 FROM items_mix
WHERE items_mix.batch_id = batches.id
AND sn LIKE ?
)
) )
OR EXISTS ( `;
SELECT 1 FROM items_mix whereConditions.push(searchCondition);
WHERE items_mix.batch_id = batches.id
AND sn LIKE ?
)
`;
query += searchCondition; const searchParam = `%${search}%`;
countQuery += searchCondition; searchParams = [searchParam, searchParam, searchParam, searchParam];
}
const searchParam = `%${search}%`; const whereClause = ` WHERE ${whereConditions.join(' AND ')}`;
params = [searchParam, searchParam, searchParam, searchParam]; const countQuery = `SELECT COUNT(*) as total FROM batches${whereClause}`;
} const query = `SELECT * FROM batches${whereClause} ORDER BY ${column} ${order} LIMIT ? OFFSET ?`;
const countParams = [...searchParams];
const queryParams = [...searchParams, limit, offset];
query += ` ORDER BY ${column} ${order} LIMIT ? OFFSET ?`; db.get(countQuery, countParams, (err, countRow) => {
params.push(limit, offset);
db.get(countQuery, search ? params.slice(0, 2) : [], (err, countRow) => {
if (err) { if (err) {
return res.status(500).json({ error: err.message }); return res.status(500).json({ error: err.message });
} }
db.all(query, params, (err, batches) => { db.all(query, queryParams, (err, batches) => {
if (err) { if (err) {
return res.status(500).json({ error: err.message }); return res.status(500).json({ error: err.message });
} }
@ -320,7 +345,7 @@ app.get('/api/batch/get-all', (req, res) => {
app.get('/api/batch/get/:id', (req, res) => { app.get('/api/batch/get/:id', (req, res) => {
const id = req.params.id; const id = req.params.id;
db.get('SELECT * FROM batches WHERE id = ?', [id], (err, batch) => { db.get('SELECT * FROM batches WHERE id = ? AND status = 0', [id], (err, batch) => {
if (err) { if (err) {
return res.status(500).json({ error: err.message }); return res.status(500).json({ error: err.message });
} }
@ -353,11 +378,11 @@ app.get('/api/batch/get/:id', (req, res) => {
}); });
}); });
// Delete batch (cascade delete items and items_mix) // Soft delete batch (update status from 0 to 1)
app.delete('/api/batch/delete/:id', (req, res) => { app.delete('/api/batch/delete/:id', (req, res) => {
const id = req.params.id; const id = req.params.id;
db.run('DELETE FROM batches WHERE id = ?', id, function (err) { db.run('UPDATE batches SET status = 1 WHERE id = ? AND status = 0', [id], function (err) {
if (err) { if (err) {
return res.status(500).json({ error: err.message }); return res.status(500).json({ error: err.message });
} }
@ -366,7 +391,7 @@ app.delete('/api/batch/delete/:id', (req, res) => {
return res.status(404).json({ error: 'Batch not found' }); return res.status(404).json({ error: 'Batch not found' });
} }
res.json({ success: true, deleted: this.changes }); res.json({ success: true, updated: this.changes, deleted: this.changes });
}); });
}); });
@ -428,6 +453,13 @@ app.get('/', (req, res) => {
}); });
const PORT = process.env.PORT || 4444; const PORT = process.env.PORT || 4444;
app.listen(PORT, () => { initializeDatabase()
console.log(`Server is running on http://localhost:${PORT}`); .then(() => {
}); app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});
})
.catch((err) => {
console.error('Database initialization failed:', err.message);
process.exit(1);
});