Thêm cột sn_custom vào items và items_mix
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
d7bf701d0e
commit
72b4db8a94
335
server.js
335
server.js
|
|
@ -1,27 +1,27 @@
|
||||||
const express = require('express');
|
const express = require("express");
|
||||||
const sqlite3 = require('sqlite3').verbose();
|
const sqlite3 = require("sqlite3").verbose();
|
||||||
const path = require('path');
|
const path = require("path");
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
// Middleware
|
// Middleware
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
app.use(express.static('public'));
|
app.use(express.static("public"));
|
||||||
|
|
||||||
// CORS middleware
|
// CORS middleware
|
||||||
app.use((req, res, next) => {
|
app.use((req, res, next) => {
|
||||||
res.header('Access-Control-Allow-Origin', '*');
|
res.header("Access-Control-Allow-Origin", "*");
|
||||||
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
|
res.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
||||||
res.header('Access-Control-Allow-Headers', 'Content-Type');
|
res.header("Access-Control-Allow-Headers", "Content-Type");
|
||||||
if (req.method === 'OPTIONS') {
|
if (req.method === "OPTIONS") {
|
||||||
return res.sendStatus(200);
|
return res.sendStatus(200);
|
||||||
}
|
}
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Initialize SQLite database
|
// Initialize SQLite database
|
||||||
const db = new sqlite3.Database('./products.db', (err) => {
|
const db = new sqlite3.Database("./products.db", (err) => {
|
||||||
if (err) console.error(err.message);
|
if (err) console.error(err.message);
|
||||||
console.log('Connected to products database.');
|
console.log("Connected to products database.");
|
||||||
});
|
});
|
||||||
|
|
||||||
function runInitAsync(sql, params = []) {
|
function runInitAsync(sql, params = []) {
|
||||||
|
|
@ -52,10 +52,12 @@ async function initializeDatabase() {
|
||||||
|
|
||||||
// Migration for existing databases: add status column if missing
|
// Migration for existing databases: add status column if missing
|
||||||
const columns = await allInitAsync(`PRAGMA table_info(batches)`);
|
const columns = await allInitAsync(`PRAGMA table_info(batches)`);
|
||||||
const hasStatusColumn = columns.some((col) => col.name === 'status');
|
const hasStatusColumn = columns.some((col) => col.name === "status");
|
||||||
if (!hasStatusColumn) {
|
if (!hasStatusColumn) {
|
||||||
await runInitAsync(`ALTER TABLE batches ADD COLUMN status INTEGER NOT NULL DEFAULT 0`);
|
await runInitAsync(
|
||||||
console.log('Added status column to batches table.');
|
`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 (
|
await runInitAsync(`CREATE TABLE IF NOT EXISTS items (
|
||||||
|
|
@ -65,6 +67,7 @@ async function initializeDatabase() {
|
||||||
mpn TEXT NOT NULL,
|
mpn TEXT NOT NULL,
|
||||||
mpn_custom TEXT,
|
mpn_custom TEXT,
|
||||||
sn TEXT NOT NULL,
|
sn TEXT NOT NULL,
|
||||||
|
sn_custom TEXT,
|
||||||
createdAt DATETIME DEFAULT CURRENT_TIMESTAMP,
|
createdAt DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
FOREIGN KEY (batch_id) REFERENCES batches(id) ON DELETE CASCADE
|
FOREIGN KEY (batch_id) REFERENCES batches(id) ON DELETE CASCADE
|
||||||
)`);
|
)`);
|
||||||
|
|
@ -76,18 +79,42 @@ async function initializeDatabase() {
|
||||||
mpn TEXT NOT NULL,
|
mpn TEXT NOT NULL,
|
||||||
mpn_custom TEXT,
|
mpn_custom TEXT,
|
||||||
sn TEXT NOT NULL,
|
sn TEXT NOT NULL,
|
||||||
|
sn_custom TEXT,
|
||||||
createdAt DATETIME DEFAULT CURRENT_TIMESTAMP,
|
createdAt DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
FOREIGN KEY (batch_id) REFERENCES batches(id) ON DELETE CASCADE
|
FOREIGN KEY (batch_id) REFERENCES batches(id) ON DELETE CASCADE
|
||||||
)`);
|
)`);
|
||||||
|
|
||||||
|
// Migration for existing databases: add sn_custom column if missing
|
||||||
|
const itemsColumns = await allInitAsync(`PRAGMA table_info(items)`);
|
||||||
|
if (!itemsColumns.some((col) => col.name === "sn_custom")) {
|
||||||
|
await runInitAsync(`ALTER TABLE items ADD COLUMN sn_custom TEXT`);
|
||||||
|
console.log("Added sn_custom column to items table.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const itemsMixColumns = await allInitAsync(`PRAGMA table_info(items_mix)`);
|
||||||
|
if (!itemsMixColumns.some((col) => col.name === "sn_custom")) {
|
||||||
|
await runInitAsync(`ALTER TABLE items_mix ADD COLUMN sn_custom TEXT`);
|
||||||
|
console.log("Added sn_custom column to items_mix table.");
|
||||||
|
}
|
||||||
|
|
||||||
// Create indexes for better performance
|
// Create indexes for better performance
|
||||||
await runInitAsync(`CREATE INDEX IF NOT EXISTS idx_items_batch_id ON items(batch_id)`);
|
await runInitAsync(
|
||||||
await runInitAsync(`CREATE INDEX IF NOT EXISTS idx_items_mix_batch_id ON items_mix(batch_id)`);
|
`CREATE INDEX IF NOT EXISTS idx_items_batch_id ON items(batch_id)`,
|
||||||
await runInitAsync(`CREATE INDEX IF NOT EXISTS idx_batches_status ON batches(status)`);
|
);
|
||||||
|
await runInitAsync(
|
||||||
|
`CREATE INDEX IF NOT EXISTS idx_items_mix_batch_id ON items_mix(batch_id)`,
|
||||||
|
);
|
||||||
|
await runInitAsync(
|
||||||
|
`CREATE INDEX IF NOT EXISTS idx_batches_status ON batches(status)`,
|
||||||
|
);
|
||||||
await runInitAsync(`CREATE INDEX IF NOT EXISTS idx_items_mpn ON items(mpn)`);
|
await runInitAsync(`CREATE INDEX IF NOT EXISTS idx_items_mpn ON items(mpn)`);
|
||||||
await runInitAsync(`CREATE INDEX IF NOT EXISTS idx_items_sn ON items(sn)`);
|
await runInitAsync(`CREATE INDEX IF NOT EXISTS idx_items_sn ON items(sn)`);
|
||||||
await runInitAsync(`CREATE INDEX IF NOT EXISTS idx_items_brand ON items(brand)`);
|
await runInitAsync(
|
||||||
await runInitAsync(`CREATE INDEX IF NOT EXISTS idx_items_mix_brand ON items_mix(brand)`);
|
`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 ====================
|
||||||
|
|
@ -117,15 +144,17 @@ function finalizeAsync(stmt) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save batch with items and items_mix
|
// Save batch with items and items_mix
|
||||||
app.post('/api/batch/save', async (req, res) => {
|
app.post("/api/batch/save", async (req, res) => {
|
||||||
const { batch_name, items, items_mix } = req.body;
|
const { batch_name, items, items_mix } = req.body;
|
||||||
|
|
||||||
if (!batch_name) {
|
if (!batch_name) {
|
||||||
return res.status(400).json({ error: 'batch_name is required' });
|
return res.status(400).json({ error: "batch_name is required" });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Array.isArray(items) || items.length === 0) {
|
if (!Array.isArray(items) || items.length === 0) {
|
||||||
return res.status(400).json({ error: 'items array is required and must not be empty' });
|
return res
|
||||||
|
.status(400)
|
||||||
|
.json({ error: "items array is required and must not be empty" });
|
||||||
}
|
}
|
||||||
|
|
||||||
let insertedItems = 0;
|
let insertedItems = 0;
|
||||||
|
|
@ -134,20 +163,20 @@ app.post('/api/batch/save', async (req, res) => {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// BEGIN
|
// BEGIN
|
||||||
await runAsync(db, 'BEGIN TRANSACTION');
|
await runAsync(db, "BEGIN TRANSACTION");
|
||||||
|
|
||||||
// Insert batch
|
// Insert batch
|
||||||
const batchResult = await runAsync(
|
const batchResult = await runAsync(
|
||||||
db,
|
db,
|
||||||
'INSERT INTO batches (batch_name) VALUES (?)',
|
"INSERT INTO batches (batch_name) VALUES (?)",
|
||||||
[batch_name]
|
[batch_name],
|
||||||
);
|
);
|
||||||
|
|
||||||
const batchId = batchResult.lastID;
|
const batchId = batchResult.lastID;
|
||||||
|
|
||||||
// ===== Insert items =====
|
// ===== Insert items =====
|
||||||
const itemStmt = db.prepare(
|
const itemStmt = db.prepare(
|
||||||
'INSERT INTO items (batch_id, brand, mpn, mpn_custom, sn) VALUES (?, ?, ?, ?, ?)'
|
"INSERT INTO items (batch_id, brand, mpn, mpn_custom, sn, sn_custom) VALUES (?, ?, ?, ?, ?, ?)",
|
||||||
);
|
);
|
||||||
|
|
||||||
for (let i = 0; i < items.length; i++) {
|
for (let i = 0; i < items.length; i++) {
|
||||||
|
|
@ -164,7 +193,8 @@ app.post('/api/batch/save', async (req, res) => {
|
||||||
item.brand || null,
|
item.brand || null,
|
||||||
item.mpn,
|
item.mpn,
|
||||||
item.mpn_custom || null,
|
item.mpn_custom || null,
|
||||||
item.sn
|
item.sn,
|
||||||
|
item.sn_custom || null,
|
||||||
]);
|
]);
|
||||||
insertedItems++;
|
insertedItems++;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
@ -177,7 +207,7 @@ app.post('/api/batch/save', async (req, res) => {
|
||||||
// ===== Insert items_mix (optional) =====
|
// ===== Insert items_mix (optional) =====
|
||||||
if (Array.isArray(items_mix) && items_mix.length > 0) {
|
if (Array.isArray(items_mix) && items_mix.length > 0) {
|
||||||
const mixStmt = db.prepare(
|
const mixStmt = db.prepare(
|
||||||
'INSERT INTO items_mix (batch_id, brand, mpn, mpn_custom, sn) VALUES (?, ?, ?, ?, ?)'
|
"INSERT INTO items_mix (batch_id, brand, mpn, mpn_custom, sn, sn_custom) VALUES (?, ?, ?, ?, ?, ?)",
|
||||||
);
|
);
|
||||||
|
|
||||||
for (let i = 0; i < items_mix.length; i++) {
|
for (let i = 0; i < items_mix.length; i++) {
|
||||||
|
|
@ -194,11 +224,14 @@ app.post('/api/batch/save', async (req, res) => {
|
||||||
item.brand || null,
|
item.brand || null,
|
||||||
item.mpn,
|
item.mpn,
|
||||||
item.mpn_custom || null,
|
item.mpn_custom || null,
|
||||||
item.sn
|
item.sn,
|
||||||
|
item.sn_custom || null,
|
||||||
]);
|
]);
|
||||||
insertedMixItems++;
|
insertedMixItems++;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
errors.push(`Error inserting mixed item at index ${i}: ${err.message}`);
|
errors.push(
|
||||||
|
`Error inserting mixed item at index ${i}: ${err.message}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -206,7 +239,7 @@ app.post('/api/batch/save', async (req, res) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// COMMIT
|
// COMMIT
|
||||||
await runAsync(db, 'COMMIT');
|
await runAsync(db, "COMMIT");
|
||||||
|
|
||||||
return res.json({
|
return res.json({
|
||||||
success: true,
|
success: true,
|
||||||
|
|
@ -214,37 +247,35 @@ app.post('/api/batch/save', async (req, res) => {
|
||||||
batch_name,
|
batch_name,
|
||||||
inserted_items: insertedItems,
|
inserted_items: insertedItems,
|
||||||
inserted_mix_items: insertedMixItems,
|
inserted_mix_items: insertedMixItems,
|
||||||
errors: errors.length ? errors : undefined
|
errors: errors.length ? errors : undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// ROLLBACK nếu có lỗi nghiêm trọng
|
// ROLLBACK nếu có lỗi nghiêm trọng
|
||||||
try {
|
try {
|
||||||
await runAsync(db, 'ROLLBACK');
|
await runAsync(db, "ROLLBACK");
|
||||||
} catch (_) { }
|
} catch (_) {}
|
||||||
|
|
||||||
return res.status(500).json({
|
return res.status(500).json({
|
||||||
error: err.message
|
error: err.message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// Get all batches with their items and items_mix
|
// Get all batches with their items and items_mix
|
||||||
app.get('/api/batch/get-all', (req, res) => {
|
app.get("/api/batch/get-all", (req, res) => {
|
||||||
const page = parseInt(req.query.page) || 1;
|
const page = parseInt(req.query.page) || 1;
|
||||||
const limit = parseInt(req.query.limit) || 50;
|
const limit = parseInt(req.query.limit) || 50;
|
||||||
const search = req.query.search || '';
|
const search = req.query.search || "";
|
||||||
const sortBy = req.query.sortBy || 'id';
|
const sortBy = req.query.sortBy || "id";
|
||||||
const sortOrder = req.query.sortOrder || 'DESC';
|
const sortOrder = req.query.sortOrder || "DESC";
|
||||||
const offset = (page - 1) * limit;
|
const offset = (page - 1) * limit;
|
||||||
|
|
||||||
// Validate sortBy
|
// Validate sortBy
|
||||||
const validColumns = ['id', 'batch_name', 'createdAt'];
|
const validColumns = ["id", "batch_name", "createdAt"];
|
||||||
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";
|
||||||
|
|
||||||
const whereConditions = ['status = 0'];
|
const whereConditions = ["status = 0"];
|
||||||
let searchParams = [];
|
let searchParams = [];
|
||||||
|
|
||||||
if (search) {
|
if (search) {
|
||||||
|
|
@ -270,7 +301,7 @@ app.get('/api/batch/get-all', (req, res) => {
|
||||||
searchParams = [searchParam, searchParam, searchParam, searchParam];
|
searchParams = [searchParam, searchParam, searchParam, searchParam];
|
||||||
}
|
}
|
||||||
|
|
||||||
const whereClause = ` WHERE ${whereConditions.join(' AND ')}`;
|
const whereClause = ` WHERE ${whereConditions.join(" AND ")}`;
|
||||||
const countQuery = `SELECT COUNT(*) as total FROM batches${whereClause}`;
|
const countQuery = `SELECT COUNT(*) as total FROM batches${whereClause}`;
|
||||||
const query = `SELECT * FROM batches${whereClause} ORDER BY ${column} ${order} LIMIT ? OFFSET ?`;
|
const query = `SELECT * FROM batches${whereClause} ORDER BY ${column} ${order} LIMIT ? OFFSET ?`;
|
||||||
const countParams = [...searchParams];
|
const countParams = [...searchParams];
|
||||||
|
|
@ -292,7 +323,7 @@ app.get('/api/batch/get-all', (req, res) => {
|
||||||
total: 0,
|
total: 0,
|
||||||
page,
|
page,
|
||||||
limit,
|
limit,
|
||||||
totalPages: 0
|
totalPages: 0,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -300,156 +331,194 @@ app.get('/api/batch/get-all', (req, res) => {
|
||||||
let processed = 0;
|
let processed = 0;
|
||||||
const batchesWithItems = [];
|
const batchesWithItems = [];
|
||||||
|
|
||||||
batches.forEach(batch => {
|
batches.forEach((batch) => {
|
||||||
// Get items
|
// Get items
|
||||||
db.all('SELECT brand, mpn, mpn_custom, sn, createdAt FROM items WHERE batch_id = ?', [batch.id], (err, items) => {
|
db.all(
|
||||||
if (err) {
|
"SELECT brand, mpn, mpn_custom, sn, sn_custom, createdAt FROM items WHERE batch_id = ?",
|
||||||
console.error('Error fetching items:', err);
|
[batch.id],
|
||||||
items = [];
|
(err, items) => {
|
||||||
}
|
|
||||||
|
|
||||||
// Get items_mix
|
|
||||||
db.all('SELECT brand, mpn, mpn_custom, sn, createdAt FROM items_mix WHERE batch_id = ?', [batch.id], (err, items_mix) => {
|
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error('Error fetching items_mix:', err);
|
console.error("Error fetching items:", err);
|
||||||
items_mix = [];
|
items = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
batchesWithItems.push({
|
// Get items_mix
|
||||||
id: batch.id,
|
db.all(
|
||||||
batch_name: batch.batch_name,
|
"SELECT brand, mpn, mpn_custom, sn, sn_custom, createdAt FROM items_mix WHERE batch_id = ?",
|
||||||
createdAt: batch.createdAt,
|
[batch.id],
|
||||||
items: items || [],
|
(err, items_mix) => {
|
||||||
items_mix: items_mix || []
|
if (err) {
|
||||||
});
|
console.error("Error fetching items_mix:", err);
|
||||||
|
items_mix = [];
|
||||||
|
}
|
||||||
|
|
||||||
processed++;
|
batchesWithItems.push({
|
||||||
|
id: batch.id,
|
||||||
|
batch_name: batch.batch_name,
|
||||||
|
createdAt: batch.createdAt,
|
||||||
|
items: items || [],
|
||||||
|
items_mix: items_mix || [],
|
||||||
|
});
|
||||||
|
|
||||||
if (processed === batches.length) {
|
processed++;
|
||||||
res.json({
|
|
||||||
batches: batchesWithItems,
|
if (processed === batches.length) {
|
||||||
total: countRow.total,
|
res.json({
|
||||||
page,
|
batches: batchesWithItems,
|
||||||
limit,
|
total: countRow.total,
|
||||||
totalPages: Math.ceil(countRow.total / limit)
|
page,
|
||||||
});
|
limit,
|
||||||
}
|
totalPages: Math.ceil(countRow.total / limit),
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Get single batch by ID
|
// Get single batch by ID
|
||||||
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 = ? AND status = 0', [id], (err, batch) => {
|
db.get(
|
||||||
if (err) {
|
"SELECT * FROM batches WHERE id = ? AND status = 0",
|
||||||
return res.status(500).json({ error: err.message });
|
[id],
|
||||||
}
|
(err, batch) => {
|
||||||
|
|
||||||
if (!batch) {
|
|
||||||
return res.status(404).json({ error: 'Batch not found' });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get items
|
|
||||||
db.all('SELECT brand, mpn, mpn_custom, sn, createdAt FROM items WHERE batch_id = ?', [id], (err, items) => {
|
|
||||||
if (err) {
|
if (err) {
|
||||||
return res.status(500).json({ error: err.message });
|
return res.status(500).json({ error: err.message });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get items_mix
|
if (!batch) {
|
||||||
db.all('SELECT brand, mpn, mpn_custom, sn, createdAt FROM items_mix WHERE batch_id = ?', [id], (err, items_mix) => {
|
return res.status(404).json({ error: "Batch not found" });
|
||||||
if (err) {
|
}
|
||||||
return res.status(500).json({ error: err.message });
|
|
||||||
}
|
|
||||||
|
|
||||||
res.json({
|
// Get items
|
||||||
id: batch.id,
|
db.all(
|
||||||
batch_name: batch.batch_name,
|
"SELECT brand, mpn, mpn_custom, sn, sn_custom, createdAt FROM items WHERE batch_id = ?",
|
||||||
createdAt: batch.createdAt,
|
[id],
|
||||||
items: items || [],
|
(err, items) => {
|
||||||
items_mix: items_mix || []
|
if (err) {
|
||||||
});
|
return res.status(500).json({ error: err.message });
|
||||||
});
|
}
|
||||||
});
|
|
||||||
});
|
// Get items_mix
|
||||||
|
db.all(
|
||||||
|
"SELECT brand, mpn, mpn_custom, sn, sn_custom, createdAt FROM items_mix WHERE batch_id = ?",
|
||||||
|
[id],
|
||||||
|
(err, items_mix) => {
|
||||||
|
if (err) {
|
||||||
|
return res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
id: batch.id,
|
||||||
|
batch_name: batch.batch_name,
|
||||||
|
createdAt: batch.createdAt,
|
||||||
|
items: items || [],
|
||||||
|
items_mix: items_mix || [],
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Soft delete batch (update status from 0 to 1)
|
// 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('UPDATE batches SET status = 1 WHERE id = ? AND status = 0', [id], function (err) {
|
db.run(
|
||||||
if (err) {
|
"UPDATE batches SET status = 1 WHERE id = ? AND status = 0",
|
||||||
return res.status(500).json({ error: err.message });
|
[id],
|
||||||
}
|
function (err) {
|
||||||
|
if (err) {
|
||||||
|
return res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
|
||||||
if (this.changes === 0) {
|
if (this.changes === 0) {
|
||||||
return res.status(404).json({ error: 'Batch not found' });
|
return res.status(404).json({ error: "Batch not found" });
|
||||||
}
|
}
|
||||||
|
|
||||||
res.json({ success: true, updated: this.changes, deleted: this.changes });
|
res.json({ success: true, updated: this.changes, deleted: this.changes });
|
||||||
});
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Search items across all batches
|
// Search items across all batches
|
||||||
app.get('/api/items/search', (req, res) => {
|
app.get("/api/items/search", (req, res) => {
|
||||||
const search = req.query.q || '';
|
const search = req.query.q || "";
|
||||||
|
|
||||||
if (!search) {
|
if (!search) {
|
||||||
return res.status(400).json({ error: 'Search query is required' });
|
return res.status(400).json({ error: "Search query is required" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const searchParam = `%${search}%`;
|
const searchParam = `%${search}%`;
|
||||||
|
|
||||||
const query = `
|
const query = `
|
||||||
SELECT
|
SELECT
|
||||||
b.id as batch_id,
|
b.id as batch_id,
|
||||||
b.batch_name,
|
b.batch_name,
|
||||||
i.brand,
|
i.brand,
|
||||||
i.mpn,
|
i.mpn,
|
||||||
i.mpn_custom,
|
i.mpn_custom,
|
||||||
i.sn,
|
i.sn,
|
||||||
|
i.sn_custom,
|
||||||
i.createdAt,
|
i.createdAt,
|
||||||
'items' as type
|
'items' as type
|
||||||
FROM items i
|
FROM items i
|
||||||
JOIN batches b ON i.batch_id = b.id
|
JOIN batches b ON i.batch_id = b.id
|
||||||
WHERE i.mpn LIKE ? OR i.sn LIKE ? OR i.brand LIKE ? OR i.mpn_custom LIKE ?
|
WHERE i.mpn LIKE ? OR i.sn LIKE ? OR i.brand LIKE ? OR i.mpn_custom LIKE ? OR i.sn_custom LIKE ?
|
||||||
UNION ALL
|
UNION ALL
|
||||||
SELECT
|
SELECT
|
||||||
b.id as batch_id,
|
b.id as batch_id,
|
||||||
b.batch_name,
|
b.batch_name,
|
||||||
im.brand,
|
im.brand,
|
||||||
im.mpn,
|
im.mpn,
|
||||||
im.mpn_custom,
|
im.mpn_custom,
|
||||||
im.sn,
|
im.sn,
|
||||||
|
im.sn_custom,
|
||||||
im.createdAt,
|
im.createdAt,
|
||||||
'items_mix' as type
|
'items_mix' as type
|
||||||
FROM items_mix im
|
FROM items_mix im
|
||||||
JOIN batches b ON im.batch_id = b.id
|
JOIN batches b ON im.batch_id = b.id
|
||||||
WHERE im.mpn LIKE ? OR im.sn LIKE ? OR im.brand LIKE ? OR im.mpn_custom LIKE ?
|
WHERE im.mpn LIKE ? OR im.sn LIKE ? OR im.brand LIKE ? OR im.mpn_custom LIKE ? OR im.sn_custom LIKE ?
|
||||||
ORDER BY createdAt DESC
|
ORDER BY createdAt DESC
|
||||||
LIMIT 100
|
LIMIT 100
|
||||||
`;
|
`;
|
||||||
|
|
||||||
db.all(query, [
|
db.all(
|
||||||
searchParam, searchParam, searchParam, searchParam,
|
query,
|
||||||
searchParam, searchParam, searchParam, searchParam
|
[
|
||||||
], (err, results) => {
|
searchParam,
|
||||||
if (err) {
|
searchParam,
|
||||||
return res.status(500).json({ error: err.message });
|
searchParam,
|
||||||
}
|
searchParam,
|
||||||
|
searchParam,
|
||||||
|
searchParam,
|
||||||
|
searchParam,
|
||||||
|
searchParam,
|
||||||
|
searchParam,
|
||||||
|
searchParam,
|
||||||
|
],
|
||||||
|
(err, results) => {
|
||||||
|
if (err) {
|
||||||
|
return res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
|
||||||
res.json({ results });
|
res.json({ results });
|
||||||
});
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Serve HTML UI
|
// Serve HTML UI
|
||||||
app.get('/', (req, res) => {
|
app.get("/", (req, res) => {
|
||||||
res.sendFile(path.join(__dirname, 'public', 'index.html'));
|
res.sendFile(path.join(__dirname, "public", "index.html"));
|
||||||
});
|
});
|
||||||
|
|
||||||
const PORT = process.env.PORT || 4444;
|
const PORT = process.env.PORT || 4444;
|
||||||
|
|
@ -460,6 +529,6 @@ initializeDatabase()
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.error('Database initialization failed:', err.message);
|
console.error("Database initialization failed:", err.message);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue