const Database = require('better-sqlite3'); const { v4: uuidv4 } = require('uuid'); const path = require('path'); const dbPath = process.env.DB_PATH || path.join(__dirname, 'data/school.db'); console.log('Seeding database at:', dbPath); const db = new Database(dbPath); console.log('Seeding comprehensive inventory catalog and stock levels...'); try { // Define and immediately execute the transaction db.transaction(() => { // 1. Ensure categories exist const categories = [ { name: 'Stationery', code: 'STAT' }, { name: 'Cleaning Supplies', code: 'CLEN' }, { name: 'Furniture', code: 'FURN' }, { name: 'Electronics', code: 'ELEC' }, { name: 'Catering & Kitchen', code: 'KITC' }, { name: 'Sports & Grounds', code: 'SPRT' }, { name: 'Bedding & Hostels', code: 'BEDD' } ]; for (const cat of categories) { const existing = db.prepare('SELECT id FROM item_categories WHERE code = ?').get(cat.code); if (!existing) { db.prepare(` INSERT INTO item_categories (uid, name, code, sync_status) VALUES (?, ?, ?, 'pending') `).run(uuidv4(), cat.name, cat.code); console.log(`Inserted category: ${cat.name} (${cat.code})`); } else { console.log(`Category exists: ${cat.name} (${cat.code})`); } } // 2. Ensure suppliers exist const suppliers = [ { name: 'Global Supplies Ltd', code: 'SUP01', contact_person: 'John Doe', email: 'john@globalsupplies.com', phone: '0772111222' }, { name: 'Catering & Bedding Wholesale', code: 'SUP02', contact_person: 'Jane Moyo', email: 'jane@cateringbedding.co.zw', phone: '0773444555' }, { name: 'Sports Equipment World', code: 'SUP03', contact_person: 'David Sibanda', email: 'david@sportsworld.com', phone: '0712888999' } ]; for (const sup of suppliers) { const existing = db.prepare('SELECT id FROM suppliers WHERE code = ?').get(sup.code); if (!existing) { db.prepare(` INSERT INTO suppliers (uid, name, code, contact_person, email, phone, sync_status) VALUES (?, ?, ?, ?, ?, ?, 'pending') `).run(uuidv4(), sup.name, sup.code, sup.contact_person, sup.email, sup.phone); console.log(`Inserted supplier: ${sup.name} (${sup.code})`); } else { console.log(`Supplier exists: ${sup.name} (${sup.code})`); } } // 3. Ensure store locations exist const storeLocations = [ { name: 'Main Warehouse', code: 'WH01', is_warehouse: 1 }, { name: 'School Store', code: 'ST01', is_warehouse: 0 }, { name: 'Science Lab Prep Room', code: 'SL01', is_warehouse: 0 } ]; for (const loc of storeLocations) { const existing = db.prepare('SELECT id FROM store_locations WHERE code = ?').get(loc.code); if (!existing) { db.prepare(` INSERT INTO store_locations (uid, name, code, is_warehouse, sync_status) VALUES (?, ?, ?, ?, 'pending') `).run(uuidv4(), loc.name, loc.code, loc.is_warehouse); console.log(`Inserted store location: ${loc.name} (${loc.code})`); } else { console.log(`Store location exists: ${loc.name} (${loc.code})`); } } // Get DB IDs of categories, suppliers, locations to link correctly const getCatId = (code) => db.prepare('SELECT id FROM item_categories WHERE code = ?').get(code)?.id; const getSupId = (code) => db.prepare('SELECT id FROM suppliers WHERE code = ?').get(code)?.id; const getLocId = (code) => db.prepare('SELECT id FROM store_locations WHERE code = ?').get(code)?.id; // 4. Create Items const itemsToSeed = [ // Stationery { name: 'A4 Notebooks', code: 'STAT-01', barcode: '8901234567890', description: 'Ruled A4 120 pages notebook', catCode: 'STAT', unit: 'pcs', purchase_price: 1.5, reorder_level: 50, supCode: 'SUP01' }, { name: 'Whiteboard Markers', code: 'STAT-02', barcode: '8901234567891', description: 'Black dry erase whiteboard markers', catCode: 'STAT', unit: 'box', purchase_price: 6.0, reorder_level: 10, supCode: 'SUP01' }, { name: 'Form 3 Mathematics Textbooks', code: 'STAT-03', barcode: '8901234567892', description: 'NextGen High School Maths textbook', catCode: 'STAT', unit: 'pcs', purchase_price: 12.0, reorder_level: 15, supCode: 'SUP01' }, // Hostel/Bedding { name: 'Single Bed Mattress', code: 'BEDD-01', barcode: '8901234567893', description: 'Standard high-density foam single mattress', catCode: 'BEDD', unit: 'pcs', purchase_price: 45.0, reorder_level: 5, supCode: 'SUP02' }, { name: 'Cotton Pillow', code: 'BEDD-02', barcode: '8901234567894', description: 'Hypoallergenic soft cotton sleeping pillow', catCode: 'BEDD', unit: 'pcs', purchase_price: 8.0, reorder_level: 10, supCode: 'SUP02' }, { name: 'Woolen Blanket', code: 'BEDD-03', barcode: '8901234567895', description: 'Warm heavy-duty blue woolen blanket', catCode: 'BEDD', unit: 'pcs', purchase_price: 18.0, reorder_level: 8, supCode: 'SUP02' }, // Catering/Kitchen { name: 'Industrial Boiling Pot (50L)', code: 'KITC-01', barcode: '8901234567896', description: 'Stainless steel giant pots for bulk cooking', catCode: 'KITC', unit: 'pcs', purchase_price: 120.0, reorder_level: 2, supCode: 'SUP02' }, { name: 'Serving Plates', code: 'KITC-02', barcode: '8901234567897', description: 'Durable melamine school lunch plates', catCode: 'KITC', unit: 'pcs', purchase_price: 2.2, reorder_level: 100, supCode: 'SUP02' }, { name: 'Catering Chef Knife', code: 'KITC-03', barcode: '8901234567898', description: 'Professional 10-inch kitchen slicing knife', catCode: 'KITC', unit: 'pcs', purchase_price: 15.0, reorder_level: 4, supCode: 'SUP02' }, // Sports/Grounds { name: 'Grass Trimmer Mower', code: 'SPRT-01', barcode: '8901234567899', description: 'Petrol powered outdoor grass trimmer', catCode: 'SPRT', unit: 'pcs', purchase_price: 180.0, reorder_level: 1, supCode: 'SUP03' }, { name: 'Heavy Duty Wheelbarrow', code: 'SPRT-02', barcode: '8901234567900', description: 'Steel gardening wheelbarrow for soil/trash', catCode: 'SPRT', unit: 'pcs', purchase_price: 35.0, reorder_level: 2, supCode: 'SUP03' }, { name: 'Leather Soccer Matchball', code: 'SPRT-03', barcode: '8901234567901', description: 'Size 5 hand-stitched professional matchball', catCode: 'SPRT', unit: 'pcs', purchase_price: 25.0, reorder_level: 5, supCode: 'SUP03' } ]; for (const item of itemsToSeed) { const catId = getCatId(item.catCode); const supId = getSupId(item.supCode); // Insert item if it doesn't exist const existing = db.prepare('SELECT id FROM items WHERE code = ?').get(item.code); let itemId; if (!existing) { const res = db.prepare(` INSERT INTO items (uid, name, code, barcode, description, category_id, unit, purchase_price, reorder_level, supplier_id, sync_status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'pending') `).run(uuidv4(), item.name, item.code, item.barcode, item.description, catId, item.unit, item.purchase_price, item.reorder_level, supId || null); itemId = res.lastInsertRowid; console.log(`Inserted item: ${item.name} (${item.code})`); } else { itemId = existing.id; console.log(`Item exists: ${item.name} (${item.code})`); } // 5. Populate Stock Levels for each item in School Store or Warehouse const warehouseId = getLocId('WH01'); const storeId = getLocId('ST01'); if (warehouseId) { const existingStock = db.prepare('SELECT id FROM item_stock WHERE item_id = ? AND store_id = ? AND is_deleted = 0').get(itemId, warehouseId); if (existingStock) { db.prepare(` UPDATE item_stock SET quantity = ?, sync_status = 'pending', updated_at = CURRENT_TIMESTAMP WHERE id = ? `).run(250, existingStock.id); } else { db.prepare(` INSERT INTO item_stock (uid, item_id, store_id, quantity, sync_status) VALUES (?, ?, ?, ?, 'pending') `).run(uuidv4(), itemId, warehouseId, 250); } } if (storeId) { const existingStock = db.prepare('SELECT id FROM item_stock WHERE item_id = ? AND store_id = ? AND is_deleted = 0').get(itemId, storeId); if (existingStock) { db.prepare(` UPDATE item_stock SET quantity = ?, sync_status = 'pending', updated_at = CURRENT_TIMESTAMP WHERE id = ? `).run(50, existingStock.id); } else { db.prepare(` INSERT INTO item_stock (uid, item_id, store_id, quantity, sync_status) VALUES (?, ?, ?, ?, 'pending') `).run(uuidv4(), itemId, storeId, 50); } } } })(); // Invoked the transaction! console.log('✅ Inventory database seed completed successfully!'); } catch (e) { console.error('❌ Seeding failed:', e.message); } finally { db.close(); }