Changes in / [6149556:6fea37e]


Ignore:
Files:
578 deleted
6 edited

Legend:

Unmodified
Added
Removed
  • .env

    r6149556 r6fea37e  
    1 # PostgreSQL Connection via SSH Tunnel
    2 # SSH tunnel should be established to: 194.149.135.130
    3 # SSH username: t_handcraft_store
    4 # SSH password: 9f0985a6
    5 # After SSH tunnel, connect to localhost:5432
    6 
    7 PGHOST=localhost
    8 PGPORT=9999
    9 PGDATABASE=db_202526z_va_prj_handcraft_store
    10 PGUSER=db_202526z_va_prj_handcraft_store_owner
    11 PGPASSWORD=77769964489a
    12 
    131SMTP_HOST=smtp.ethereal.email
    14 SMTP_PORT=3000
     2SMTP_PORT=587
    153SMTP_USER=frederik52@ethereal.email
    164SMTP_PASS=sgqN9t4qn7RxCdavyv
    175JWT_SECRET=handcraft_marketplace_secret_key_2024
    186PORT=3000
     7DB_PATH=./handcraft.db
  • README.md

    r6149556 r6fea37e  
    33 * Open the project folder in Command line
    44 * Run npm install command to install all dependencies
    5  * Run npm start to lunch the cmdprogram
     5 * Run npm start to lunch the program
    66   * After running the command, app is available at locatlhost:3000
    77 * See the database with command node view-database.js
    … …  
    1111
    1212Klimentina Efremova
    13 
    14 =======
  • database.js

    r6149556 r6fea37e  
    1 const { Pool } = require('pg');
     1const sqlite3 = require('sqlite3').verbose();
     2const path = require('path');
    23const bcrypt = require('bcryptjs');
    3 require('dotenv').config();
    4 
    5 /*
    6  * ============================================================
    7  * PostgreSQL CONNECTION
    8  * ============================================================
    9  *
    10  * Put your actual FINKI connection information in .env
    11  *
    12  * Example:
    13  *
    14  * PGHOST=localhost
    15  * PGPORT=5432
    16  * PGDATABASE=handcraft
    17  * PGUSER=your_username
    18  * PGPASSWORD=your_password
    19  *
    20  * If you use the SSH tunnel, PGHOST/PGPORT will normally
    21  * point to the LOCAL end of the SSH tunnel.
    22  */
    23 
    24 const pool = new Pool({
    25     host: process.env.PGHOST || 'localhost',
    26     port: parseInt(process.env.PGPORT || '5432', 10),
    27     database: process.env.PGDATABASE || 'db_202526z_va_prj_handcraft_store',
    28     user: process.env.PGUSER || 'db_202526z_va_prj_handcraft_store_owner',
    29     password: process.env.PGPASSWORD || '',
    30     max: 10,
    31     idleTimeoutMillis: 30000,
    32     connectionTimeoutMillis: 10000
     4const crypto = require('crypto');
     5
     6const dbPath = path.join(__dirname, 'database', 'handcraft.db');
     7const dbDir = path.dirname(dbPath);
     8
     9// Create database directory if it doesn't exist
     10const fs = require('fs');
     11if (!fs.existsSync(dbDir)) {
     12    fs.mkdirSync(dbDir, { recursive: true });
     13}
     14
     15const database = new sqlite3.Database(dbPath, (err) => {
     16    if (err) {
     17        console.error('Error opening database:', err.message);
     18    } else {
     19        console.log('āœ… Connected to SQLite database');
     20    }
    3321});
    3422
    35 pool.on('connect', () => {
    36     console.log('āœ… Connected to PostgreSQL database');
    37 });
    38 
    39 pool.on('error', (err) => {
    40     console.error('āŒ Unexpected PostgreSQL pool error:', err);
    41 });
    42 
    43 /*
    44  * ============================================================
    45  * HELPER
    46  * ============================================================
    47  */
    48 
    49 function query(text, params = [], callback) {
    50     pool.query(text, params)
    51         .then(result => {
    52             callback(null, result);
    53         })
    54         .catch(err => {
     23// Enable foreign keys
     24database.run('PRAGMA foreign_keys = ON');
     25
     26// Helper function to ensure general category exists with ID 1
     27function ensureGeneralCategory(callback) {
     28    // First check if category with ID 1 exists and is named 'General'
     29    database.get('SELECT category_id, name FROM category WHERE category_id = 1', [], (err, row) => {
     30        if (err) {
     31            callback(err);
     32        } else if (row && row.name === 'General') {
     33            // Category with ID 1 already exists and is General
     34            console.log('āœ… General category exists with ID: 1');
     35            callback(null);
     36        } else if (row && row.name !== 'General') {
     37            // Category with ID 1 exists but has different name - update it
     38            database.run(
     39                'UPDATE category SET name = ?, description = ? WHERE category_id = 1',
     40                ['General', 'General products category'],
     41                function(err) {
     42                    if (err) {
     43                        callback(err);
     44                    } else {
     45                        console.log('āœ… Updated category ID 1 to General');
     46                        callback(null);
     47                    }
     48                }
     49            );
     50        } else {
     51            // No category with ID 1 exists, create it
     52            // First, check if we need to reset the autoincrement sequence
     53            database.run(
     54                'INSERT INTO category (category_id, name, description) VALUES (1, ?, ?)',
     55                ['General', 'General products category'],
     56                function(err) {
     57                    if (err) {
     58                        // If insert fails, try without specifying ID (let SQLite assign it)
     59                        database.run(
     60                            'INSERT INTO category (name, description) VALUES (?, ?)',
     61                            ['General', 'General products category'],
     62                            function(err) {
     63                                if (err) {
     64                                    callback(err);
     65                                } else {
     66                                    console.log('āœ… Created General category with auto-assigned ID');
     67                                    callback(null);
     68                                }
     69                            }
     70                        );
     71                    } else {
     72                        console.log('āœ… Created General category with ID: 1');
     73                        callback(null);
     74                    }
     75                }
     76            );
     77        }
     78    });
     79}
     80
     81// Helper function to get the General category ID
     82function getGeneralCategoryId(callback) {
     83    // First try to get category with ID 1 that is named 'General'
     84    database.get('SELECT category_id FROM category WHERE category_id = 1 AND name = ?', ['General'], (err, row) => {
     85        if (err) {
    5586            callback(err, null);
    56         });
    57 }
    58 
    59 
    60 /*
    61  * ============================================================
    62  * GENERAL CATEGORY
    63  * ============================================================
    64  */
    65 
    66 function ensureGeneralCategory(callback) {
    67 
    68     query(
    69         `SELECT category_id, name
    70          FROM category
    71          WHERE category_id = 1`,
    72         [],
    73         (err, result) => {
    74 
    75             if (err) {
    76                 callback(err);
    77                 return;
    78             }
    79 
    80             const row = result.rows[0];
    81 
    82             if (row && row.name === 'General') {
    83 
    84                 console.log('āœ… General category exists with ID: 1');
    85                 callback(null);
    86 
    87             } else if (row && row.name !== 'General') {
    88 
    89                 query(
    90                     `UPDATE category
    91                      SET name = $1,
    92                          description = $2
    93                      WHERE category_id = 1`,
    94                     ['General', 'General products category'],
    95                     (err) => {
    96 
    97                         if (err) {
    98                             callback(err);
    99                         } else {
    100                             console.log('āœ… Updated category ID 1 to General');
    101                             callback(null);
    102                         }
    103                     }
    104                 );
    105 
    106             } else {
    107 
    108                 query(
    109                     `INSERT INTO category
    110                         (category_id, name, description)
    111                      VALUES
    112                         (1, $1, $2)
    113                      ON CONFLICT (category_id) DO NOTHING`,
    114                     ['General', 'General products category'],
    115                     (err) => {
    116 
    117                         if (err) {
    118                             callback(err);
    119                         } else {
    120                             console.log('āœ… Created General category with ID: 1');
    121                             callback(null);
    122                         }
    123                     }
    124                 );
    125             }
    126         }
    127     );
    128 }
    129 
    130 
    131 function getGeneralCategoryId(callback) {
    132 
    133     query(
    134         `SELECT category_id
    135          FROM category
    136          WHERE category_id = 1
    137            AND name = $1`,
    138         ['General'],
    139         (err, result) => {
    140 
    141             if (err) {
    142                 callback(err, null);
    143                 return;
    144             }
    145 
    146             if (result.rows.length > 0) {
    147                 callback(null, result.rows[0].category_id);
    148                 return;
    149             }
    150 
    151             query(
    152                 `SELECT category_id
    153                  FROM category
    154                  WHERE name = $1`,
    155                 ['General'],
    156                 (err, result) => {
    157 
    158                     if (err) {
    159                         callback(err, null);
    160                         return;
    161                     }
    162 
    163                     if (result.rows.length > 0) {
    164                         callback(null, result.rows[0].category_id);
    165                         return;
    166                     }
    167 
    168                     query(
    169                         `INSERT INTO category
    170                             (name, description)
    171                          VALUES
    172                             ($1, $2)
    173                          RETURNING category_id`,
     87        } else if (row) {
     88            callback(null, row.category_id);
     89        } else {
     90            // If not found with ID 1, try to find by name
     91            database.get('SELECT category_id FROM category WHERE name = ?', ['General'], (err, row) => {
     92                if (err) {
     93                    callback(err, null);
     94                } else if (row) {
     95                    callback(null, row.category_id);
     96                } else {
     97                    // Create General category if it doesn't exist
     98                    database.run(
     99                        'INSERT INTO category (name, description) VALUES (?, ?)',
    174100                        ['General', 'General products category'],
    175                         (err, result) => {
    176 
     101                        function(err) {
    177102                            if (err) {
    178103                                callback(err, null);
    179104                            } else {
    180                                 const newId = result.rows[0].category_id;
    181 
    182                                 console.log(
    183                                     `āœ… Created new General category with ID: ${newId}`
    184                                 );
    185 
     105                                const newId = this.lastID;
     106                                console.log(`āœ… Created new General category with ID: ${newId}`);
    186107                                callback(null, newId);
    187108                            }
    … …  
    189110                    );
    190111                }
    191             );
    192         }
    193     );
    194 }
    195 
    196 
    197 /*
    198  * ============================================================
    199  * USER FUNCTIONS
    200  * ============================================================
    201  */
    202 
     112            });
     113        }
     114    });
     115}
     116
     117// User functions
    203118function getUserByUsername(username, callback) {
    204 
    205     query(
    206         `SELECT *
    207          FROM users
    208          WHERE username = $1`,
    209         [username],
    210         (err, result) => {
    211 
    212             callback(
    213                 err,
    214                 result ? result.rows[0] : null
    215             );
    216         }
    217     );
    218 }
    219 
     119    database.get('SELECT * FROM users WHERE username = ?', [username], (err, row) => {
     120        callback(err, row);
     121    });
     122}
    220123
    221124function getUserById(id, callback) {
    222 
    223     query(
    224         `SELECT *
    225          FROM users
    226          WHERE id = $1`,
    227         [id],
    228         (err, result) => {
    229 
    230             if (err || !result || result.rows.length === 0) {
    231                 callback(err, null);
    232                 return;
    233             }
    234 
    235             const row = result.rows[0];
    236 
    237             query(
    238                 `SELECT r.*
    239                  FROM roles r
    240                  JOIN user_roles ur
    241                    ON r.role_id = ur.role_id
    242                  WHERE ur.user_id = $1`,
    243                 [id],
    244                 (err, result) => {
    245 
    246                     if (err) {
    247                         callback(err, null);
    248                     } else {
    249                         row.roles = result.rows || [];
    250                         callback(null, row);
    251                     }
    252                 }
    253             );
    254         }
    255     );
    256 }
    257 
     125    database.get('SELECT * FROM users WHERE id = ?', [id], (err, row) => {
     126        if (err || !row) {
     127            callback(err, null);
     128            return;
     129        }
     130
     131        // Get user roles
     132        database.all(
     133            `SELECT r.* FROM roles r
     134       JOIN user_roles ur ON r.role_id = ur.role_id
     135       WHERE ur.user_id = ?`,
     136            [id],
     137            (err, roles) => {
     138                if (err) {
     139                    callback(err, null);
     140                } else {
     141                    row.roles = roles || [];
     142                    callback(null, row);
     143                }
     144            }
     145        );
     146    });
     147}
    258148
    259149function createUser(id, username, email, password, userType, callback) {
    260 
    261150    const hashedPassword = bcrypt.hashSync(password, 10);
    262 
    263     query(
    264         `INSERT INTO users
    265             (id, username, email, password, user_type)
    266          VALUES
    267             ($1, $2, $3, $4, $5)`,
     151    database.run(
     152        'INSERT INTO users (id, username, email, password, user_type) VALUES (?, ?, ?, ?, ?)',
    268153        [id, username, email, hashedPassword, userType],
    269         (err) => {
    270 
     154        function(err) {
    271155            if (err) {
    272156                callback(err, null);
    … …  
    278162}
    279163
    280 
    281 /*
    282  * ============================================================
    283  * CLIENT FUNCTIONS
    284  * ============================================================
    285  */
    286 
     164// Client functions
    287165function getClientByEmail(email, callback) {
    288 
    289     query(
    290         `SELECT *
    291          FROM client
    292          WHERE email = $1`,
    293         [email],
    294         (err, result) => {
    295 
    296             callback(
    297                 err,
    298                 result ? result.rows[0] : null
    299             );
    300         }
    301     );
    302 }
    303 
     166    database.get('SELECT * FROM client WHERE email = ?', [email], (err, row) => {
     167        callback(err, row);
     168    });
     169}
    304170
    305171function getClientById(id, callback) {
    306 
    307     query(
    308         `SELECT *
    309          FROM client
    310          WHERE client_id = $1`,
    311         [id],
    312         (err, result) => {
    313 
    314             callback(
    315                 err,
    316                 result ? result.rows[0] : null
    317             );
    318         }
    319     );
    320 }
    321 
     172    database.get('SELECT * FROM client WHERE client_id = ?', [id], (err, row) => {
     173        callback(err, row);
     174    });
     175}
    322176
    323177function createClient(clientData, callback) {
    324 
    325     const hashedPassword =
    326         bcrypt.hashSync(clientData.password, 10);
    327 
    328     query(
    329         `INSERT INTO client
    330             (first_name, last_name, email, password)
    331          VALUES
    332             ($1, $2, $3, $4)
    333          RETURNING client_id`,
    334         [
    335             clientData.first_name,
    336             clientData.last_name,
    337             clientData.email,
    338             hashedPassword
    339         ],
    340         (err, result) => {
    341 
     178    const hashedPassword = bcrypt.hashSync(clientData.password, 10);
     179    database.run(
     180        'INSERT INTO client (first_name, last_name, email, password) VALUES (?, ?, ?, ?)',
     181        [clientData.first_name, clientData.last_name, clientData.email, hashedPassword],
     182        function(err) {
    342183            if (err) {
    343184                callback(err, null);
    344185            } else {
    345                 callback(
    346                     null,
    347                     result.rows[0].client_id
    348                 );
    349             }
    350         }
    351     );
    352 }
    353 
    354 
    355 function verifyClientPassword(
    356     password,
    357     hashedPassword,
    358     callback
    359 ) {
    360 
     186                callback(null, this.lastID);
     187            }
     188        }
     189    );
     190}
     191
     192function verifyClientPassword(password, hashedPassword, callback) {
    361193    try {
    362 
    363         const isValid =
    364             bcrypt.compareSync(password, hashedPassword);
    365 
     194        const isValid = bcrypt.compareSync(password, hashedPassword);
    366195        callback(null, isValid);
    367 
    368196    } catch (err) {
    369 
    370197        callback(err, false);
    371198    }
    372199}
    373200
    374 
    375 /*
    376  * ============================================================
    377  * PERSONAL / EMPLOYEE FUNCTIONS
    378  * ============================================================
    379  */
    380 
     201// Personal functions
    381202function getPersonalByEmail(email, callback) {
    382 
    383     query(
    384         `SELECT *
    385          FROM personal
    386          WHERE email = $1`,
    387         [email],
    388         (err, result) => {
    389 
    390             callback(
    391                 err,
    392                 result ? result.rows[0] : null
    393             );
    394         }
    395     );
    396 }
    397 
     203    database.get('SELECT * FROM personal WHERE email = ?', [email], (err, row) => {
     204        callback(err, row);
     205    });
     206}
    398207
    399208function getPersonalById(id, callback) {
    400 
    401     query(
    402         `SELECT *
    403          FROM personal
    404          WHERE id = $1`,
    405         [id],
    406         (err, result) => {
    407 
    408             callback(
    409                 err,
    410                 result ? result.rows[0] : null
    411             );
    412         }
    413     );
    414 }
    415 
    416 
     209    database.get('SELECT * FROM personal WHERE id = ?', [id], (err, row) => {
     210        callback(err, row);
     211    });
     212}
     213
     214// Password verification for regular users
    417215function verifyPassword(password, hashedPassword) {
    418216    return bcrypt.compareSync(password, hashedPassword);
    419217}
    420218
    421 
    422 function updatePasswordAndClearForce(
    423     userId,
    424     newPassword,
    425     callback
    426 ) {
    427 
    428     const hashedPassword =
    429         bcrypt.hashSync(newPassword, 10);
    430 
    431     query(
    432         `UPDATE users
    433          SET password = $1,
    434              force_password_change = 0
    435          WHERE id = $2`,
     219// Password update
     220function updatePasswordAndClearForce(userId, newPassword, callback) {
     221    const hashedPassword = bcrypt.hashSync(newPassword, 10);
     222    database.run(
     223        'UPDATE users SET password = ?, force_password_change = 0 WHERE id = ?',
    436224        [hashedPassword, userId],
    437         (err) => {
    438 
     225        function(err) {
    439226            callback(err);
    440227        }
    … …  
    442229}
    443230
    444 
    445 /*
    446  * ============================================================
    447  * PRODUCT FUNCTIONS
    448  * ============================================================
    449  */
    450 
     231// Product functions
    451232function getProducts(categoryId, searchTerm, callback) {
    452 
    453     let queryText = `
    454         SELECT
    455             p.*,
    456             c.name AS category_name,
    457             s.name AS store_name
    458         FROM product p
    459         JOIN category c
    460           ON p.category_id = c.category_id
    461         JOIN store s
    462           ON p.store_id = s.store_id
    463         WHERE 1 = 1
    464     `;
    465 
     233    let query = `
     234    SELECT p.*, c.name as category_name, s.name as store_name
     235    FROM product p
     236    JOIN category c ON p.category_id = c.category_id
     237    JOIN store s ON p.store_id = s.store_id
     238    WHERE 1=1
     239  `;
    466240    const params = [];
    467     let paramIndex = 1;
    468241
    469242    if (categoryId && categoryId !== 'all') {
    470 
    471         queryText +=
    472             ` AND p.category_id = $${paramIndex}`;
    473 
     243        query += ' AND p.category_id = ?';
    474244        params.push(categoryId);
    475         paramIndex++;
    476245    }
    477246
    478247    if (searchTerm) {
    479 
    480         queryText +=
    481             ` AND (
    482                 p.description ILIKE $${paramIndex}
    483                 OR p.code ILIKE $${paramIndex + 1}
    484             )`;
    485 
    486         params.push(`%${searchTerm}%`);
    487         params.push(`%${searchTerm}%`);
    488 
    489         paramIndex += 2;
     248        query += ' AND (p.description LIKE ? OR p.code LIKE ?)';
     249        params.push(`%${searchTerm}%`, `%${searchTerm}%`);
    490250    }
    491251
    492     queryText += ` ORDER BY p.code`;
    493 
    494     query(
    495         queryText,
    496         params,
    497         (err, result) => {
    498 
    499             if (err) {
     252    database.all(query, params, (err, rows) => {
     253        if (err) {
     254            callback(err, null);
     255        } else {
     256            callback(null, rows || []);
     257        }
     258    });
     259}
     260
     261function getProductById(id, callback) {
     262    database.get(
     263        `SELECT p.*, c.name as category_name, s.name as store_name
     264     FROM product p
     265     JOIN category c ON p.category_id = c.category_id
     266     JOIN store s ON p.store_id = s.store_id
     267     WHERE p.id = ?`,
     268        [id],
     269        (err, row) => {
     270            if (err || !row) {
    500271                callback(err, null);
    501272            } else {
    502                 callback(null, result.rows || []);
    503             }
    504         }
    505     );
    506 }
    507 
    508 
    509 function getProductById(id, callback) {
    510 
    511     query(
    512         `SELECT
    513             p.*,
    514             c.name AS category_name,
    515             s.name AS store_name
    516          FROM product p
    517          JOIN category c
    518            ON p.category_id = c.category_id
    519          JOIN store s
    520            ON p.store_id = s.store_id
    521          WHERE p.id = $1`,
    522         [id],
    523         (err, result) => {
    524 
    525             if (err || result.rows.length === 0) {
     273                // Get images for product
     274                database.all(
     275                    'SELECT * FROM image WHERE product_code = ?',
     276                    [row.code],
     277                    (err, images) => {
     278                        if (err) {
     279                            callback(err, null);
     280                        } else {
     281                            row.images = images || [];
     282                            // Get colors for product
     283                            database.all(
     284                                'SELECT * FROM color WHERE product_code = ?',
     285                                [row.code],
     286                                (err, colors) => {
     287                                    if (err) {
     288                                        callback(err, null);
     289                                    } else {
     290                                        row.colors = colors || [];
     291                                        callback(null, row);
     292                                    }
     293                                }
     294                            );
     295                        }
     296                    }
     297                );
     298            }
     299        }
     300    );
     301}
     302
     303function getProductByCode(code, callback) {
     304    database.get(
     305        `SELECT p.*, c.name as category_name, s.name as store_name
     306     FROM product p
     307     JOIN category c ON p.category_id = c.category_id
     308     JOIN store s ON p.store_id = s.store_id
     309     WHERE p.code = ?`,
     310        [code],
     311        (err, row) => {
     312            if (err || !row) {
    526313                callback(err, null);
    527                 return;
    528             }
    529 
    530             const row = result.rows[0];
    531 
    532             query(
    533                 `SELECT *
    534                  FROM image
    535                  WHERE product_code = $1`,
    536                 [row.code],
    537                 (err, result) => {
    538 
    539                     if (err) {
    540                         callback(err, null);
    541                         return;
     314            } else {
     315                // Get images for product
     316                database.all(
     317                    'SELECT * FROM image WHERE product_code = ?',
     318                    [code],
     319                    (err, images) => {
     320                        if (err) {
     321                            callback(err, null);
     322                        } else {
     323                            row.images = images || [];
     324                            // Get colors for product
     325                            database.all(
     326                                'SELECT * FROM color WHERE product_code = ?',
     327                                [code],
     328                                (err, colors) => {
     329                                    if (err) {
     330                                        callback(err, null);
     331                                    } else {
     332                                        row.colors = colors || [];
     333                                        callback(null, row);
     334                                    }
     335                                }
     336                            );
     337                        }
    542338                    }
    543 
    544                     row.images = result.rows || [];
    545 
    546                     query(
    547                         `SELECT *
    548                          FROM color
    549                          WHERE product_code = $1`,
    550                         [row.code],
    551                         (err, result) => {
    552 
    553                             if (err) {
    554                                 callback(err, null);
    555                             } else {
    556                                 row.colors =
    557                                     result.rows || [];
    558 
    559                                 callback(null, row);
    560                             }
    561                         }
    562                     );
    563                 }
    564             );
    565         }
    566     );
    567 }
    568 
    569 
    570 function getProductByCode(code, callback) {
    571 
    572     query(
    573         `SELECT
    574             p.*,
    575             c.name AS category_name,
    576             s.name AS store_name
    577          FROM product p
    578          JOIN category c
    579            ON p.category_id = c.category_id
    580          JOIN store s
    581            ON p.store_id = s.store_id
    582          WHERE p.code = $1`,
    583         [code],
    584         (err, result) => {
    585 
    586             if (err || result.rows.length === 0) {
    587                 callback(err, null);
    588                 return;
    589             }
    590 
    591             const row = result.rows[0];
    592 
    593             query(
    594                 `SELECT *
    595                  FROM image
    596                  WHERE product_code = $1`,
    597                 [code],
    598                 (err, result) => {
    599 
    600                     if (err) {
    601                         callback(err, null);
    602                         return;
    603                     }
    604 
    605                     row.images = result.rows || [];
    606 
    607                     query(
    608                         `SELECT *
    609                          FROM color
    610                          WHERE product_code = $1`,
    611                         [code],
    612                         (err, result) => {
    613 
    614                             if (err) {
    615                                 callback(err, null);
    616                             } else {
    617 
    618                                 row.colors =
    619                                     result.rows || [];
    620 
    621                                 callback(null, row);
    622                             }
    623                         }
    624                     );
    625                 }
    626             );
    627         }
    628     );
    629 }
    630 
     339                );
     340            }
     341        }
     342    );
     343}
    631344
    632345function addProduct(personalId, productData, callback) {
    633 
    634346    getGeneralCategoryId((err, generalCategoryId) => {
    635 
    636347        if (err) {
    637348            callback(err, null);
    … …  
    639350        }
    640351
    641         const categoryId =
    642             productData.category_id || generalCategoryId;
    643 
     352        const categoryId = productData.category_id || generalCategoryId;
     353
     354        // FIXED: Added validation for required fields
    644355        if (!productData.code) {
    645             callback(
    646                 new Error('Product code is required'),
    647                 null
    648             );
     356            callback(new Error('Product code is required'), null);
    649357            return;
    650358        }
    651359
    652360        if (!productData.store_id) {
    653             callback(
    654                 new Error('Store ID is required'),
    655                 null
    656             );
     361            callback(new Error('Store ID is required'), null);
    657362            return;
    658363        }
    659364
    660         const productId =
    661             'PROD_' +
    662             Date.now().toString().slice(-8);
    663 
    664         query(
    665             `INSERT INTO product
    666                 (
    667                     id,
    668                     code,
    669                     description,
    670                     price,
    671                     availability,
    672                     weight,
    673                     dimensions,
    674                     production_time,
    675                     category_id,
    676                     store_id,
    677                     created_at
    678                 )
    679              VALUES
    680                 (
    681                     $1,
    682                     $2,
    683                     $3,
    684                     $4,
    685                     $5,
    686                     $6,
    687                     $7,
    688                     $8,
    689                     $9,
    690                     $10,
    691                     NOW()
    692                 )
    693              RETURNING id`,
     365        database.run(
     366            `INSERT INTO product (
     367        id, code, description, price, availability, weight, dimensions,
     368        production_time, category_id, store_id, created_at
     369      ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'))`,
    694370            [
    695                 productId,
     371                'PROD_' + Date.now().toString().slice(-8), // Generate a unique ID
    696372                productData.code,
    697373                productData.description || 'No description',
    … …  
    704380                productData.store_id
    705381            ],
    706             (err, result) => {
    707 
     382            function(err) {
    708383                if (err) {
    709384                    callback(err, null);
    710                     return;
    711                 }
    712 
    713                 const returnedId = result.rows[0].id;
    714 
    715                 /*
    716                  * Log product creation
    717                  */
    718                 query(
    719                     `INSERT INTO "change"
    720                         (date_and_time, product_code, changes)
    721                      VALUES
    722                         (NOW(), $1, $2)`,
    723                     [
    724                         productData.code,
    725                         'Product created'
    726                     ],
    727                     (err) => {
    728 
    729                         if (err) {
    730                             console.error(
    731                                 'Error logging product creation:',
    732                                 err
    733                             );
    734                         }
    735                     }
    736                 );
    737 
    738                 /*
    739                  * Log who made the change
    740                  */
    741                 query(
    742                     `INSERT INTO makes_change
    743                         (
    744                             personal_id,
    745                             change_date_time,
    746                             product_code
    747                         )
    748                      VALUES
    749                         ($1, NOW(), $2)`,
    750                     [
    751                         personalId,
    752                         productData.code
    753                     ],
    754                     (err) => {
    755 
    756                         if (err) {
    757                             console.error(
    758                                 'Error logging change maker:',
    759                                 err
    760                             );
    761                         }
    762                     }
    763                 );
    764 
    765                 /*
    766                  * Insert images
    767                  */
    768                 if (
    769                     productData.images &&
    770                     Array.isArray(productData.images) &&
    771                     productData.images.length > 0
    772                 ) {
    773 
    774                     productData.images.forEach(
    775                         (imageUrl, index) => {
    776 
    777                             query(
    778                                 `INSERT INTO image
    779                                     (
    780                                         product_code,
    781                                         image_url,
    782                                         is_primary
    783                                     )
    784                                  VALUES
    785                                     ($1, $2, $3)`,
    786                                 [
    787                                     productData.code,
    788                                     imageUrl,
    789                                     index === 0
    790                                 ],
    791                                 (err) => {
    792 
     385                } else {
     386                    const productId = this.lastID;
     387
     388                    // Log the change
     389                    database.run(
     390                        `INSERT INTO "change" (date_and_time, product_code, changes)
     391             VALUES (datetime('now'), ?, ?)`,
     392                        [productData.code, 'Product created'],
     393                        function(err) {
     394                            if (err) {
     395                                console.error('Error logging product creation:', err);
     396                            }
     397                        }
     398                    );
     399
     400                    // Log who made the change
     401                    database.run(
     402                        `INSERT INTO makes_change (personal_id, change_date_time, product_code)
     403             VALUES (?, datetime('now'), ?)`,
     404                        [personalId, productData.code],
     405                        function(err) {
     406                            if (err) {
     407                                console.error('Error logging change maker:', err);
     408                            }
     409                        }
     410                    );
     411
     412                    // Insert images if provided
     413                    if (productData.images && Array.isArray(productData.images) && productData.images.length > 0) {
     414                        productData.images.forEach((imageUrl, index) => {
     415                            database.run(
     416                                `INSERT INTO image (product_code, image_url, is_primary)
     417                 VALUES (?, ?, ?)`,
     418                                [productData.code, imageUrl, index === 0 ? 1 : 0],
     419                                function(err) {
    793420                                    if (err) {
    794                                         console.error(
    795                                             'Error inserting image:',
    796                                             err
    797                                         );
    798                                     }
    799                                 }
    800                             );
    801                         }
    802                     );
    803                 }
    804 
    805                 /*
    806                  * Insert colors
    807                  */
    808                 if (
    809                     productData.colors &&
    810                     Array.isArray(productData.colors) &&
    811                     productData.colors.length > 0
    812                 ) {
    813 
    814                     productData.colors.forEach(color => {
    815 
    816                         query(
    817                             `INSERT INTO color
    818                                 (product_code, name)
    819                              VALUES
    820                                 ($1, $2)`,
    821                             [
    822                                 productData.code,
    823                                 color
    824                             ],
    825                             (err) => {
    826 
    827                                 if (err) {
    828                                     console.error(
    829                                         'Error inserting color:',
    830                                         err
    831                                     );
    832                                 }
    833                             }
    834                         );
    835                     });
    836                 }
    837 
    838                 callback(null, returnedId);
    839             }
    840         );
    841     });
    842 }
    843 
    844 
    845 function updateProduct(
    846     personalId,
    847     productData,
    848     callback
    849 ) {
    850 
    851     const updates = [];
    852     const params = [];
    853 
    854     if (productData.description !== undefined) {
    855         updates.push(`description = $${params.length + 1}`);
    856         params.push(productData.description);
    857     }
    858 
    859     if (productData.price !== undefined) {
    860         updates.push(`price = $${params.length + 1}`);
    861         params.push(productData.price);
    862     }
    863 
    864     if (productData.availability !== undefined) {
    865         updates.push(`availability = $${params.length + 1}`);
    866         params.push(productData.availability);
    867     }
    868 
    869     if (productData.weight !== undefined) {
    870         updates.push(`weight = $${params.length + 1}`);
    871         params.push(productData.weight);
    872     }
    873 
    874     if (productData.dimensions !== undefined) {
    875         updates.push(`dimensions = $${params.length + 1}`);
    876         params.push(productData.dimensions);
    877     }
    878 
    879     if (productData.production_time !== undefined) {
    880         updates.push(`production_time = $${params.length + 1}`);
    881         params.push(productData.production_time);
    882     }
    883 
    884     if (productData.category_id !== undefined) {
    885         updates.push(`category_id = $${params.length + 1}`);
    886         params.push(productData.category_id);
    887     }
    888 
    889     if (updates.length === 0) {
    890         callback(null, 0);
    891         return;
    892     }
    893 
    894     params.push(productData.code);
    895 
    896     const codeParameter = params.length;
    897 
    898     query(
    899         `UPDATE product
    900          SET ${updates.join(', ')}
    901          WHERE code = $${codeParameter}`,
    902         params,
    903         (err, result) => {
    904 
    905             if (err) {
    906                 callback(err, null);
    907                 return;
    908             }
    909 
    910             const changesDesc =
    911                 `Product updated: ${updates.join(', ')}`;
    912 
    913             /*
    914              * Log product change
    915              */
    916             query(
    917                 `INSERT INTO "change"
    918                     (
    919                         date_and_time,
    920                         product_code,
    921                         changes
    922                     )
    923                  VALUES
    924                     (NOW(), $1, $2)`,
    925                 [
    926                     productData.code,
    927                     changesDesc
    928                 ],
    929                 (err) => {
    930 
    931                     if (err) {
    932                         console.error(
    933                             'Error logging product update:',
    934                             err
    935                         );
    936                     }
    937                 }
    938             );
    939 
    940             /*
    941              * Log who made the change
    942              */
    943             query(
    944                 `INSERT INTO makes_change
    945                     (
    946                         personal_id,
    947                         change_date_time,
    948                         product_code
    949                     )
    950                  VALUES
    951                     ($1, NOW(), $2)`,
    952                 [
    953                     personalId,
    954                     productData.code
    955                 ],
    956                 (err) => {
    957 
    958                     if (err) {
    959                         console.error(
    960                             'Error logging change maker:',
    961                             err
    962                         );
    963                     }
    964                 }
    965             );
    966 
    967             /*
    968              * Images
    969              */
    970             if (
    971                 productData.images &&
    972                 Array.isArray(productData.images)
    973             ) {
    974 
    975                 query(
    976                     `DELETE FROM image
    977                      WHERE product_code = $1`,
    978                     [productData.code],
    979                     (err) => {
    980 
    981                         if (err) {
    982                             console.error(
    983                                 'Error deleting old images:',
    984                                 err
    985                             );
    986                             return;
    987                         }
    988 
    989                         productData.images.forEach(
    990                             (image, index) => {
    991 
    992                                 query(
    993                                     `INSERT INTO image
    994                                         (
    995                                             product_code,
    996                                             image_url,
    997                                             is_primary
    998                                         )
    999                                      VALUES
    1000                                         ($1, $2, $3)`,
    1001                                     [
    1002                                         productData.code,
    1003                                         image,
    1004                                         index === 0
    1005                                     ],
    1006                                     (err) => {
    1007 
    1008                                         if (err) {
    1009                                             console.error(
    1010                                                 'Error inserting image:',
    1011                                                 err
    1012                                             );
    1013                                         }
    1014                                     }
    1015                                 );
    1016                             }
    1017                         );
    1018                     }
    1019                 );
    1020             }
    1021 
    1022             /*
    1023              * Colors
    1024              */
    1025             if (
    1026                 productData.colors &&
    1027                 Array.isArray(productData.colors)
    1028             ) {
    1029 
    1030                 query(
    1031                     `DELETE FROM color
    1032                      WHERE product_code = $1`,
    1033                     [productData.code],
    1034                     (err) => {
    1035 
    1036                         if (err) {
    1037                             console.error(
    1038                                 'Error deleting old colors:',
    1039                                 err
    1040                             );
    1041                             return;
    1042                         }
    1043 
    1044                         productData.colors.forEach(color => {
    1045 
    1046                             query(
    1047                                 `INSERT INTO color
    1048                                     (product_code, name)
    1049                                  VALUES
    1050                                     ($1, $2)`,
    1051                                 [
    1052                                     productData.code,
    1053                                     color
    1054                                 ],
    1055                                 (err) => {
    1056 
    1057                                     if (err) {
    1058                                         console.error(
    1059                                             'Error inserting color:',
    1060                                             err
    1061                                         );
     421                                        console.error('Error inserting image:', err);
    1062422                                    }
    1063423                                }
    … …  
    1065425                        });
    1066426                    }
    1067                 );
    1068             }
    1069 
    1070             callback(null, result.rowCount);
    1071         }
    1072     );
    1073 }
    1074 
    1075 
    1076 function deleteProduct(
    1077     productCode,
    1078     storeId,
    1079     personalId,
    1080     callback
    1081 ) {
    1082 
    1083     pool.connect()
    1084         .then(client => {
    1085 
    1086             return client.query('BEGIN')
    1087                 .then(() => {
    1088 
    1089                     return client.query(
    1090                         `INSERT INTO "change"
    1091                             (
    1092                                 date_and_time,
    1093                                 product_code,
    1094                                 changes
    1095                             )
    1096                          VALUES
    1097                             (NOW(), $1, $2)`,
    1098                         [
    1099                             productCode,
    1100                             'Product deleted'
    1101                         ]
    1102                     );
    1103                 })
    1104                 .then(() => {
    1105 
    1106                     return client.query(
    1107                         `INSERT INTO makes_change
    1108                             (
    1109                                 personal_id,
    1110                                 change_date_time,
    1111                                 product_code
    1112                             )
    1113                          VALUES
    1114                             ($1, NOW(), $2)`,
    1115                         [
    1116                             personalId,
    1117                             productCode
    1118                         ]
    1119                     );
    1120                 })
    1121                 .then(() => {
    1122 
    1123                     return client.query(
    1124                         `DELETE FROM product
    1125                          WHERE code = $1
    1126                            AND store_id = $2`,
    1127                         [
    1128                             productCode,
    1129                             storeId
    1130                         ]
    1131                     );
    1132                 })
    1133                 .then(result => {
    1134 
    1135                     return client.query('COMMIT')
    1136                         .then(() => {
    1137 
    1138                             client.release();
    1139 
    1140                             callback(
    1141                                 null,
    1142                                 result.rowCount
     427
     428                    // Insert colors if provided
     429                    if (productData.colors && Array.isArray(productData.colors) && productData.colors.length > 0) {
     430                        productData.colors.forEach(color => {
     431                            database.run(
     432                                `INSERT INTO color (product_code, name)
     433                                 VALUES (?, ?)`,
     434                                [productData.code, color],
     435                                function(err) {
     436                                    if (err) {
     437                                        console.error('Error inserting color:', err);
     438                                    }
     439                                }
    1143440                            );
    1144441                        });
    1145                 })
    1146                 .catch(err => {
    1147 
    1148                     return client.query('ROLLBACK')
    1149                         .catch(() => {})
    1150                         .then(() => {
    1151 
    1152                             client.release();
    1153                             callback(err);
    1154                         });
    1155                 });
    1156         })
    1157         .catch(err => {
    1158             callback(err);
    1159         });
    1160 }
    1161 
    1162 
    1163 /*
    1164  * ============================================================
    1165  * CATEGORY FUNCTIONS
    1166  * ============================================================
    1167  */
    1168 
    1169 function getCategories(callback) {
    1170 
    1171     query(
    1172         `SELECT *
    1173          FROM category
    1174          ORDER BY name`,
    1175         [],
    1176         (err, result) => {
    1177 
    1178             callback(
    1179                 err,
    1180                 result ? result.rows : []
    1181             );
    1182         }
    1183     );
    1184 }
    1185 
    1186 
    1187 function getCategoriesWithParents(callback) {
    1188 
    1189     query(
    1190         `SELECT
    1191             c1.*,
    1192             c2.name AS parent_name
    1193          FROM category c1
    1194          LEFT JOIN category c2
    1195            ON c1.parent_category_id =
    1196               c2.category_id
    1197          ORDER BY c1.name`,
    1198         [],
    1199         (err, result) => {
    1200 
    1201             callback(
    1202                 err,
    1203                 result ? result.rows : []
    1204             );
    1205         }
    1206     );
    1207 }
    1208 
    1209 
    1210 function createCategory(categoryData, callback) {
    1211 
    1212     query(
    1213         `INSERT INTO category
    1214             (
    1215                 name,
    1216                 description,
    1217                 parent_category_id
    1218             )
    1219          VALUES
    1220             ($1, $2, $3)
    1221          RETURNING category_id`,
    1222         [
    1223             categoryData.name,
    1224             categoryData.description || null,
    1225             categoryData.parent_id || null
    1226         ],
    1227         (err, result) => {
    1228 
     442                    }
     443
     444                    callback(null, productId);
     445                }
     446            }
     447        );
     448    });
     449}
     450
     451function updateProduct(personalId, productData, callback) {
     452    const updates = [];
     453    const params = [];
     454
     455    if (productData.description !== undefined) {
     456        updates.push('description = ?');
     457        params.push(productData.description);
     458    }
     459
     460    if (productData.price !== undefined) {
     461        updates.push('price = ?');
     462        params.push(productData.price);
     463    }
     464
     465    if (productData.availability !== undefined) {
     466        updates.push('availability = ?');
     467        params.push(productData.availability);
     468    }
     469
     470    if (productData.weight !== undefined) {
     471        updates.push('weight = ?');
     472        params.push(productData.weight);
     473    }
     474
     475    if (productData.dimensions !== undefined) {
     476        updates.push('dimensions = ?');
     477        params.push(productData.dimensions);
     478    }
     479
     480    if (productData.production_time !== undefined) {
     481        updates.push('production_time = ?');
     482        params.push(productData.production_time);
     483    }
     484
     485    if (productData.category_id !== undefined) {
     486        updates.push('category_id = ?');
     487        params.push(productData.category_id);
     488    }
     489
     490    if (updates.length === 0) {
     491        callback(null, 0);
     492        return;
     493    }
     494
     495    params.push(productData.code);
     496
     497    database.run(
     498        `UPDATE product SET ${updates.join(', ')} WHERE code = ?`,
     499        params,
     500        function(err) {
    1229501            if (err) {
    1230502                callback(err, null);
    1231503            } else {
    1232 
     504                // Log the change
     505                const changesDesc = `Product updated: ${updates.join(', ')}`;
     506                database.run(
     507                    `INSERT INTO "change" (date_and_time, product_code, changes)
     508           VALUES (datetime('now'), ?, ?)`,
     509                    [productData.code, changesDesc],
     510                    function(err) {
     511                        if (err) {
     512                            console.error('Error logging product update:', err);
     513                        }
     514                        // Log who made the change
     515                        database.run(
     516                            `INSERT INTO makes_change (personal_id, change_date_time, product_code)
     517               VALUES (?, datetime('now'), ?)`,
     518                            [personalId, productData.code],
     519                            function(err) {
     520                                if (err) {
     521                                    console.error('Error logging change maker:', err);
     522                                }
     523                            }
     524                        );
     525                    }
     526                );
     527
     528                // Handle images if provided
     529                if (productData.images && Array.isArray(productData.images)) {
     530                    // Delete old images first
     531                    database.run('DELETE FROM image WHERE product_code = ?', [productData.code], (err) => {
     532                        if (!err) {
     533                            // Insert new images
     534                            productData.images.forEach(image => {
     535                                database.run(
     536                                    'INSERT INTO image (product_code, image) VALUES (?, ?)',
     537                                    [productData.code, image]
     538                                );
     539                            });
     540                        }
     541                    });
     542                }
     543
     544                // Handle colors if provided
     545                if (productData.colors && Array.isArray(productData.colors)) {
     546                    // Delete old colors first
     547                    database.run('DELETE FROM color WHERE product_code = ?', [productData.code], (err) => {
     548                        if (!err) {
     549                            // Insert new colors
     550                            productData.colors.forEach(color => {
     551                                database.run(
     552                                    'INSERT INTO color (product_code, color) VALUES (?, ?)',
     553                                    [productData.code, color]
     554                                );
     555                            });
     556                        }
     557                    });
     558                }
     559
     560                callback(null, this.changes);
     561            }
     562        }
     563    );
     564}
     565
     566function deleteProduct(productCode, storeId, personalId, callback) {
     567    database.run('BEGIN TRANSACTION', (err) => {
     568        if (err) {
     569            callback(err);
     570            return;
     571        }
     572
     573        // Log the deletion
     574        database.run(
     575            `INSERT INTO "change" (date_and_time, product_code, changes)
     576       VALUES (datetime('now'), ?, ?)`,
     577            [productCode, 'Product deleted'],
     578            function(err) {
     579                if (err) {
     580                    database.run('ROLLBACK');
     581                    callback(err);
     582                    return;
     583                }
     584
     585                // Log who deleted it
     586                database.run(
     587                    `INSERT INTO makes_change (personal_id, change_date_time, product_code)
     588           VALUES (?, datetime('now'), ?)`,
     589                    [personalId, productCode],
     590                    function(err) {
     591                        if (err) {
     592                            database.run('ROLLBACK');
     593                            callback(err);
     594                            return;
     595                        }
     596
     597                        // Delete the product (cascades to image, color)
     598                        database.run(
     599                            'DELETE FROM product WHERE code = ? AND store_id = ?',
     600                            [productCode, storeId],
     601                            function(err) {
     602                                if (err) {
     603                                    database.run('ROLLBACK');
     604                                    callback(err);
     605                                } else {
     606                                    database.run('COMMIT', callback);
     607                                }
     608                            }
     609                        );
     610                    }
     611                );
     612            }
     613        );
     614    });
     615}
     616
     617// Category functions
     618function getCategories(callback) {
     619    database.all('SELECT * FROM category ORDER BY name', [], (err, rows) => {
     620        callback(err, rows || []);
     621    });
     622}
     623
     624function getCategoriesWithParents(callback) {
     625    database.all(
     626        `SELECT c1.*, c2.name as parent_name
     627     FROM category c1
     628     LEFT JOIN category c2 ON c1.parent_category_id = c2.category_id
     629     ORDER BY c1.name`,
     630        [],
     631        (err, rows) => {
     632            callback(err, rows || []);
     633        }
     634    );
     635}
     636
     637function createCategory(categoryData, callback) {
     638    database.run(
     639        'INSERT INTO category (name, description, parent_category_id) VALUES (?, ?, ?)',
     640        [categoryData.name, categoryData.description || null, categoryData.parent_id || null],
     641        function(err) {
     642            if (err) {
     643                callback(err, null);
     644            } else {
    1233645                callback(null, {
    1234                     id: result.rows[0].category_id,
     646                    id: this.lastID,
    1235647                    name: categoryData.name,
    1236648                    parent_id: categoryData.parent_id,
    … …  
    1242654}
    1243655
    1244 
    1245 /*
    1246  * ============================================================
    1247  * STORE FUNCTIONS
    1248  * ============================================================
    1249  */
    1250 
     656// Store functions
    1251657function getStores(callback) {
    1252 
    1253     query(
    1254         `SELECT *
    1255          FROM store
    1256          ORDER BY name`,
    1257         [],
    1258         (err, result) => {
    1259 
    1260             callback(
    1261                 err,
    1262                 result ? result.rows : []
    1263             );
    1264         }
    1265     );
    1266 }
    1267 
     658    database.all('SELECT * FROM store ORDER BY name', [], (err, rows) => {
     659        callback(err, rows || []);
     660    });
     661}
    1268662
    1269663function getStoreProducts(storeId, callback) {
    1270 
    1271     query(
    1272         `SELECT
    1273             p.*,
    1274             c.name AS category_name
    1275          FROM product p
    1276          JOIN category c
    1277            ON p.category_id = c.category_id
    1278          WHERE p.store_id = $1
    1279          ORDER BY p.code`,
     664    database.all(
     665        `SELECT p.*, c.name as category_name
     666     FROM product p
     667     JOIN category c ON p.category_id = c.category_id
     668     WHERE p.store_id = ?
     669     ORDER BY p.code`,
    1280670        [storeId],
    1281         (err, result) => {
    1282 
    1283             callback(
    1284                 err,
    1285                 result ? result.rows : []
    1286             );
    1287         }
    1288     );
    1289 }
    1290 
    1291 
    1292 function getStoreOrders(storeId, callback) {
    1293 
    1294     query(
    1295         `SELECT
    1296             o.*,
    1297             c.first_name,
    1298             c.last_name
    1299          FROM "order" o
    1300          JOIN client c
    1301            ON o.client_id = c.client_id
    1302          WHERE o.store_id = $1
    1303          ORDER BY o.order_date DESC`,
    1304         [storeId],
    1305         (err, result) => {
    1306 
     671        (err, rows) => {
    1307672            if (err) {
    1308673                callback(err, null);
    1309                 return;
    1310             }
    1311 
    1312             const orders = result.rows || [];
    1313 
    1314             if (orders.length === 0) {
    1315                 callback(null, []);
    1316                 return;
    1317             }
    1318 
    1319             let completed = 0;
    1320 
    1321             orders.forEach(order => {
    1322 
    1323                 query(
    1324                     `SELECT
    1325                         oi.*,
    1326                         p.description
    1327                      FROM order_items oi
    1328                      JOIN product p
    1329                        ON oi.product_code = p.code
    1330                      WHERE oi.order_num = $1`,
    1331                     [order.order_num],
    1332                     (err, result) => {
    1333 
    1334                         if (!err) {
    1335                             order.items =
    1336                                 result.rows || [];
    1337                         } else {
    1338                             order.items = [];
    1339                         }
    1340 
    1341                         completed++;
    1342 
    1343                         if (completed === orders.length) {
    1344                             callback(null, orders);
    1345                         }
    1346                     }
    1347                 );
    1348             });
    1349         }
    1350     );
    1351 }
    1352 
    1353 
    1354 function getStoreEmployees(storeId, callback) {
    1355 
    1356     query(
    1357         `SELECT
    1358             p.*,
    1359             e.date_of_hire,
    1360             perm.type AS permission_type,
    1361             perm.authorisation
    1362          FROM personal p
    1363          JOIN works_in_store w
    1364            ON p.id = w.personal_id
    1365          LEFT JOIN employees e
    1366            ON p.id = e.employee_id
    1367          LEFT JOIN permissions perm
    1368            ON p.id = perm.personal_id
    1369          WHERE w.store_id = $1`,
     674            } else {
     675                callback(null, rows || []);
     676            }
     677        }
     678    );
     679}
     680
     681function getStoreOrders(storeId, callback) {
     682    database.all(
     683        `SELECT o.*, c.first_name, c.last_name
     684     FROM "order" o
     685     JOIN client c ON o.client_id = c.client_id
     686     WHERE o.store_id = ?
     687     ORDER BY o.order_date DESC`,
    1370688        [storeId],
    1371         (err, result) => {
    1372 
    1373             callback(
    1374                 err,
    1375                 result ? result.rows : []
    1376             );
    1377         }
    1378     );
    1379 }
    1380 
    1381 
    1382 function getStoreReports(storeId, callback) {
    1383 
    1384     query(
    1385         `SELECT *
    1386          FROM report
    1387          WHERE store_id = $1
    1388          ORDER BY generated_at DESC`,
    1389         [storeId],
    1390         (err, result) => {
    1391 
    1392             callback(
    1393                 err,
    1394                 result ? result.rows : []
    1395             );
    1396         }
    1397     );
    1398 }
    1399 
    1400 
    1401 function getStoreStats(storeId, callback) {
    1402 
    1403     const stats = {};
    1404 
    1405     query(
    1406         `SELECT COUNT(*) AS total_products
    1407          FROM product
    1408          WHERE store_id = $1`,
    1409         [storeId],
    1410         (err, result) => {
    1411 
     689        (err, rows) => {
    1412690            if (err) {
    1413691                callback(err, null);
    1414                 return;
    1415             }
    1416 
    1417             stats.total_products =
    1418                 result.rows[0]
    1419                     ? Number(result.rows[0].total_products)
    1420                     : 0;
    1421 
    1422             query(
    1423                 `SELECT COUNT(*) AS total_orders
    1424                  FROM "order"
    1425                  WHERE store_id = $1`,
     692            } else {
     693                // Get order items for each order
     694                let completed = 0;
     695                const orders = rows || [];
     696
     697                if (orders.length === 0) {
     698                    callback(null, []);
     699                    return;
     700                }
     701
     702                orders.forEach(order => {
     703                    database.all(
     704                        `SELECT oi.*, p.description
     705             FROM order_items oi
     706             JOIN product p ON oi.product_code = p.code
     707             WHERE oi.order_num = ?`,
     708                        [order.order_num],
     709                        (err, items) => {
     710                            if (!err) {
     711                                order.items = items || [];
     712                            } else {
     713                                order.items = [];
     714                            }
     715                            completed++;
     716                            if (completed === orders.length) {
     717                                callback(null, orders);
     718                            }
     719                        }
     720                    );
     721                });
     722            }
     723        }
     724    );
     725}
     726
     727function getStoreEmployees(storeId, callback) {
     728    database.all(
     729        `SELECT p.*, e.date_of_hire, perm.type as permission_type, perm.authorisation
     730     FROM personal p
     731     JOIN works_in_store w ON p.id = w.personal_id
     732     LEFT JOIN employees e ON p.id = e.employee_id
     733     LEFT JOIN permissions perm ON p.id = perm.personal_id
     734     WHERE w.store_id = ?`,
     735        [storeId],
     736        (err, rows) => {
     737            callback(err, rows || []);
     738        }
     739    );
     740}
     741
     742function getStoreReports(storeId, callback) {
     743    database.all(
     744        `SELECT * FROM report
     745     WHERE store_id = ?
     746     ORDER BY generated_at DESC`,
     747        [storeId],
     748        (err, rows) => {
     749            callback(err, rows || []);
     750        }
     751    );
     752}
     753
     754function getStoreStats(storeId, callback) {
     755    const stats = {};
     756
     757    // Get total products
     758    database.get(
     759        'SELECT COUNT(*) as total_products FROM product WHERE store_id = ?',
     760        [storeId],
     761        (err, row) => {
     762            stats.total_products = row ? row.total_products : 0;
     763
     764            // Get total orders
     765            database.get(
     766                'SELECT COUNT(*) as total_orders FROM "order" WHERE store_id = ?',
    1426767                [storeId],
    1427                 (err, result) => {
    1428 
    1429                     if (err) {
    1430                         callback(err, null);
    1431                         return;
    1432                     }
    1433 
    1434                     stats.total_orders =
    1435                         result.rows[0]
    1436                             ? Number(result.rows[0].total_orders)
    1437                             : 0;
    1438 
    1439                     query(
    1440                         `SELECT
    1441                             COALESCE(
    1442                                 SUM(
    1443                                     oi.price * oi.quantity
    1444                                 ),
    1445                                 0
    1446                             ) AS total_revenue
    1447                          FROM order_items oi
    1448                          JOIN "order" o
    1449                            ON oi.order_num =
    1450                               o.order_num
    1451                          WHERE o.store_id = $1`,
     768                (err, row) => {
     769                    stats.total_orders = row ? row.total_orders : 0;
     770
     771                    // Get total revenue
     772                    database.get(
     773                        `SELECT SUM(oi.price * oi.quantity) as total_revenue
     774             FROM order_items oi
     775             JOIN "order" o ON oi.order_num = o.order_num
     776             WHERE o.store_id = ?`,
    1452777                        [storeId],
    1453                         (err, result) => {
    1454 
     778                        (err, row) => {
     779                            stats.total_revenue = row && row.total_revenue ? row.total_revenue : 0;
     780
     781                            // Get average rating
     782                            database.get(
     783                                `SELECT AVG(rating) as avg_rating
     784                 FROM review r
     785                 JOIN product p ON r.product_code = p.code
     786                 WHERE p.store_id = ?`,
     787                                [storeId],
     788                                (err, row) => {
     789                                    stats.avg_rating = row && row.avg_rating ? row.avg_rating : 0;
     790                                    callback(null, stats);
     791                                }
     792                            );
     793                        }
     794                    );
     795                }
     796            );
     797        }
     798    );
     799}
     800
     801// Order functions
     802function createOrderNew(orderData, callback) {
     803    database.run('BEGIN TRANSACTION', (err) => {
     804        if (err) {
     805            callback(err, null);
     806            return;
     807        }
     808
     809        database.run(
     810            `INSERT INTO "order" (order_num, client_id, order_date, quantity, payment_method,
     811        discount, delivery_address, store_id)
     812       VALUES (?, ?, datetime('now'), ?, ?, ?, ?, ?)`,
     813            [
     814                orderData.order_num,
     815                orderData.client_id,
     816                orderData.quantity,
     817                orderData.payment_method,
     818                orderData.discount,
     819                orderData.delivery_address,
     820                orderData.store_id
     821            ],
     822            function(err) {
     823                if (err) {
     824                    database.run('ROLLBACK');
     825                    callback(err, null);
     826                    return;
     827                }
     828
     829                let itemsInserted = 0;
     830                const items = orderData.items || [];
     831
     832                if (items.length === 0) {
     833                    database.run('COMMIT');
     834                    callback(null, orderData.order_num);
     835                    return;
     836                }
     837
     838                items.forEach(item => {
     839                    database.run(
     840                        `INSERT INTO order_items (order_num, product_code, quantity, price)
     841             VALUES (?, ?, ?, ?)`,
     842                        [orderData.order_num, item.product_code, item.quantity, item.price],
     843                        function(err) {
    1455844                            if (err) {
     845                                database.run('ROLLBACK');
    1456846                                callback(err, null);
    1457847                                return;
    1458848                            }
    1459849
    1460                             stats.total_revenue =
    1461                                 Number(
    1462                                     result.rows[0]
    1463                                         .total_revenue || 0
    1464                                 );
    1465 
    1466                             query(
    1467                                 `SELECT
    1468                                     COALESCE(
    1469                                         AVG(r.rating),
    1470                                         0
    1471                                     ) AS avg_rating
    1472                                  FROM review r
    1473                                  JOIN product p
    1474                                    ON r.product_code =
    1475                                       p.code
    1476                                  WHERE p.store_id = $1`,
    1477                                 [storeId],
    1478                                 (err, result) => {
    1479 
     850                            itemsInserted++;
     851                            if (itemsInserted === items.length) {
     852                                database.run('COMMIT', (err) => {
    1480853                                    if (err) {
    1481854                                        callback(err, null);
    1482                                         return;
     855                                    } else {
     856                                        callback(null, orderData.order_num);
    1483857                                    }
    1484 
    1485                                     stats.avg_rating =
    1486                                         Number(
    1487                                             result.rows[0]
    1488                                                 .avg_rating || 0
    1489                                         );
    1490 
    1491                                     callback(
    1492                                         null,
    1493                                         stats
    1494                                     );
    1495                                 }
    1496                             );
     858                                });
     859                            }
    1497860                        }
    1498861                    );
    1499                 }
    1500             );
    1501         }
    1502     );
    1503 }
    1504 
    1505 
    1506 /*
    1507  * ============================================================
    1508  * ORDER FUNCTIONS
    1509  * ============================================================
    1510  */
    1511 
    1512 function createOrderNew(orderData, callback) {
    1513 
    1514     pool.connect()
    1515         .then(client => {
    1516 
    1517             return client.query('BEGIN')
    1518                 .then(() => {
    1519 
    1520                     return client.query(
    1521                         `INSERT INTO "order"
    1522                             (
    1523                                 order_num,
    1524                                 client_id,
    1525                                 order_date,
    1526                                 quantity,
    1527                                 payment_method,
    1528                                 discount,
    1529                                 delivery_address,
    1530                                 store_id
    1531                             )
    1532                          VALUES
    1533                             (
    1534                                 $1,
    1535                                 $2,
    1536                                 NOW(),
    1537                                 $3,
    1538                                 $4,
    1539                                 $5,
    1540                                 $6,
    1541                                 $7
    1542                             )`,
    1543                         [
    1544                             orderData.order_num,
    1545                             orderData.client_id,
    1546                             orderData.quantity,
    1547                             orderData.payment_method,
    1548                             orderData.discount,
    1549                             orderData.delivery_address,
    1550                             orderData.store_id
    1551                         ]
    1552                     );
    1553                 })
    1554                 .then(() => {
    1555 
    1556                     const items =
    1557                         orderData.items || [];
    1558 
    1559                     if (items.length === 0) {
    1560                         return client.query('COMMIT')
    1561                             .then(() => {
    1562 
    1563                                 client.release();
    1564 
    1565                                 callback(
    1566                                     null,
    1567                                     orderData.order_num
    1568                                 );
    1569                             });
    1570                     }
    1571 
    1572                     return Promise.all(
    1573                         items.map(item => {
    1574 
    1575                             return client.query(
    1576                                 `INSERT INTO order_items
    1577                                     (
    1578                                         order_num,
    1579                                         product_code,
    1580                                         quantity,
    1581                                         price
    1582                                     )
    1583                                  VALUES
    1584                                     ($1, $2, $3, $4)`,
    1585                                 [
    1586                                     orderData.order_num,
    1587                                     item.product_code,
    1588                                     item.quantity,
    1589                                     item.price
    1590                                 ]
    1591                             );
    1592                         })
    1593                     )
    1594                         .then(() => client.query('COMMIT'))
    1595                         .then(() => {
    1596 
    1597                             client.release();
    1598 
    1599                             callback(
    1600                                 null,
    1601                                 orderData.order_num
    1602                             );
    1603                         });
    1604                 })
    1605                 .catch(err => {
    1606 
    1607                     return client.query('ROLLBACK')
    1608                         .catch(() => {})
    1609                         .then(() => {
    1610 
    1611                             client.release();
    1612                             callback(err, null);
    1613                         });
    1614862                });
    1615         })
    1616         .catch(err => {
    1617             callback(err, null);
    1618         });
    1619 }
    1620 
     863            }
     864        );
     865    });
     866}
    1621867
    1622868function getOrdersByClient(clientId, callback) {
    1623 
    1624     query(
    1625         `SELECT
    1626             o.*,
    1627             s.name AS store_name
    1628          FROM "order" o
    1629          JOIN store s
    1630            ON o.store_id = s.store_id
    1631          WHERE o.client_id = $1
    1632          ORDER BY o.order_date DESC`,
     869    database.all(
     870        `SELECT o.*, s.name as store_name
     871     FROM "order" o
     872     JOIN store s ON o.store_id = s.store_id
     873     WHERE o.client_id = ?
     874     ORDER BY o.order_date DESC`,
    1633875        [clientId],
    1634         (err, result) => {
    1635 
     876        (err, rows) => {
    1636877            if (err) {
    1637878                callback(err, null);
    1638                 return;
    1639             }
    1640 
    1641             const orders = result.rows || [];
    1642 
    1643             if (orders.length === 0) {
    1644                 callback(null, []);
    1645                 return;
    1646             }
    1647 
    1648             let completed = 0;
    1649 
    1650             orders.forEach(order => {
    1651 
    1652                 query(
    1653                     `SELECT
    1654                         oi.*,
    1655                         p.description
    1656                      FROM order_items oi
    1657                      JOIN product p
    1658                        ON oi.product_code = p.code
    1659                      WHERE oi.order_num = $1`,
    1660                     [order.order_num],
    1661                     (err, result) => {
    1662 
    1663                         if (!err) {
    1664                             order.items =
    1665                                 result.rows || [];
    1666                         } else {
    1667                             order.items = [];
    1668                         }
    1669 
    1670                         completed++;
    1671 
    1672                         if (completed === orders.length) {
    1673                             callback(null, orders);
    1674                         }
    1675                     }
    1676                 );
    1677             });
    1678         }
    1679     );
    1680 }
    1681 
     879            } else {
     880                // Get order items for each order
     881                let completed = 0;
     882                const orders = rows || [];
     883
     884                if (orders.length === 0) {
     885                    callback(null, []);
     886                    return;
     887                }
     888
     889                orders.forEach(order => {
     890                    database.all(
     891                        `SELECT oi.*, p.description
     892             FROM order_items oi
     893             JOIN product p ON oi.product_code = p.code
     894             WHERE oi.order_num = ?`,
     895                        [order.order_num],
     896                        (err, items) => {
     897                            if (!err) {
     898                                order.items = items || [];
     899                            } else {
     900                                order.items = [];
     901                            }
     902                            completed++;
     903                            if (completed === orders.length) {
     904                                callback(null, orders);
     905                            }
     906                        }
     907                    );
     908                });
     909            }
     910        }
     911    );
     912}
    1682913
    1683914function getAllOrders(callback) {
    1684 
    1685     query(
    1686         `SELECT
    1687             o.*,
    1688             c.first_name,
    1689             c.last_name,
    1690             s.name AS store_name
    1691          FROM "order" o
    1692          JOIN client c
    1693            ON o.client_id = c.client_id
    1694          JOIN store s
    1695            ON o.store_id = s.store_id
    1696          ORDER BY o.order_date DESC`,
     915    database.all(
     916        `SELECT o.*, c.first_name, c.last_name, s.name as store_name
     917     FROM "order" o
     918     JOIN client c ON o.client_id = c.client_id
     919     JOIN store s ON o.store_id = s.store_id
     920     ORDER BY o.order_date DESC`,
    1697921        [],
    1698         (err, result) => {
    1699 
    1700             callback(
    1701                 err,
    1702                 result ? result.rows : []
    1703             );
    1704         }
    1705     );
    1706 }
    1707 
    1708 
    1709 /*
    1710  * ============================================================
    1711  * REVIEW FUNCTIONS
    1712  * ============================================================
    1713  */
    1714 
     922        (err, rows) => {
     923            callback(err, rows || []);
     924        }
     925    );
     926}
     927
     928// Review functions
    1715929function createReviewNew(reviewData, callback) {
    1716 
    1717     const reviewId =
    1718         'REV' +
    1719         Date.now().toString().slice(-8);
    1720 
    1721     query(
    1722         `INSERT INTO review
    1723             (
    1724                 review_id,
    1725                 client_id,
    1726                 product_code,
    1727                 rating,
    1728                 comment,
    1729                 review_date
    1730             )
    1731          VALUES
    1732             ($1, $2, $3, $4, $5, NOW())
    1733          RETURNING review_id`,
     930    database.run(
     931        `INSERT INTO review (review_id, client_id, product_code, rating, comment, review_date)
     932     VALUES (?, ?, ?, ?, ?, datetime('now'))`,
    1734933        [
    1735             reviewId,
     934            'REV' + Date.now().toString().slice(-8),
    1736935            reviewData.client_id,
    1737936            reviewData.product_code,
    … …  
    1739938            reviewData.comment || ''
    1740939        ],
    1741         (err, result) => {
    1742 
     940        function(err) {
    1743941            if (err) {
    1744942                callback(err, null);
    1745943            } else {
    1746                 callback(
    1747                     null,
    1748                     result.rows[0].review_id
    1749                 );
    1750             }
    1751         }
    1752     );
    1753 }
    1754 
    1755 
    1756 /*
    1757  * ============================================================
    1758  * REQUEST FUNCTIONS
    1759  * ============================================================
    1760  */
    1761 
     944                callback(null, this.lastID);
     945            }
     946        }
     947    );
     948}
     949
     950// Request functions
    1762951function createRequest(requestData, callback) {
    1763 
    1764     query(
    1765         `INSERT INTO request
    1766             (
    1767                 request_num,
    1768                 date_and_time,
    1769                 problem,
    1770                 client_id,
    1771                 store_id
    1772             )
    1773          VALUES
    1774             ($1, $2, $3, $4, $5)`,
     952    database.run(
     953        `INSERT INTO request (request_num, date_and_time, problem, client_id, store_id)
     954     VALUES (?, ?, ?, ?, ?)`,
    1775955        [
    1776956            requestData.request_num,
    … …  
    1780960            requestData.store_id
    1781961        ],
    1782         (err) => {
    1783 
     962        function(err) {
    1784963            if (err) {
    1785964                callback(err, null);
    1786965            } else {
    1787                 callback(
    1788                     null,
    1789                     requestData.request_num
    1790                 );
    1791             }
    1792         }
    1793     );
    1794 }
    1795 
    1796 
    1797 /*
    1798  * ============================================================
    1799  * REFUND FUNCTIONS
    1800  * ============================================================
    1801  */
    1802 
     966                callback(null, requestData.request_num);
     967            }
     968        }
     969    );
     970}
     971
     972// Refund functions
    1803973function createRefund(refundData, callback) {
    1804 
    1805     query(
    1806         `INSERT INTO refund
    1807             (
    1808                 refund_id,
    1809                 order_num,
    1810                 amount,
    1811                 reason,
    1812                 request_date
    1813             )
    1814          VALUES
    1815             ($1, $2, $3, $4, NOW())`,
     974    database.run(
     975        `INSERT INTO refund (refund_id, order_num, amount, reason, request_date)
     976     VALUES (?, ?, ?, ?, datetime('now'))`,
    1816977        [
    1817978            refundData.refund_id,
    … …  
    1820981            refundData.reason
    1821982        ],
    1822         (err) => {
    1823 
     983        function(err) {
    1824984            if (err) {
    1825985                callback(err, null);
    1826986            } else {
    1827                 callback(
    1828                     null,
    1829                     refundData.refund_id
    1830                 );
    1831             }
    1832         }
    1833     );
    1834 }
    1835 
    1836 
    1837 /*
    1838  * ============================================================
    1839  * EMPLOYEE TASKS
    1840  * ============================================================
    1841  */
    1842 
    1843 function getEmployeeTasks(
    1844     personalId,
    1845     storeId,
    1846     callback
    1847 ) {
    1848 
     987                callback(null, refundData.refund_id);
     988            }
     989        }
     990    );
     991}
     992
     993// Employee task functions
     994function getEmployeeTasks(personalId, storeId, callback) {
    1849995    const tasks = {
    1850996        pending_orders: [],
    … …  
    1853999    };
    18541000
    1855     /*
    1856      * Pending orders
    1857      */
    1858     query(
    1859         `SELECT
    1860             o.*,
    1861             c.first_name,
    1862             c.last_name
    1863          FROM "order" o
    1864          JOIN client c
    1865            ON o.client_id = c.client_id
    1866          WHERE o.store_id = $1
    1867            AND o.status = $2
    1868          ORDER BY o.order_date ASC`,
    1869         [
    1870             storeId,
    1871             'pending'
    1872         ],
    1873         (err, result) => {
    1874 
     1001    // Get pending orders
     1002    database.all(
     1003        `SELECT o.*, c.first_name, c.last_name
     1004     FROM "order" o
     1005     JOIN client c ON o.client_id = c.client_id
     1006     WHERE o.store_id = ? AND o.status = 'pending'
     1007     ORDER BY o.order_date ASC`,
     1008        [storeId],
     1009        (err, rows) => {
    18751010            if (!err) {
    1876                 tasks.pending_orders =
    1877                     result.rows || [];
    1878             }
    1879 
    1880             /*
    1881              * Pending requests
    1882              */
    1883             query(
    1884                 `SELECT
    1885                     r.*,
    1886                     c.first_name,
    1887                     c.last_name
    1888                  FROM request r
    1889                  JOIN client c
    1890                    ON r.client_id = c.client_id
    1891                  WHERE r.store_id = $1
    1892                    AND r.status = $2
    1893                  ORDER BY r.date_and_time ASC`,
    1894                 [
    1895                     storeId,
    1896                     'pending'
    1897                 ],
    1898                 (err, result) => {
    1899 
     1011                tasks.pending_orders = rows || [];
     1012            }
     1013
     1014            // Get pending requests
     1015            database.all(
     1016                `SELECT r.*, c.first_name, c.last_name
     1017         FROM request r
     1018         JOIN client c ON r.client_id = c.client_id
     1019         WHERE r.store_id = ? AND r.status = 'pending'
     1020         ORDER BY r.date_and_time ASC`,
     1021                [storeId],
     1022                (err, rows) => {
    19001023                    if (!err) {
    1901                         tasks.pending_requests =
    1902                             result.rows || [];
     1024                        tasks.pending_requests = rows || [];
    19031025                    }
    19041026
    1905                     /*
    1906                      * Pending refunds
    1907                      */
    1908                     query(
    1909                         `SELECT
    1910                             rf.*,
    1911                             o.client_id,
    1912                             c.first_name,
    1913                             c.last_name
    1914                          FROM refund rf
    1915                          JOIN "order" o
    1916                            ON rf.order_num =
    1917                               o.order_num
    1918                          JOIN client c
    1919                            ON o.client_id =
    1920                               c.client_id
    1921                          WHERE o.store_id = $1
    1922                            AND rf.status = $2
    1923                          ORDER BY rf.request_date ASC`,
    1924                         [
    1925                             storeId,
    1926                             'pending'
    1927                         ],
    1928                         (err, result) => {
    1929 
     1027                    // Get pending refunds
     1028                    database.all(
     1029                        `SELECT rf.*, o.client_id, c.first_name, c.last_name
     1030             FROM refund rf
     1031             JOIN "order" o ON rf.order_num = o.order_num
     1032             JOIN client c ON o.client_id = c.client_id
     1033             WHERE o.store_id = ? AND rf.status = 'pending'
     1034             ORDER BY rf.request_date ASC`,
     1035                        [storeId],
     1036                        (err, rows) => {
    19301037                            if (!err) {
    1931                                 tasks.pending_refunds =
    1932                                     result.rows || [];
    1933                             }
    1934 
    1935                             callback(
    1936                                 null,
    1937                                 tasks
    1938                             );
     1038                                tasks.pending_refunds = rows || [];
     1039                            }
     1040                            callback(null, tasks);
    19391041                        }
    19401042                    );
    … …  
    19451047}
    19461048
    1947 
    1948 /*
    1949  * ============================================================
    1950  * CLIENT STATISTICS
    1951  * ============================================================
    1952  */
    1953 
     1049// Client stats functions
    19541050function getClientStats(clientId, callback) {
    1955 
    19561051    const stats = {};
    19571052
    1958     query(
    1959         `SELECT COUNT(*) AS total_orders
    1960          FROM "order"
    1961          WHERE client_id = $1`,
     1053    // Get total orders
     1054    database.get(
     1055        'SELECT COUNT(*) as total_orders FROM "order" WHERE client_id = ?',
    19621056        [clientId],
    1963         (err, result) => {
    1964 
    1965             if (err) {
    1966                 callback(err, null);
    1967                 return;
    1968             }
    1969 
    1970             stats.total_orders =
    1971                 Number(
    1972                     result.rows[0]
    1973                         ? result.rows[0].total_orders
    1974                         : 0
    1975                 );
    1976 
    1977             query(
    1978                 `SELECT
    1979                     COALESCE(
    1980                         SUM(
    1981                             oi.price * oi.quantity
    1982                         ),
    1983                         0
    1984                     ) AS total_spent
    1985                  FROM order_items oi
    1986                  JOIN "order" o
    1987                    ON oi.order_num = o.order_num
    1988                  WHERE o.client_id = $1`,
     1057        (err, row) => {
     1058            stats.total_orders = row ? row.total_orders : 0;
     1059
     1060            // Get total spent
     1061            database.get(
     1062                `SELECT SUM(oi.price * oi.quantity) as total_spent
     1063         FROM order_items oi
     1064         JOIN "order" o ON oi.order_num = o.order_num
     1065         WHERE o.client_id = ?`,
    19891066                [clientId],
    1990                 (err, result) => {
    1991 
    1992                     if (err) {
    1993                         callback(err, null);
    1994                         return;
    1995                     }
    1996 
    1997                     stats.total_spent =
    1998                         Number(
    1999                             result.rows[0]
    2000                                 ? result.rows[0].total_spent
    2001                                 : 0
    2002                         );
    2003 
    2004                     query(
    2005                         `SELECT COUNT(*) AS pending_orders
    2006                          FROM "order"
    2007                          WHERE client_id = $1
    2008                            AND status = $2`,
    2009                         [
    2010                             clientId,
    2011                             'pending'
    2012                         ],
    2013                         (err, result) => {
    2014 
    2015                             if (err) {
    2016                                 callback(err, null);
    2017                                 return;
    2018                             }
    2019 
    2020                             stats.pending_orders =
    2021                                 Number(
    2022                                     result.rows[0]
    2023                                         ? result.rows[0]
    2024                                             .pending_orders
    2025                                         : 0
    2026                                 );
    2027 
    2028                             query(
    2029                                 `SELECT
    2030                                     COUNT(*) AS delivered_orders
    2031                                  FROM "order"
    2032                                  WHERE client_id = $1
    2033                                    AND status = $2`,
    2034                                 [
    2035                                     clientId,
    2036                                     'delivered'
    2037                                 ],
    2038                                 (err, result) => {
    2039 
    2040                                     if (err) {
    2041                                         callback(err, null);
    2042                                         return;
    2043                                     }
    2044 
    2045                                     stats.delivered_orders =
    2046                                         Number(
    2047                                             result.rows[0]
    2048                                                 ? result.rows[0]
    2049                                                     .delivered_orders
    2050                                                 : 0
    2051                                         );
    2052 
    2053                                     callback(
    2054                                         null,
    2055                                         stats
    2056                                     );
     1067                (err, row) => {
     1068                    stats.total_spent = row && row.total_spent ? row.total_spent : 0;
     1069
     1070                    // Get pending orders
     1071                    database.get(
     1072                        'SELECT COUNT(*) as pending_orders FROM "order" WHERE client_id = ? AND status = "pending"',
     1073                        [clientId],
     1074                        (err, row) => {
     1075                            stats.pending_orders = row ? row.pending_orders : 0;
     1076
     1077                            // Get delivered orders
     1078                            database.get(
     1079                                'SELECT COUNT(*) as delivered_orders FROM "order" WHERE client_id = ? AND status = "delivered"',
     1080                                [clientId],
     1081                                (err, row) => {
     1082                                    stats.delivered_orders = row ? row.delivered_orders : 0;
     1083                                    callback(null, stats);
    20571084                                }
    20581085                            );
    … …  
    20651092}
    20661093
    2067 
    2068 /*
    2069  * ============================================================
    2070  * ADMIN USER FUNCTIONS
    2071  * ============================================================
    2072  */
    2073 
     1094// User functions for admin
    20741095function getAllUsers(callback) {
    2075 
    2076     query(
    2077         `SELECT
    2078             client_id AS id,
    2079             first_name,
    2080             last_name,
    2081             email,
    2082             'client' AS user_type,
    2083             NULL AS username,
    2084             5 AS role_priority
     1096    const usersMap = new Map(); // Use Map to deduplicate by ID
     1097
     1098    // Get client users
     1099    database.all(
     1100        `SELECT client_id as id, first_name, last_name, email, 'client' as user_type,
     1101                NULL as username, NULL as role_priority
    20851102         FROM client
    20861103         ORDER BY client_id`,
    20871104        [],
    2088         (err, result) => {
    2089 
    2090             if (err) {
    2091                 callback(err, null);
    2092                 return;
    2093             }
    2094 
    2095             const usersMap = new Map();
    2096 
    2097             result.rows.forEach(row => {
    2098                 usersMap.set(row.id, row);
    2099             });
    2100 
    2101             /*
    2102              * Personal users
    2103              */
    2104             query(
    2105                 `SELECT
    2106                     p.id,
    2107                     p.first_name,
    2108                     p.last_name,
    2109                     p.email,
    2110                     CASE
    2111                         WHEN b.boss_id IS NOT NULL
    2112                             THEN 'store_owner'
    2113                         ELSE 'store_employee'
    2114                     END AS user_type,
    2115                     NULL AS username,
    2116                     CASE
    2117                         WHEN b.boss_id IS NOT NULL
    2118                             THEN 2
    2119                         ELSE 4
    2120                     END AS role_priority
     1105        (err, rows) => {
     1106            if (!err && rows) {
     1107                rows.forEach(row => {
     1108                    // Clients have lowest priority (5)
     1109                    row.role_priority = 5;
     1110                    usersMap.set(row.id, row);
     1111                });
     1112            }
     1113
     1114            // Get personal users (employees and store owners)
     1115            database.all(
     1116                `SELECT p.id, p.first_name, p.last_name, p.email,
     1117                        CASE
     1118                            WHEN b.boss_id IS NOT NULL THEN 'store_owner'
     1119                            ELSE 'store_employee'
     1120                        END as user_type,
     1121                        NULL as username,
     1122                        CASE
     1123                            WHEN b.boss_id IS NOT NULL THEN 2  -- store_owner priority 2
     1124                            ELSE 4                             -- store_employee priority 4
     1125                        END as role_priority
    21211126                 FROM personal p
    2122                  LEFT JOIN boss b
    2123                    ON p.id = b.boss_id
    2124                  LEFT JOIN employees e
    2125                    ON p.id = e.employee_id
    2126                  WHERE b.boss_id IS NOT NULL
    2127                     OR e.employee_id IS NOT NULL
     1127                 LEFT JOIN boss b ON p.id = b.boss_id
     1128                 LEFT JOIN employees e ON p.id = e.employee_id
     1129                 WHERE b.boss_id IS NOT NULL OR e.employee_id IS NOT NULL
    21281130                 ORDER BY p.id`,
    21291131                [],
    2130                 (err, result) => {
    2131 
    2132                     if (err) {
    2133                         callback(err, null);
    2134                         return;
     1132                (err, rows) => {
     1133                    if (!err && rows) {
     1134                        rows.forEach(row => {
     1135                            // Only add if not exists or current has higher priority (lower number)
     1136                            const existing = usersMap.get(row.id);
     1137                            if (!existing || (existing.role_priority && row.role_priority < existing.role_priority)) {
     1138                                usersMap.set(row.id, row);
     1139                            }
     1140                        });
    21351141                    }
    21361142
    2137                     result.rows.forEach(row => {
    2138 
    2139                         const existing =
    2140                             usersMap.get(row.id);
    2141 
    2142                         if (
    2143                             !existing ||
    2144                             (
    2145                                 existing.role_priority &&
    2146                                 row.role_priority <
    2147                                 existing.role_priority
    2148                             )
    2149                         ) {
    2150                             usersMap.set(
    2151                                 row.id,
    2152                                 row
    2153                             );
    2154                         }
    2155                     });
    2156 
    2157                     /*
    2158                      * System users
    2159                      */
    2160                     query(
    2161                         `SELECT
    2162                             id,
    2163                             username,
    2164                             email,
    2165                             user_type,
    2166                             CASE
    2167                                 WHEN user_type = 'admin'
    2168                                     THEN 1
    2169                                 ELSE 3
    2170                             END AS role_priority
     1143                    // Get system users (including admin)
     1144                    database.all(
     1145                        `SELECT id, username, email, user_type,
     1146                                CASE
     1147                                    WHEN user_type = 'admin' THEN 1  -- admin highest priority
     1148                                    ELSE 3                            -- other system users priority 3
     1149                                END as role_priority
    21711150                         FROM users
    21721151                         ORDER BY id`,
    21731152                        [],
    2174                         (err, result) => {
    2175 
    2176                             if (err) {
    2177                                 callback(err, null);
    2178                                 return;
    2179                             }
    2180 
    2181                             result.rows.forEach(row => {
    2182 
    2183                                 const existing =
    2184                                     usersMap.get(row.id);
    2185 
    2186                                 if (
    2187                                     !existing ||
    2188                                     (
    2189                                         existing.role_priority &&
    2190                                         row.role_priority <
    2191                                         existing.role_priority
    2192                                     )
    2193                                 ) {
    2194 
    2195                                     const userData = {
    2196                                         id: row.id,
    2197                                         username: row.username,
    2198                                         email: row.email,
    2199                                         user_type: row.user_type,
    2200                                         role_priority: row.role_priority
    2201                                     };
    2202 
    2203                                     if (
    2204                                         row.user_type ===
    2205                                         'admin'
    2206                                     ) {
    2207                                         userData.first_name =
    2208                                             'Admin';
    2209 
    2210                                         userData.last_name =
    2211                                             'User';
     1153                        (err, rows) => {
     1154                            if (!err && rows) {
     1155                                rows.forEach(row => {
     1156                                    // System users have priority based on type
     1157                                    const existing = usersMap.get(row.id);
     1158                                    if (!existing || (existing.role_priority && row.role_priority < existing.role_priority)) {
     1159                                        // For system users, format the response properly
     1160                                        const userData = {
     1161                                            id: row.id,
     1162                                            username: row.username,
     1163                                            email: row.email,
     1164                                            user_type: row.user_type,
     1165                                            role_priority: row.role_priority
     1166                                        };
     1167                                        // Add first_name/last_name if not present
     1168                                        if (row.user_type === 'admin') {
     1169                                            userData.first_name = 'Admin';
     1170                                            userData.last_name = 'User';
     1171                                        }
     1172                                        usersMap.set(row.id, userData);
    22121173                                    }
    2213 
    2214                                     usersMap.set(
    2215                                         row.id,
    2216                                         userData
    2217                                     );
    2218                                 }
     1174                                });
     1175                            }
     1176
     1177                            // Convert Map to array and remove role_priority before sending
     1178                            const users = Array.from(usersMap.values()).map(user => {
     1179                                const { role_priority, ...userWithoutPriority } = user;
     1180                                return userWithoutPriority;
    22191181                            });
    22201182
    2221                             const users =
    2222                                 Array.from(
    2223                                     usersMap.values()
    2224                                 ).map(user => {
    2225 
    2226                                     const {
    2227                                         role_priority,
    2228                                         ...userWithoutPriority
    2229                                     } = user;
    2230 
    2231                                     return userWithoutPriority;
    2232                                 });
    2233 
    2234                             callback(
    2235                                 null,
    2236                                 users
    2237                             );
     1183                            callback(null, users);
    22381184                        }
    22391185                    );
    … …  
    22441190}
    22451191
    2246 
    2247 /*
    2248  * ============================================================
    2249  * AUDIT LOG
    2250  * ============================================================
    2251  */
    2252 
    2253 function logAudit(
    2254     userId,
    2255     action,
    2256     resourceType,
    2257     resourceId,
    2258     details,
    2259     ipAddress
    2260 ) {
    2261 
    2262     query(
    2263         `INSERT INTO audit_log
    2264             (
    2265                 user_id,
    2266                 action,
    2267                 resource_type,
    2268                 resource_id,
    2269                 details,
    2270                 ip_address
    2271             )
    2272          VALUES
    2273             ($1, $2, $3, $4, $5, $6)`,
    2274         [
    2275             userId,
    2276             action,
    2277             resourceType,
    2278             resourceId,
    2279             details,
    2280             ipAddress
    2281         ],
     1192// Audit log function
     1193function logAudit(userId, action, resourceType, resourceId, details, ipAddress) {
     1194    database.run(
     1195        `INSERT INTO audit_log (user_id, action, resource_type, resource_id, details, ip_address)
     1196     VALUES (?, ?, ?, ?, ?, ?)`,
     1197        [userId, action, resourceType, resourceId, details, ipAddress],
    22821198        (err) => {
    2283 
    22841199            if (err) {
    2285                 console.error(
    2286                     'Error logging audit:',
    2287                     err
    2288                 );
    2289             }
    2290         }
    2291     );
    2292 }
    2293 
    2294 
    2295 /*
    2296  * ============================================================
    2297  * EXPORTS
    2298  * ============================================================
    2299  */
     1200                console.error('Error logging audit:', err);
     1201            }
     1202        }
     1203    );
     1204}
    23001205
    23011206module.exports = {
    2302 
    2303     pool,
    2304 
    2305     query,
    2306 
     1207    database,
    23071208    ensureGeneralCategory,
    23081209    getGeneralCategoryId,
    2309 
    23101210    getUserByUsername,
    23111211    getUserById,
    23121212    createUser,
    2313 
    23141213    getClientByEmail,
    23151214    getClientById,
    23161215    createClient,
    23171216    verifyClientPassword,
    2318 
    23191217    getPersonalByEmail,
    23201218    getPersonalById,
    23211219    verifyPassword,
    23221220    updatePasswordAndClearForce,
    2323 
    23241221    getProducts,
    23251222    getProductById,
    … …  
    23281225    updateProduct,
    23291226    deleteProduct,
    2330 
    23311227    getCategories,
    23321228    getCategoriesWithParents,
    23331229    createCategory,
    2334 
    23351230    getStores,
    23361231    getStoreProducts,
    … …  
    23391234    getStoreReports,
    23401235    getStoreStats,
    2341 
    23421236    createOrderNew,
    23431237    getOrdersByClient,
    23441238    getAllOrders,
    2345 
    23461239    createReviewNew,
    2347 
    23481240    createRequest,
    2349 
    23501241    createRefund,
    2351 
    23521242    getEmployeeTasks,
    2353 
    23541243    getClientStats,
    2355 
    23561244    getAllUsers,
    2357 
    23581245    logAudit
    23591246};
  • package.json

    r6149556 r6fea37e  
    1313    "dotenv": "^16.3.1",
    1414    "nodemailer": "^6.9.7",
    15     "pg": "^8.23.0"
     15    "sqlite3": "^5.1.6"
    1616  },
    1717  "devDependencies": {
  • server.js

    r6149556 r6fea37e  
    11const http = require('http');
    22const url = require('url');
    3 const { Pool } = require('pg');
     3const database = require('./database.js');
    44const fs = require('fs');
    55const path = require('path');
    … …  
    2525    const emailConfig = {
    2626        host: process.env.SMTP_HOST || 'smtp.gmail.com',
    27         port: (() => {
    28             const configuredPort = parseInt(process.env.SMTP_PORT, 10);
    29             if (process.env.SMTP_HOST === 'smtp.ethereal.email' && configuredPort === 3000) return 587;
    30             return configuredPort || 587;
    31         })(),
     27        port: parseInt(process.env.SMTP_PORT) || 587,
    3228        secure: false,
    3329        auth: {
    … …  
    173169    let code = '';
    174170    for(let i = 0; i < 6; i++) {
    175         code += crypto.randomInt(0, 10);
     171        code += crypto.randomInt(0, 9);
    176172    }
    177173    return code;
    … …  
    322318
    323319                database.database.get(
    324                     'SELECT boss_id FROM boss WHERE boss_id = $1',
     320                    'SELECT boss_id FROM boss WHERE boss_id = ?',
    325321                    [personalId],
    326322                    (err, boss) => {
    … …  
    343339}
    344340
    345 
    346 
    347 const pool = new Pool({
    348     connectionString: process.env.DATABASE_URL,
    349     host: process.env.PGHOST || process.env.DB_HOST || 'localhost',
    350     port: Number(process.env.PGPORT || process.env.DB_PORT || 5432),
    351     user: process.env.PGUSER || process.env.DB_USER || 'postgres',
    352     password: process.env.PGPASSWORD || process.env.DB_PASSWORD || '',
    353     database: process.env.PGDATABASE || process.env.DB_NAME || 'handcraft_marketplace',
    354     max: Number(process.env.PG_POOL_MAX || 10),
    355     idleTimeoutMillis: 30000
    356 });
    357 
    358 let transactionClient = null;
    359 
    360 function dbQuery(sql, params = [], callback) {
    361     const client = transactionClient || pool;
    362     client.query(sql, params)
    363         .then(result => callback(null, result))
    364         .catch(err => callback(err));
     341// Database initialization function
     342async function initializeDatabase() {
     343    console.log('šŸ” Checking database schema...');
     344
     345    // List of all required tables
     346    const requiredTables = [
     347        'client',
     348        'store',
     349        'category',
     350        'users',
     351        'personal',
     352        'product',
     353        'boss',
     354        'employees',
     355        'works_in_store',
     356        'permissions',
     357        'order',
     358        'order_items',
     359        'review',
     360        'request',
     361        'refund',
     362        'report',
     363        'audit_log',
     364        'color',
     365        'image',
     366        'delivery_address',
     367        'roles',
     368        'user_roles'
     369    ];
     370
     371    try {
     372        // For SQLite, we need to use a different approach to check tables
     373        const result = await new Promise((resolve, reject) => {
     374            database.database.all(
     375                "SELECT name FROM sqlite_master WHERE type='table'",
     376                [],
     377                (err, rows) => {
     378                    if (err) reject(err);
     379                    else resolve(rows || []);
     380                }
     381            );
     382        });
     383
     384        const existingTables = result.map(row => row.name);
     385        const missingTables = requiredTables.filter(table => !existingTables.includes(table));
     386
     387        if (missingTables.length > 0) {
     388            console.log(`āš ļø Missing tables: ${missingTables.join(', ')}`);
     389            console.log('šŸ”„ Recreating entire database...');
     390
     391            // Drop all tables in correct order (respecting foreign keys)
     392            await dropAllTables();
     393
     394            // Create all tables
     395            await createAllTables();
     396
     397            // Create indexes
     398            await createIndexes();
     399
     400            // Insert initial data
     401            await insertInitialData();
     402
     403            console.log('āœ… Database recreation completed');
     404        } else {
     405            console.log('āœ… All required tables exist');
     406            // Even if tables exist, ensure admin user exists with ID 000000
     407            await ensureAdminUser();
     408        }
     409    } catch (err) {
     410        console.error('āŒ Error checking database schema:', err);
     411        console.log('āš ļø Attempting to recreate database anyway...');
     412
     413        try {
     414            await dropAllTables();
     415            await createAllTables();
     416            await createIndexes();
     417            await insertInitialData();
     418            console.log('āœ… Database recreation completed');
     419        } catch (createErr) {
     420            console.error('āŒ Failed to recreate database:', createErr);
     421        }
     422    }
    365423}
    366424
    367 const database = {
    368     database: {
    369         get(sql, params, callback) {
    370             if (typeof params === 'function') {
    371                 callback = params;
    372                 params = [];
    373             }
    374             dbQuery(sql, params || [], (err, result) => {
    375                 callback(err, result && result.rows ? result.rows[0] : undefined);
    376             });
    377         },
    378         all(sql, params, callback) {
    379             if (typeof params === 'function') {
    380                 callback = params;
    381                 params = [];
    382             }
    383             dbQuery(sql, params || [], (err, result) => {
    384                 callback(err, result ? result.rows : []);
    385             });
    386         },
    387         run(sql, params, callback) {
    388             if (typeof params === 'function') {
    389                 callback = params;
    390                 params = [];
    391             }
    392             const normalized = String(sql).trim().replace(/;\s*$/, '').toUpperCase();
    393 
    394             if (normalized === 'BEGIN TRANSACTION' || normalized === 'BEGIN') {
    395                 if (transactionClient) {
    396                     callback?.(null);
     425// Function to ensure admin user exists with ID 000000
     426function ensureAdminUser() {
     427    return new Promise((resolve) => {
     428        database.database.get(
     429            'SELECT * FROM users WHERE id = ? OR username = ? OR email = ?',
     430            ['000000', 'admin', 'admin@handcraft.com'],
     431            (err, existingAdmin) => {
     432                if (err) {
     433                    console.error('Error checking for existing admin:', err.message);
     434                    resolve();
    397435                    return;
    398436                }
    399                 pool.connect().then(client => {
    400                     transactionClient = client;
    401                     return client.query('BEGIN');
    402                 }).then(() => callback?.(null))
    403                     .catch(err => {
    404                         if (transactionClient) transactionClient.release();
    405                         transactionClient = null;
    406                         callback?.(err);
     437
     438                // Insert admin user if it doesn't exist
     439                if (!existingAdmin) {
     440                    const adminId = '000000';
     441                    const adminPassword = bcrypt.hashSync('Admin123!', 10);
     442
     443                    // Start a transaction
     444                    database.database.run('BEGIN TRANSACTION', (err) => {
     445                        if (err) {
     446                            console.error('Error beginning transaction:', err);
     447                            resolve();
     448                            return;
     449                        }
     450
     451                        // Insert into users table
     452                        database.database.run(
     453                            `INSERT INTO users (id, username, email, password, user_type, force_password_change)
     454                             VALUES (?, ?, ?, ?, ?, ?)`,
     455                            [adminId, 'admin', 'admin@handcraft.com', adminPassword, 'admin', 1],
     456                            function(err) {
     457                                if (err) {
     458                                    database.database.run('ROLLBACK');
     459                                    console.error('Error inserting admin user:', err.message);
     460                                    resolve();
     461                                    return;
     462                                }
     463
     464                                // Insert into personal table (required for boss table)
     465                                database.database.run(
     466                                    `INSERT INTO personal (id, first_name, last_name, ssn, email, password)
     467                                     VALUES (?, ?, ?, ?, ?, ?)`,
     468                                    [adminId, 'Admin', 'User', '0000000000000', 'admin@handcraft.com', adminPassword],
     469                                    function(err) {
     470                                        if (err) {
     471                                            database.database.run('ROLLBACK');
     472                                            console.error('Error inserting admin personal:', err.message);
     473                                            resolve();
     474                                            return;
     475                                        }
     476
     477                                        // Insert into boss table (store owner)
     478                                        database.database.run(
     479                                            `INSERT INTO boss (boss_id, signature)
     480                                             VALUES (?, ?)`,
     481                                            [adminId, 'Admin Signature'],
     482                                            function(err) {
     483                                                if (err) {
     484                                                    database.database.run('ROLLBACK');
     485                                                    console.error('Error inserting admin boss:', err.message);
     486                                                    resolve();
     487                                                    return;
     488                                                }
     489
     490                                                // Insert into permissions
     491                                                database.database.run(
     492                                                    `INSERT INTO permissions (personal_id, type, authorisation)
     493                                                     VALUES (?, ?, ?)`,
     494                                                    [adminId, 'ADMIN', 'full_access'],
     495                                                    function(err) {
     496                                                        if (err) {
     497                                                            console.error('Error inserting admin permissions:', err.message);
     498                                                            // Continue even if this fails
     499                                                        }
     500
     501                                                        // Assign admin role
     502                                                        database.database.get(
     503                                                            'SELECT role_id FROM roles WHERE name = ?',
     504                                                            ['admin'],
     505                                                            (err, adminRole) => {
     506                                                                if (!err && adminRole) {
     507                                                                    database.database.run(
     508                                                                        'INSERT OR IGNORE INTO user_roles (user_id, role_id) VALUES (?, ?)',
     509                                                                        [adminId, adminRole.role_id],
     510                                                                        (err) => {
     511                                                                            if (err) {
     512                                                                                console.error('Error assigning admin role:', err.message);
     513                                                                            }
     514                                                                        }
     515                                                                    );
     516                                                                }
     517
     518                                                                database.database.run('COMMIT', (commitErr) => {
     519                                                                    if (commitErr) {
     520                                                                        console.error('Error committing transaction:', commitErr);
     521                                                                        database.database.run('ROLLBACK');
     522                                                                    } else {
     523                                                                        console.log('\n');
     524                                                                        console.log('šŸ” ===== ADMIN CREDENTIALS =====');
     525                                                                        console.log('šŸ†” ID: 000000');
     526                                                                        console.log('šŸ‘¤ Username: admin');
     527                                                                        console.log('šŸ“§ Email: admin@handcraft.com');
     528                                                                        console.log('šŸ”‘ Password: Admin123!');
     529                                                                        console.log('āš ļø This is a first-time login. You will be required to change your password after 2FA verification.');
     530                                                                        console.log('================================\n');
     531                                                                    }
     532                                                                    resolve();
     533                                                                });
     534                                                            }
     535                                                        );
     536                                                    }
     537                                                );
     538                                            }
     539                                        );
     540                                    }
     541                                );
     542                            }
     543                        );
    407544                    });
     545                } else {
     546                    console.log('āœ… Admin user already exists with ID:', existingAdmin.id);
     547                    resolve();
     548                }
     549            }
     550        );
     551    });
     552}
     553
     554function dropAllTables() {
     555    return new Promise((resolve, reject) => {
     556        console.log('šŸ—‘ļø Dropping all tables...');
     557
     558        // Drop in reverse order of creation (respect foreign keys)
     559        const dropQueries = [
     560            'DROP TABLE IF EXISTS user_roles',
     561            'DROP TABLE IF EXISTS roles',
     562            'DROP TABLE IF EXISTS delivery_address',
     563            'DROP TABLE IF EXISTS image',
     564            'DROP TABLE IF EXISTS color',
     565            'DROP TABLE IF EXISTS audit_log',
     566            'DROP TABLE IF EXISTS report',
     567            'DROP TABLE IF EXISTS refund',
     568            'DROP TABLE IF EXISTS request',
     569            'DROP TABLE IF EXISTS review',
     570            'DROP TABLE IF EXISTS order_items',
     571            'DROP TABLE IF EXISTS "order"',
     572            'DROP TABLE IF EXISTS permissions',
     573            'DROP TABLE IF EXISTS works_in_store',
     574            'DROP TABLE IF EXISTS employees',
     575            'DROP TABLE IF EXISTS boss',
     576            'DROP TABLE IF EXISTS product',
     577            'DROP TABLE IF EXISTS personal',
     578            'DROP TABLE IF EXISTS users',
     579            'DROP TABLE IF EXISTS category',
     580            'DROP TABLE IF EXISTS store',
     581            'DROP TABLE IF EXISTS client'
     582        ];
     583
     584        let index = 0;
     585
     586        function runNext() {
     587            if (index >= dropQueries.length) {
     588                console.log('āœ… All tables dropped');
     589                resolve();
    408590                return;
    409591            }
    410592
    411             if (normalized === 'COMMIT') {
    412                 if (!transactionClient) {
    413                     callback?.(null);
    414                     return;
     593            database.database.run(dropQueries[index], [], (err) => {
     594                if (err) {
     595                    console.error(`Error dropping table: ${err.message}`);
     596                    // Continue anyway
    415597                }
    416                 const client = transactionClient;
    417                 client.query('COMMIT')
    418                     .then(() => {
    419                         transactionClient = null;
    420                         client.release();
    421                         callback?.(null);
    422                     })
    423                     .catch(err => {
    424                         transactionClient = null;
    425                         client.release();
    426                         callback?.(err);
    427                     });
    428                 return;
    429             }
    430 
    431             if (normalized === 'ROLLBACK') {
    432                 if (!transactionClient) {
    433                     callback?.(null);
    434                     return;
    435                 }
    436                 const client = transactionClient;
    437                 client.query('ROLLBACK')
    438                     .then(() => {
    439                         transactionClient = null;
    440                         client.release();
    441                         callback?.(null);
    442                     })
    443                     .catch(err => {
    444                         transactionClient = null;
    445                         client.release();
    446                         callback?.(err);
    447                     });
    448                 return;
    449             }
    450 
    451             dbQuery(sql, params || [], (err, result) => {
    452                 if (callback) {
    453                     callback.call(
    454                         { changes: result ? result.rowCount : 0, lastID: result?.rows?.[0]?.id },
    455                         err
    456                     );
    457                 }
     598                index++;
     599                runNext();
    458600            });
    459601        }
    460     },
    461 
    462     async initializeDatabase() {
    463         // The database supplied by the project is authoritative.  Existing tables
    464         // are removed before recreation so an old incompatible schema can never
    465         // survive a restart and cause CREATE TABLE IF NOT EXISTS to skip columns.
    466         const schemaCompatibility = await pool.query(`
    467             SELECT
    468                 EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema='public' AND table_name='product') AS product_exists,
    469                 EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema='public' AND table_name='product' AND column_name='category_id') AS product_has_category,
    470                 EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema='public' AND table_name='product' AND column_name='store_id') AS product_has_store,
    471                 EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema='public' AND table_name='order' AND column_name='store_id') AS order_has_store,
    472                 EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema='public' AND table_name='request' AND column_name='store_id') AS request_has_store,
    473                 EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema='public' AND table_name='review' AND column_name='review_id') AS review_has_review_id,
    474                 EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema='public' AND table_name='refund' AND column_name='request_date') AS refund_has_request_date
    475         `);
    476 
    477         const c = schemaCompatibility.rows[0];
    478         const forceReset = String(process.env.RESET_DATABASE || '').toLowerCase() === 'true' || process.env.RESET_DATABASE === '1';
    479         const schemaMismatch = !c.product_exists || !c.product_has_category || c.product_has_store || c.order_has_store || c.request_has_store || c.review_has_review_id || c.refund_has_request_date;
    480         const resetDatabase = forceReset || schemaMismatch;
    481 
    482         if (resetDatabase) {
    483             console.log('🧹 Existing PostgreSQL schema is incompatible with the project schema. Recreating project tables...');
    484             await pool.query(`
    485                 DROP TABLE IF EXISTS audit_log CASCADE;
    486                 DROP TABLE IF EXISTS user_roles CASCADE;
    487                 DROP TABLE IF EXISTS roles CASCADE;
    488                 DROP TABLE IF EXISTS users CASCADE;
    489                 DROP TABLE IF EXISTS approves CASCADE;
    490                 DROP TABLE IF EXISTS includes CASCADE;
    491                 DROP TABLE IF EXISTS sells CASCADE;
    492                 DROP TABLE IF EXISTS worked CASCADE;
    493                 DROP TABLE IF EXISTS works_in_store CASCADE;
    494                 DROP TABLE IF EXISTS makes_change CASCADE;
    495                 DROP TABLE IF EXISTS "change" CASCADE;
    496                 DROP TABLE IF EXISTS for_store CASCADE;
    497                 DROP TABLE IF EXISTS answers CASCADE;
    498                 DROP TABLE IF EXISTS makes_request CASCADE;
    499                 DROP TABLE IF EXISTS request CASCADE;
    500                 DROP TABLE IF EXISTS exchanges_data CASCADE;
    501                 DROP TABLE IF EXISTS monthly_profit CASCADE;
    502                 DROP TABLE IF EXISTS report CASCADE;
    503                 DROP TABLE IF EXISTS refund CASCADE;
    504                 DROP TABLE IF EXISTS review CASCADE;
    505                 DROP TABLE IF EXISTS "order" CASCADE;
    506                 DROP TABLE IF EXISTS delivery_address CASCADE;
    507                 DROP TABLE IF EXISTS client CASCADE;
    508                 DROP TABLE IF EXISTS employees CASCADE;
    509                 DROP TABLE IF EXISTS boss CASCADE;
    510                 DROP TABLE IF EXISTS permissions CASCADE;
    511                 DROP TABLE IF EXISTS personal CASCADE;
    512                 DROP TABLE IF EXISTS color CASCADE;
    513                 DROP TABLE IF EXISTS image CASCADE;
    514                 DROP TABLE IF EXISTS product CASCADE;
    515                 DROP TABLE IF EXISTS store CASCADE;
    516                 DROP TABLE IF EXISTS category CASCADE;
    517             `);
    518         }
    519 
    520         const schema = `
    521             CREATE TABLE IF NOT EXISTS category (
    522                 id SERIAL PRIMARY KEY,
    523                 name VARCHAR(50) NOT NULL,
    524                 parent_category_id INTEGER REFERENCES category(id) NOT NULL
    525             );
    526 
    527             CREATE TABLE IF NOT EXISTS product (
    528                 code VARCHAR(8) PRIMARY KEY DEFAULT '-1',
    529                 price DECIMAL(10,2) NOT NULL CHECK (price >= 0.0),
    530                 availability INTEGER NOT NULL,
    531                 weight DECIMAL(5,2) NOT NULL CHECK (weight > 0),
    532                 width_x_length_x_depth VARCHAR(20) NOT NULL,
    533                 aprox_production_time INTEGER NOT NULL,
    534                 description VARCHAR(500) NOT NULL,
    535                 category_id INTEGER NOT NULL REFERENCES category(id) ON DELETE SET DEFAULT
    536             );
    537 
    538             CREATE TABLE IF NOT EXISTS image (
    539                 product_code VARCHAR(8) REFERENCES product(code) ON DELETE CASCADE,
    540                 image VARCHAR NOT NULL DEFAULT 'Image NOT found!'
    541             );
    542 
    543             CREATE TABLE IF NOT EXISTS color (
    544                 product_code VARCHAR(8) REFERENCES product(code) ON DELETE CASCADE,
    545                 color VARCHAR(50)
    546             );
    547 
    548             CREATE TABLE IF NOT EXISTS store (
    549                 store_ID VARCHAR(3) PRIMARY KEY,
    550                 name VARCHAR(50) UNIQUE NOT NULL,
     602
     603        runNext();
     604    });
     605}
     606
     607function createAllTables() {
     608    return new Promise((resolve, reject) => {
     609        console.log('šŸ—ļø Creating tables...');
     610
     611        const createQueries = [
     612            // Client table (SERIAL ID starting from 1000)
     613            `CREATE TABLE IF NOT EXISTS client (
     614                client_id INTEGER PRIMARY KEY AUTOINCREMENT,
     615                first_name VARCHAR(100) NOT NULL,
     616                last_name VARCHAR(100) NOT NULL,
     617                email VARCHAR(255) UNIQUE NOT NULL,
     618                password VARCHAR(255) NOT NULL,
     619                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
     620            )`,
     621
     622            // Store table (VARCHAR ID)
     623            `CREATE TABLE IF NOT EXISTS store (
     624                store_id VARCHAR(10) PRIMARY KEY,
     625                name VARCHAR(255) NOT NULL,
    551626                date_of_founding DATE NOT NULL,
    552                 physical_address VARCHAR(100) NOT NULL,
    553                 store_email VARCHAR(40) UNIQUE NOT NULL CHECK (store_email ~* '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$'),
    554                 rating DECIMAL(2,1) NOT NULL DEFAULT 0 CHECK (rating>=0.0 AND rating<=5.0)
    555             );
    556 
    557             CREATE TABLE IF NOT EXISTS personal (
    558                 id VARCHAR(10) PRIMARY KEY,
    559                 first_name VARCHAR(20) NOT NULL,
    560                 last_name VARCHAR(20) NOT NULL,
    561                 ssn VARCHAR(13) NOT NULL CHECK (ssn ~ '^[0-9]{13}$'),
    562                 email VARCHAR(50) UNIQUE NOT NULL CHECK (email ~* '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$'),
    563                 password VARCHAR NOT NULL
    564             );
    565 
    566             CREATE TABLE IF NOT EXISTS permissions (
    567                 personal_is VARCHAR(10) PRIMARY KEY REFERENCES personal(id) ON DELETE CASCADE,
    568                 type VARCHAR(50) NOT NULL,
    569                 authorisation VARCHAR(50) NOT NULL
    570             );
    571 
    572             CREATE TABLE IF NOT EXISTS boss (
    573                 boss_id VARCHAR(10) PRIMARY KEY REFERENCES personal(id) ON DELETE CASCADE
    574             );
    575 
    576             CREATE TABLE IF NOT EXISTS employees (
    577                 employee_id VARCHAR(10) PRIMARY KEY REFERENCES personal(id) ON DELETE CASCADE,
    578                 date_of_hire DATE NOT NULL
    579             );
    580 
    581             CREATE TABLE IF NOT EXISTS client (
    582                 client_ID SERIAL PRIMARY KEY,
    583                 first_name VARCHAR(50) NOT NULL,
    584                 last_name VARCHAR(50) NOT NULL,
    585                 email VARCHAR(50) UNIQUE NOT NULL CHECK (email ~* '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$'),
    586                 password VARCHAR NOT NULL
    587             );
    588 
    589             CREATE TABLE IF NOT EXISTS delivery_address (
    590                 client_ID INTEGER PRIMARY KEY REFERENCES client(client_ID) ON DELETE CASCADE,
    591                 address VARCHAR(200) NOT NULL,
    592                 city VARCHAR(30) NOT NULL,
    593                 postcode VARCHAR(20) NOT NULL,
    594                 country VARCHAR(40) NOT NULL,
    595                 is_default BOOLEAN DEFAULT TRUE
    596             );
    597 
    598             CREATE TABLE IF NOT EXISTS "order" (
    599                 order_num VARCHAR(11) PRIMARY KEY,
    600                 client_ID INTEGER REFERENCES client(client_ID) ON DELETE CASCADE,
    601                 status VARCHAR(20) NOT NULL DEFAULT 'placed order',
    602                 last_date_mod TIMESTAMP NOT NULL,
    603                 payment_method VARCHAR(250) NOT NULL,
    604                 discount DECIMAL(5,2) DEFAULT 0.0 CHECK(discount>=0.0 AND discount<=100.00),
    605                 CONSTRAINT check_status CHECK (status IN ('placed order', 'being processed', 'shipping', 'delivered', 'canceled'))
    606             );
    607 
    608             CREATE TABLE IF NOT EXISTS review (
    609                 order_num VARCHAR(11) PRIMARY KEY REFERENCES "order"(order_num) ON DELETE CASCADE,
    610                 comment VARCHAR(300),
    611                 rating DECIMAL(2,1) NOT NULL CHECK(rating>=0.0 AND rating<=5.0),
    612                 last_mod_date TIMESTAMP NOT NULL
    613             );
    614 
    615             CREATE TABLE IF NOT EXISTS refund (
    616                 refund_id SERIAL PRIMARY KEY,
    617                 order_num VARCHAR(11) REFERENCES "order"(order_num) ON DELETE CASCADE,
    618                 reason VARCHAR(300),
    619                 amount DECIMAL(5,2) NOT NULL,
    620                 status VARCHAR(100) NOT NULL DEFAULT 'requested refund',
    621                 CONSTRAINT check_refund_status CHECK (status IN ('requested refund', 'being revied', 'approved', 'not approved', 'processed'))
    622             );
    623 
    624             CREATE TABLE IF NOT EXISTS report (
    625                 date TIMESTAMP NOT NULL,
    626                 store_ID VARCHAR(3) NOT NULL REFERENCES store(store_ID) ON DELETE CASCADE,
    627                 overall_profit NUMERIC NOT NULL DEFAULT 0.0 CHECK(overall_profit>=0),
    628                 sales_trend VARCHAR(100) NOT NULL,
    629                 marketing_growth VARCHAR(100) NOT NULL,
    630                 owner_signature VARCHAR(50) NOT NULL DEFAULT 'Not signed yet',
    631                 PRIMARY KEY (date, store_ID)
    632             );
    633 
    634             CREATE TABLE IF NOT EXISTS monthly_profit (
    635                 report_date TIMESTAMP NOT NULL,
    636                 store_ID VARCHAR(3) NOT NULL,
    637                 month_and_year DATE NOT NULL,
    638                 profit NUMERIC NOT NULL DEFAULT 0.0,
    639                 PRIMARY KEY(report_date, store_ID),
    640                 FOREIGN KEY (report_date, store_ID) REFERENCES report(date, store_ID) ON DELETE CASCADE
    641             );
    642 
    643             CREATE TABLE IF NOT EXISTS exchanges_data (
    644                 report_date TIMESTAMP NOT NULL,
    645                 store_ID VARCHAR(3) NOT NULL,
    646                 monthly_profit NUMERIC NOT NULL DEFAULT 0.0,
    647                 date TIMESTAMP NOT NULL,
    648                 sales NUMERIC NOT NULL DEFAULT 0.0,
    649                 damages NUMERIC NOT NULL DEFAULT 0.0 CHECK (damages<=0),
    650                 PRIMARY KEY (report_date, store_ID),
    651                 FOREIGN KEY (report_date, store_ID) REFERENCES report(date, store_ID) ON DELETE CASCADE
    652             );
    653 
    654             CREATE TABLE IF NOT EXISTS request (
    655                 request_num VARCHAR(14) PRIMARY KEY,
    656                 date_and_time TIMESTAMP NOT NULL,
    657                 problem VARCHAR(300) NOT NULL,
    658                 notes_of_communication VARCHAR,
    659                 customer_satisfaction NUMERIC NOT NULL
    660             );
    661 
    662             CREATE TABLE IF NOT EXISTS makes_request (
    663                 client_ID INTEGER NOT NULL REFERENCES client(client_ID) ON DELETE CASCADE,
    664                 order_num VARCHAR(11) UNIQUE NOT NULL REFERENCES "order"(order_num) ON DELETE CASCADE,
    665                 PRIMARY KEY(client_ID, order_num)
    666             );
    667 
    668             CREATE TABLE IF NOT EXISTS answers (
    669                 request_num VARCHAR(14) REFERENCES request(request_num) ON DELETE CASCADE,
    670                 personal_id VARCHAR(10) NOT NULL REFERENCES personal(id) ON DELETE CASCADE,
    671                 PRIMARY KEY(request_num, personal_id)
    672             );
    673 
    674             CREATE TABLE IF NOT EXISTS for_store (
    675                 request_num VARCHAR(14) REFERENCES request(request_num) ON DELETE CASCADE,
    676                 store_ID VARCHAR(3) REFERENCES store(store_ID) ON DELETE CASCADE,
    677                 PRIMARY KEY(request_num, store_ID)
    678             );
    679 
    680             CREATE TABLE IF NOT EXISTS "change" (
    681                 date_and_time TIMESTAMP NOT NULL,
    682                 product_code VARCHAR(8) REFERENCES product(code) ON DELETE CASCADE,
    683                 changes VARCHAR NOT NULL,
    684                 PRIMARY KEY (date_and_time, product_code)
    685             );
    686 
    687             CREATE TABLE IF NOT EXISTS makes_change (
    688                 personal_id VARCHAR(10) REFERENCES personal(id) ON DELETE CASCADE,
    689                 change_date_time TIMESTAMP,
    690                 product_code VARCHAR(8),
    691                 PRIMARY KEY(personal_id, change_date_time, product_code),
    692                 FOREIGN KEY(change_date_time, product_code) REFERENCES "change"(date_and_time, product_code) ON DELETE CASCADE
    693             );
    694 
    695             CREATE TABLE IF NOT EXISTS works_in_store (
    696                 personal_id VARCHAR(10) REFERENCES personal(id) ON DELETE CASCADE,
    697                 store_ID VARCHAR(3) REFERENCES store(store_ID) ON DELETE CASCADE,
    698                 PRIMARY KEY(personal_id, store_ID)
    699             );
    700 
    701             CREATE TABLE IF NOT EXISTS worked (
    702                 personal_id VARCHAR(10) REFERENCES personal(id) ON DELETE CASCADE,
    703                 report_date TIMESTAMP,
    704                 store_ID VARCHAR(3),
    705                 wage NUMERIC NOT NULL CHECK (wage>=0),
    706                 pay_method VARCHAR DEFAULT 'full-time',
    707                 total_hours NUMERIC NOT NULL,
    708                 week VARCHAR(23) NOT NULL,
    709                 PRIMARY KEY (personal_id, report_date, store_ID),
    710                 FOREIGN KEY (report_date, store_ID) REFERENCES report(date, store_ID) ON DELETE CASCADE,
    711                 CONSTRAINT check_pay_method CHECK (pay_method IN ('full_time', 'part-time', 'custom'))
    712             );
    713 
    714             CREATE TABLE IF NOT EXISTS sells (
    715                 product_code VARCHAR(8) REFERENCES product(code) ON DELETE CASCADE,
    716                 store_ID VARCHAR(3) REFERENCES store(store_ID) ON DELETE CASCADE,
    717                 discount NUMERIC NOT NULL DEFAULT 0.0,
    718                 PRIMARY KEY (product_code, store_ID)
    719             );
    720 
    721             CREATE TABLE IF NOT EXISTS includes (
    722                 order_num VARCHAR(11) REFERENCES "order"(order_num) ON DELETE CASCADE,
    723                 product_code VARCHAR(8) REFERENCES product(code) ON DELETE CASCADE,
    724                 quantity INTEGER NOT NULL CHECK(quantity>=0),
    725                 PRIMARY KEY (order_num, product_code)
    726             );
    727 
    728             CREATE TABLE IF NOT EXISTS approves (
    729                 boss_id VARCHAR(10) REFERENCES boss(boss_id) ON DELETE CASCADE,
    730                 report_date TIMESTAMP,
    731                 store_ID VARCHAR(3),
    732                 owner_signature VARCHAR NOT NULL,
    733                 PRIMARY KEY (boss_id, report_date, store_ID),
    734                 FOREIGN KEY (report_date, store_ID) REFERENCES report(date, store_ID) ON DELETE CASCADE
    735             );
    736 
    737             -- These four small tables are application authentication/audit storage.
    738             -- They do not modify any of the project tables above.
    739             CREATE TABLE IF NOT EXISTS users (
     627                physical_address TEXT NOT NULL,
     628                store_email VARCHAR(255) UNIQUE NOT NULL,
     629                rating DECIMAL(3,2) DEFAULT 0.0
     630            )`,
     631
     632            // Category table (SERIAL ID starting from 1)
     633            `CREATE TABLE IF NOT EXISTS category (
     634                category_id INTEGER PRIMARY KEY AUTOINCREMENT,
     635                name VARCHAR(100) NOT NULL,
     636                description TEXT,
     637                parent_category_id INTEGER REFERENCES category(category_id) ON DELETE SET NULL
     638            )`,
     639
     640            // Users table (VARCHAR ID)
     641            `CREATE TABLE IF NOT EXISTS users (
    740642                id VARCHAR(50) PRIMARY KEY,
    741643                username VARCHAR(100) UNIQUE NOT NULL,
    … …  
    743645                password VARCHAR(255) NOT NULL,
    744646                user_type VARCHAR(50) NOT NULL,
    745                 force_password_change BOOLEAN DEFAULT FALSE,
     647                force_password_change INTEGER DEFAULT 0,
    746648                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    747             );
    748 
    749             CREATE TABLE IF NOT EXISTS roles (
    750                 role_id SERIAL PRIMARY KEY,
    751                 name VARCHAR(50) UNIQUE NOT NULL,
    752                 description TEXT
    753             );
    754 
    755             CREATE TABLE IF NOT EXISTS user_roles (
    756                 user_id VARCHAR(50) REFERENCES users(id) ON DELETE CASCADE,
    757                 role_id INTEGER REFERENCES roles(role_id) ON DELETE CASCADE,
    758                 PRIMARY KEY(user_id, role_id)
    759             );
    760 
    761             CREATE TABLE IF NOT EXISTS audit_log (
    762                 log_id BIGSERIAL PRIMARY KEY,
     649            )`,
     650
     651            // Personal table (VARCHAR ID - format: storeId(3) + '001' for owner, storeId(3) + employeeNum(3) for employees)
     652            `CREATE TABLE IF NOT EXISTS personal (
     653                id VARCHAR(10) PRIMARY KEY,
     654                first_name VARCHAR(100) NOT NULL,
     655                last_name VARCHAR(100) NOT NULL,
     656                ssn VARCHAR(13) UNIQUE NOT NULL,
     657                email VARCHAR(255) UNIQUE NOT NULL,
     658                password VARCHAR(255) NOT NULL,
     659                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
     660            )`,
     661
     662            // Product table (VARCHAR ID)
     663            `CREATE TABLE IF NOT EXISTS product (
     664                id VARCHAR(50) PRIMARY KEY,
     665                code VARCHAR(20) UNIQUE NOT NULL,
     666                description TEXT NOT NULL,
     667                price DECIMAL(10,2) NOT NULL,
     668                availability INTEGER NOT NULL DEFAULT 0,
     669                weight DECIMAL(10,2),
     670                dimensions VARCHAR(50),
     671                production_time INTEGER,
     672                category_id INTEGER REFERENCES category(category_id) ON DELETE SET NULL,
     673                store_id VARCHAR(10) REFERENCES store(store_id) ON DELETE CASCADE,
     674                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
     675            )`,
     676
     677            // Boss table (VARCHAR ID - references personal.id)
     678            `CREATE TABLE IF NOT EXISTS boss (
     679                boss_id VARCHAR(10) PRIMARY KEY REFERENCES personal(id) ON DELETE CASCADE,
     680                signature TEXT NOT NULL,
     681                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
     682            )`,
     683
     684            // Employees table (VARCHAR ID - references personal.id)
     685            `CREATE TABLE IF NOT EXISTS employees (
     686                employee_id VARCHAR(10) PRIMARY KEY REFERENCES personal(id) ON DELETE CASCADE,
     687                date_of_hire DATE NOT NULL,
     688                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
     689            )`,
     690
     691            // Works_in_store table (junction)
     692            `CREATE TABLE IF NOT EXISTS works_in_store (
     693                personal_id VARCHAR(10) REFERENCES personal(id) ON DELETE CASCADE,
     694                store_id VARCHAR(10) REFERENCES store(store_id) ON DELETE CASCADE,
     695                PRIMARY KEY (personal_id, store_id)
     696            )`,
     697
     698            // Permissions table
     699            `CREATE TABLE IF NOT EXISTS permissions (
     700                permission_id INTEGER PRIMARY KEY AUTOINCREMENT,
     701                personal_id VARCHAR(10) REFERENCES personal(id) ON DELETE CASCADE,
     702                type VARCHAR(50) NOT NULL,
     703                authorisation TEXT,
     704                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
     705            )`,
     706
     707            // Order table (VARCHAR ID)
     708            `CREATE TABLE IF NOT EXISTS "order" (
     709                order_num VARCHAR(20) PRIMARY KEY,
     710                client_id INTEGER REFERENCES client(client_id) ON DELETE SET NULL,
     711                order_date TIMESTAMP NOT NULL,
     712                quantity INTEGER NOT NULL,
     713                payment_method VARCHAR(50) NOT NULL,
     714                discount DECIMAL(10,2) DEFAULT 0,
     715                delivery_address TEXT NOT NULL,
     716                store_id VARCHAR(10) REFERENCES store(store_id) ON DELETE SET NULL,
     717                status VARCHAR(50) DEFAULT 'pending',
     718                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
     719            )`,
     720
     721            // Order_items table
     722            `CREATE TABLE IF NOT EXISTS order_items (
     723                item_id INTEGER PRIMARY KEY AUTOINCREMENT,
     724                order_num VARCHAR(20) REFERENCES "order"(order_num) ON DELETE CASCADE,
     725                product_code VARCHAR(20) REFERENCES product(code) ON DELETE SET NULL,
     726                quantity INTEGER NOT NULL,
     727                price DECIMAL(10,2) NOT NULL,
     728                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
     729            )`,
     730
     731            // Review table (VARCHAR ID)
     732            `CREATE TABLE IF NOT EXISTS review (
     733                review_id VARCHAR(20) PRIMARY KEY,
     734                client_id INTEGER REFERENCES client(client_id) ON DELETE SET NULL,
     735                product_code VARCHAR(20) REFERENCES product(code) ON DELETE CASCADE,
     736                rating INTEGER NOT NULL CHECK (rating >= 1 AND rating <= 5),
     737                comment TEXT,
     738                review_date TIMESTAMP NOT NULL,
     739                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
     740            )`,
     741
     742            // Request table (VARCHAR ID)
     743            `CREATE TABLE IF NOT EXISTS request (
     744                request_num VARCHAR(50) PRIMARY KEY,
     745                date_and_time TIMESTAMP NOT NULL,
     746                problem TEXT NOT NULL,
     747                client_id INTEGER REFERENCES client(client_id) ON DELETE SET NULL,
     748                store_id VARCHAR(10) REFERENCES store(store_id) ON DELETE CASCADE,
     749                status VARCHAR(50) DEFAULT 'pending',
     750                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
     751            )`,
     752
     753            // Refund table (VARCHAR ID)
     754            `CREATE TABLE IF NOT EXISTS refund (
     755                refund_id VARCHAR(50) PRIMARY KEY,
     756                order_num VARCHAR(20) REFERENCES "order"(order_num) ON DELETE CASCADE,
     757                amount DECIMAL(10,2) NOT NULL,
     758                reason TEXT NOT NULL,
     759                status VARCHAR(50) DEFAULT 'pending',
     760                request_date TIMESTAMP NOT NULL,
     761                processed_date TIMESTAMP,
     762                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
     763            )`,
     764
     765            // Report table (VARCHAR ID)
     766            `CREATE TABLE IF NOT EXISTS report (
     767                id VARCHAR(50) PRIMARY KEY,
     768                store_id VARCHAR(10) REFERENCES store(store_id) ON DELETE CASCADE,
     769                period VARCHAR(50) NOT NULL,
     770                start_date DATE NOT NULL,
     771                end_date DATE NOT NULL,
     772                type VARCHAR(50) NOT NULL,
     773                generated_by VARCHAR(10) REFERENCES personal(id) ON DELETE SET NULL,
     774                generated_at TIMESTAMP NOT NULL,
     775                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
     776            )`,
     777
     778            // Audit_log table (SERIAL ID)
     779            `CREATE TABLE IF NOT EXISTS audit_log (
     780                log_id INTEGER PRIMARY KEY AUTOINCREMENT,
    763781                user_id VARCHAR(50),
    764782                action VARCHAR(100) NOT NULL,
    … …  
    768786                ip_address VARCHAR(45),
    769787                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
     788            )`,
     789
     790            // Color table (SERIAL ID)
     791            `CREATE TABLE IF NOT EXISTS color (
     792                color_id INTEGER PRIMARY KEY AUTOINCREMENT,
     793                name VARCHAR(50) NOT NULL,
     794                hex_code VARCHAR(7) NOT NULL,
     795                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
     796            )`,
     797
     798            // Image table (SERIAL ID)
     799            `CREATE TABLE IF NOT EXISTS image (
     800                image_id INTEGER PRIMARY KEY AUTOINCREMENT,
     801                product_code VARCHAR(20) REFERENCES product(code) ON DELETE CASCADE,
     802                image_url TEXT NOT NULL,
     803                is_primary BOOLEAN DEFAULT FALSE,
     804                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
     805            )`,
     806
     807            // Delivery_address table (SERIAL ID)
     808            `CREATE TABLE IF NOT EXISTS delivery_address (
     809                address_id INTEGER PRIMARY KEY AUTOINCREMENT,
     810                client_id INTEGER REFERENCES client(client_id) ON DELETE CASCADE,
     811                address TEXT NOT NULL,
     812                city VARCHAR(100) NOT NULL,
     813                postcode VARCHAR(20) NOT NULL,
     814                country VARCHAR(100) NOT NULL,
     815                is_default BOOLEAN DEFAULT FALSE,
     816                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
     817            )`,
     818
     819            // Roles table (SERIAL ID)
     820            `CREATE TABLE IF NOT EXISTS roles (
     821                role_id INTEGER PRIMARY KEY AUTOINCREMENT,
     822                name VARCHAR(50) UNIQUE NOT NULL,
     823                description TEXT,
     824                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
     825            )`,
     826
     827            // User_roles table (junction)
     828            `CREATE TABLE IF NOT EXISTS user_roles (
     829                user_id VARCHAR(50) REFERENCES users(id) ON DELETE CASCADE,
     830                role_id INTEGER REFERENCES roles(role_id) ON DELETE CASCADE,
     831                PRIMARY KEY (user_id, role_id)
     832            )`
     833        ];
     834
     835        let index = 0;
     836
     837        function runNext() {
     838            if (index >= createQueries.length) {
     839                console.log('āœ… All tables created');
     840                resolve();
     841                return;
     842            }
     843
     844            const tableName = createQueries[index].split('TABLE')[1].split('(')[0].trim().replace('IF NOT EXISTS', '').trim();
     845            console.log(`Creating table: ${tableName}...`);
     846
     847            database.database.run(createQueries[index], [], (err) => {
     848                if (err) {
     849                    console.error(`Error creating table: ${err.message}`);
     850                    reject(err);
     851                    return;
     852                }
     853                console.log(`āœ… Created table: ${tableName}`);
     854                index++;
     855                runNext();
     856            });
     857        }
     858
     859        runNext();
     860    });
     861}
     862
     863function createIndexes() {
     864    return new Promise((resolve, reject) => {
     865        console.log('šŸ“Š Creating indexes...');
     866
     867        const indexQueries = [
     868            'CREATE INDEX IF NOT EXISTS idx_product_store ON product(store_id)',
     869            'CREATE INDEX IF NOT EXISTS idx_product_category ON product(category_id)',
     870            'CREATE INDEX IF NOT EXISTS idx_order_client ON "order"(client_id)',
     871            'CREATE INDEX IF NOT EXISTS idx_order_store ON "order"(store_id)',
     872            'CREATE INDEX IF NOT EXISTS idx_order_date ON "order"(order_date)',
     873            'CREATE INDEX IF NOT EXISTS idx_review_client ON review(client_id)',
     874            'CREATE INDEX IF NOT EXISTS idx_review_product ON review(product_code)',
     875            'CREATE INDEX IF NOT EXISTS idx_request_client ON request(client_id)',
     876            'CREATE INDEX IF NOT EXISTS idx_request_store ON request(store_id)',
     877            'CREATE INDEX IF NOT EXISTS idx_refund_order ON refund(order_num)',
     878            'CREATE INDEX IF NOT EXISTS idx_refund_status ON refund(status)',
     879            'CREATE INDEX IF NOT EXISTS idx_personal_email ON personal(email)',
     880            'CREATE INDEX IF NOT EXISTS idx_client_email ON client(email)',
     881            'CREATE INDEX IF NOT EXISTS idx_users_email ON users(email)',
     882            'CREATE INDEX IF NOT EXISTS idx_users_username ON users(username)',
     883            'CREATE INDEX IF NOT EXISTS idx_audit_user ON audit_log(user_id)',
     884            'CREATE INDEX IF NOT EXISTS idx_audit_action ON audit_log(action)',
     885            'CREATE INDEX IF NOT EXISTS idx_audit_created ON audit_log(created_at)',
     886            'CREATE INDEX IF NOT EXISTS idx_delivery_client ON delivery_address(client_id)',
     887            'CREATE INDEX IF NOT EXISTS idx_works_in_store_personal ON works_in_store(personal_id)',
     888            'CREATE INDEX IF NOT EXISTS idx_works_in_store_store ON works_in_store(store_id)'
     889        ];
     890
     891        let index = 0;
     892
     893        function runNext() {
     894            if (index >= indexQueries.length) {
     895                console.log('āœ… Indexes created');
     896                resolve();
     897                return;
     898            }
     899
     900            database.database.run(indexQueries[index], [], (err) => {
     901                if (err) {
     902                    console.log(`āš ļø Index creation warning for ${indexQueries[index].substring(0, 50)}...: ${err.message}`);
     903                }
     904                index++;
     905                runNext();
     906            });
     907        }
     908
     909        runNext();
     910    });
     911}
     912
     913function insertInitialData() {
     914    return new Promise((resolve, reject) => {
     915        console.log('šŸ“ Inserting initial data...');
     916
     917        // Insert default roles
     918        const roles = [
     919            { name: 'admin', description: 'System administrator' },
     920            { name: 'store_owner', description: 'Store owner' },
     921            { name: 'store_employee', description: 'Store employee' },
     922            { name: 'client', description: 'Registered client' },
     923            { name: 'guest', description: 'Unregistered guest' }
     924        ];
     925
     926        let rolesInserted = 0;
     927
     928        roles.forEach(role => {
     929            database.database.run(
     930                `INSERT INTO roles (name, description)
     931                 VALUES (?, ?)
     932                 ON CONFLICT DO NOTHING`,
     933                [role.name, role.description],
     934                (err) => {
     935                    if (err) {
     936                        console.error(`Error inserting role ${role.name}:`, err.message);
     937                    }
     938                    rolesInserted++;
     939
     940                    if (rolesInserted === roles.length) {
     941                        console.log('āœ… Roles inserted');
     942                        // Create admin user with ID 000000
     943                        createAdminUser();
     944
     945                        // Ensure General category exists
     946                        database.ensureGeneralCategory((err) => {
     947                            if (err) {
     948                                console.error('Error ensuring General category:', err.message);
     949                            } else {
     950                                console.log('āœ… General category checked/created');
     951                            }
     952                            resolve();
     953                        });
     954                    }
     955                }
    770956            );
    771 
    772             CREATE INDEX IF NOT EXISTS idx_product_category ON product(category_id);
    773             CREATE INDEX IF NOT EXISTS idx_order_client ON "order"(client_ID);
    774             CREATE INDEX IF NOT EXISTS idx_review_order ON review(order_num);
    775             CREATE INDEX IF NOT EXISTS idx_request_date ON request(date_and_time);
    776             CREATE INDEX IF NOT EXISTS idx_refund_order ON refund(order_num);
    777             CREATE INDEX IF NOT EXISTS idx_personal_email ON personal(email);
    778             CREATE INDEX IF NOT EXISTS idx_client_email ON client(email);
    779             CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
    780             CREATE INDEX IF NOT EXISTS idx_users_username ON users(username);
    781             CREATE INDEX IF NOT EXISTS idx_audit_user ON audit_log(user_id);
    782         `;
    783 
    784         await pool.query(schema);
    785 
    786         const roles = [
    787             ['admin', 'System administrator'],
    788             ['store_owner', 'Store owner'],
    789             ['store_employee', 'Store employee'],
    790             ['client', 'Registered client'],
    791             ['guest', 'Unregistered guest']
    792         ];
    793 
    794         for (const [name, description] of roles) {
    795             await pool.query(
    796                 'INSERT INTO roles(name, description) VALUES($1,$2) ON CONFLICT(name) DO NOTHING',
    797                 [name, description]
    798             );
    799         }
    800 
    801         const hash = bcrypt.hashSync('Admin123!', 10);
    802         await pool.query(
    803             `INSERT INTO users(id, username, email, password, user_type, force_password_change)
    804              VALUES('000000','admin','admin@handcraft.com',$1,'admin',TRUE) ON CONFLICT(id) DO NOTHING`,
    805             [hash]
    806         );
    807         await pool.query(
    808             `INSERT INTO personal(id, first_name, last_name, ssn, email, password)
    809              VALUES('000000','Admin','User','0000000000000','admin@handcraft.com',$1) ON CONFLICT(id) DO NOTHING`,
    810             [hash]
    811         );
    812         await pool.query(`INSERT INTO boss(boss_id) VALUES('000000') ON CONFLICT(boss_id) DO NOTHING`);
    813         await pool.query(
    814             `INSERT INTO permissions(personal_is,type,authorisation)
    815              VALUES('000000','ADMIN','full_access') ON CONFLICT(personal_is) DO NOTHING`
    816         );
    817         await pool.query(
    818             `INSERT INTO user_roles(user_id,role_id)
    819              SELECT '000000', role_id FROM roles WHERE name='admin' ON CONFLICT DO NOTHING`
    820         );
    821         console.log('āœ… PostgreSQL project schema was recreated successfully');
    822     },
    823 
    824     close() {
    825         return pool.end();
    826     },
    827 
    828     getUserById(id, callback) {
    829         dbQuery(
    830             `SELECT u.*,
    831                     COALESCE(json_agg(json_build_object('name',r.name,'description',r.description))
    832                              FILTER (WHERE r.role_id IS NOT NULL), '[]') AS roles
    833              FROM users u
    834              LEFT JOIN user_roles ur ON ur.user_id=u.id
    835              LEFT JOIN roles r ON r.role_id=ur.role_id
    836              WHERE u.id=$1
    837              GROUP BY u.id`,
    838             [String(id)],
    839             (err, result) => callback(err, result?.rows?.[0])
    840         );
    841     },
    842 
    843     getUserByUsername(username, callback) {
    844         dbQuery(
    845             `SELECT u.*,
    846                     COALESCE(json_agg(json_build_object('name',r.name,'description',r.description))
    847                              FILTER (WHERE r.role_id IS NOT NULL), '[]') AS roles
    848              FROM users u
    849              LEFT JOIN user_roles ur ON ur.user_id=u.id
    850              LEFT JOIN roles r ON r.role_id=ur.role_id
    851              WHERE u.username=$1 OR u.email=$1
    852              GROUP BY u.id
    853              LIMIT 1`,
    854             [username],
    855             (err, result) => callback(err, result?.rows?.[0])
    856         );
    857     },
    858 
    859     createUser(id, username, email, password, userType, callback) {
    860         dbQuery(
    861             `INSERT INTO users(id,username,email,password,user_type,force_password_change)
    862              VALUES($1,$2,$3,$4,$5,FALSE) RETURNING id`,
    863             [String(id), username, email, password, userType],
    864             (err, result) => {
    865                 if (err) return callback(err);
    866                 const roleName = userType === 'client' ? 'client' :
    867                     userType === 'store_owner' ? 'store_owner' :
    868                         userType === 'store_employee' ? 'store_employee' : 'guest';
    869                 dbQuery(
    870                     `INSERT INTO user_roles(user_id,role_id)
    871                      SELECT $1, role_id FROM roles WHERE name=$2`,
    872                     [String(id), roleName],
    873                     roleErr => callback(roleErr, String(id))
    874                 );
    875             }
    876         );
    877     },
    878 
    879     createClient(data, callback) {
    880         dbQuery(
    881             `INSERT INTO client(first_name,last_name,email,password)
    882              VALUES($1,$2,$3,$4) RETURNING client_id`,
    883             [data.firstName || data.first_name || '', data.lastName || data.last_name || '', data.email, data.password],
    884             (err, result) => callback(err, result?.rows?.[0]?.client_id)
    885         );
    886     },
    887 
    888     getClientByEmail(email, callback) {
    889         dbQuery('SELECT * FROM client WHERE email=$1', [email],
    890             (err, result) => callback(err, result?.rows?.[0]));
    891     },
    892 
    893     getClientById(id, callback) {
    894         dbQuery('SELECT * FROM client WHERE client_id=$1', [id],
    895             (err, result) => callback(err, result?.rows?.[0]));
    896     },
    897 
    898     getPersonalByEmail(email, callback) {
    899         dbQuery('SELECT * FROM personal WHERE email=$1', [email],
    900             (err, result) => callback(err, result?.rows?.[0]));
    901     },
    902 
    903     getPersonalById(id, callback) {
    904         dbQuery('SELECT * FROM personal WHERE id=$1', [String(id)],
    905             (err, result) => callback(err, result?.rows?.[0]));
    906     },
    907 
    908     verifyPassword(password, hash) {
    909         try { return bcrypt.compareSync(password, hash); } catch { return false; }
    910     },
    911 
    912     verifyClientPassword(password, hash, callback) {
    913         bcrypt.compare(password, hash, callback);
    914     },
    915 
    916     logAudit(userId, action, resourceType, resourceId, details, ipAddress) {
    917         dbQuery(
    918             `INSERT INTO audit_log(user_id,action,resource_type,resource_id,details,ip_address)
    919              VALUES($1,$2,$3,$4,$5,$6)`,
    920             [userId == null ? null : String(userId), action, resourceType,
    921                 resourceId == null ? null : String(resourceId), details, ipAddress],
    922             () => {}
    923         );
    924     },
    925 
    926     getProducts(categoryId, searchTerm, callback) {
    927         const params = [];
    928         const where = [];
    929         if (categoryId) {
    930             params.push(categoryId);
    931             where.push(`p.category_id=$${params.length}`);
    932         }
    933         if (searchTerm) {
    934             params.push(`%${searchTerm}%`);
    935             where.push(`(p.description ILIKE $${params.length} OR p.code ILIKE $${params.length})`);
    936         }
    937         const sql = `SELECT p.*, c.name AS category_name, LEFT(p.code,3) AS store_id
    938                      FROM product p
    939                      LEFT JOIN category c ON c.id=p.category_id
    940                      ${where.length ? 'WHERE ' + where.join(' AND ') : ''}
    941                      ORDER BY p.code`;
    942         dbQuery(sql, params, (err, result) => callback(err, result?.rows || []));
    943     },
    944 
    945     getProductById(id, callback) {
    946         dbQuery(
    947             `SELECT p.*, c.name AS category_name, LEFT(p.code,3) AS store_id
    948              FROM product p
    949              LEFT JOIN category c ON c.id=p.category_id
    950              WHERE p.code=$1 LIMIT 1`,
    951             [String(id)],
    952             (err,result)=>callback(err,result?.rows?.[0])
    953         );
    954     },
    955 
    956     getProductByCode(code, callback) {
    957         dbQuery(
    958             `SELECT p.*, c.name AS category_name, LEFT(p.code,3) AS store_id
    959              FROM product p
    960              LEFT JOIN category c ON c.id=p.category_id
    961              WHERE p.code=$1`,
    962             [code],
    963             (err,result)=>callback(err,result?.rows?.[0])
    964         );
    965     },
    966 
    967     addProduct(personalId, data, callback) {
    968         const storeId = data.store_id || data.storeId || String(data.code || '').slice(0,3);
    969         if (!data.category_id) {
    970             return callback(new Error('category_id is required because product.category_id is NOT NULL'));
    971         }
    972         dbQuery(
    973             `INSERT INTO product(code,price,availability,weight,width_x_length_x_depth,
    974                                  aprox_production_time,description,category_id)
    975              VALUES($1,$2,$3,$4,$5,$6,$7,$8) RETURNING code`,
    976             [
    977                 data.code, data.price, data.availability ?? 0, data.weight,
    978                 data.width_x_length_x_depth || data.dimensions || '',
    979                 data.aprox_production_time ?? data.production_time ?? 0,
    980                 data.description, data.category_id
    981             ],
    982             (err,result)=>{
    983                 if (err) return callback(err);
    984                 dbQuery(
    985                     `INSERT INTO sells(product_code,store_ID,discount)
    986                      VALUES($1,$2,$3)
    987                      ON CONFLICT(product_code,store_ID)
    988                      DO UPDATE SET discount=EXCLUDED.discount`,
    989                     [data.code,storeId,data.discount || 0],
    990                     e => callback(e, data.code)
    991                 );
    992             }
    993         );
    994     },
    995 
    996     updateProduct(personalId, data, callback) {
    997         const fields = [];
    998         const params = [];
    999         const allowed = [
    1000             ['price','price'], ['availability','availability'], ['weight','weight'],
    1001             ['width_x_length_x_depth','width_x_length_x_depth'],
    1002             ['dimensions','width_x_length_x_depth'],
    1003             ['aprox_production_time','aprox_production_time'],
    1004             ['production_time','aprox_production_time'],
    1005             ['description','description'], ['category_id','category_id']
    1006         ];
    1007         for (const [input,col] of allowed) {
    1008             if (data[input] !== undefined) {
    1009                 params.push(data[input]);
    1010                 fields.push(`${col}=$${params.length}`);
    1011             }
    1012         }
    1013         if (!fields.length) return callback(null,0);
    1014         params.push(data.code);
    1015         dbQuery(`UPDATE product SET ${fields.join(', ')} WHERE code=$${params.length}`, params,
    1016             (err,result)=>callback(err,result?.rowCount || 0));
    1017     },
    1018 
    1019     deleteProduct(productCode, storeId, personalId, callback) {
    1020         dbQuery('DELETE FROM product WHERE code=$1 AND LEFT(code,3)=$2', [productCode,storeId],
    1021             (err)=>callback(err));
    1022     },
    1023 
    1024     createCategory(data, callback) {
    1025         const parent = data.parent_category_id ?? data.parentCategoryId ?? data.parent_id;
    1026         if (parent === undefined || parent === null || parent === '') {
    1027             return callback(new Error('parent_category_id is required by the project schema'));
    1028         }
    1029         dbQuery(
    1030             `INSERT INTO category(name,parent_category_id) VALUES($1,$2) RETURNING id,name,parent_category_id`,
    1031             [data.name, parent],
    1032             (err,result)=>callback(err,result?.rows?.[0])
    1033         );
    1034     },
    1035 
    1036     getCategories(callback) {
    1037         dbQuery('SELECT * FROM category ORDER BY name', [], (err,result)=>callback(err,result?.rows||[]));
    1038     },
    1039 
    1040     getCategoriesWithParents(callback) {
    1041         dbQuery(
    1042             `SELECT c.*,p.name AS parent_name
    1043              FROM category c LEFT JOIN category p ON p.id=c.parent_category_id
    1044              ORDER BY c.name`,
    1045             [], (err,result)=>callback(err,result?.rows||[])
    1046         );
    1047     },
    1048 
    1049     getStores(callback) {
    1050         dbQuery('SELECT * FROM store ORDER BY name', [], (err,result)=>callback(err,result?.rows||[]));
    1051     },
    1052 
    1053     createOrderNew(data, callback) {
    1054         const items = data.items || data.products || data.order_items || [];
    1055         const storeId = data.store_id || data.storeId || (items[0]?.code ? String(items[0].code).slice(0,3) : null);
    1056         if (!storeId) return callback(new Error('Store ID is required'));
    1057         const year = String(new Date().getFullYear()).slice(-3);
    1058 
    1059         dbQuery(
    1060             `SELECT COUNT(*)::int AS n
    1061              FROM "order"
    1062              WHERE LEFT(order_num,3)=$1
    1063                AND SUBSTRING(order_num FROM 4 FOR 3)=$2`,
    1064             [storeId, year],
    1065             (countErr,countResult)=>{
    1066                 if (countErr) return callback(countErr);
    1067                 const seq=Number(countResult.rows[0].n)+1;
    1068                 const orderNum=`${storeId}${year}${String(seq).padStart(5,'0')}`;
    1069                 dbQuery(
    1070                     `INSERT INTO "order"(order_num,client_ID,status,last_date_mod,payment_method,discount)
    1071                      VALUES($1,$2,$3,CURRENT_TIMESTAMP,$4,$5) RETURNING order_num`,
    1072                     [orderNum,data.client_id,data.status||'placed order',data.payment_method||'cash',data.discount||0],
    1073                     (err,result)=>{
    1074                         if(err) return callback(err);
    1075                         let pending=items.length;
    1076                         if(!pending) return callback(null,orderNum);
    1077                         let firstErr=null;
    1078                         for(const item of items){
    1079                             dbQuery(
    1080                                 `INSERT INTO includes(order_num,product_code,quantity) VALUES($1,$2,$3)`,
    1081                                 [orderNum,item.product_code||item.code,item.quantity||1],
    1082                                 e=>{ if(e && !firstErr) firstErr=e; if(--pending===0) callback(firstErr,firstErr?undefined:orderNum); }
     957        });
     958    });
     959}
     960
     961// Function to create admin user with ID 000000
     962function createAdminUser() {
     963    const adminId = '000000';
     964    const adminPassword = bcrypt.hashSync('Admin123!', 10);
     965
     966    database.database.get(
     967        'SELECT * FROM users WHERE id = ? OR username = ? OR email = ?',
     968        [adminId, 'admin', 'admin@handcraft.com'],
     969        (err, existingAdmin) => {
     970            if (err) {
     971                console.error('Error checking for existing admin:', err.message);
     972                return;
     973            }
     974
     975            if (!existingAdmin) {
     976                // Start a transaction
     977                database.database.run('BEGIN TRANSACTION', (err) => {
     978                    if (err) {
     979                        console.error('Error beginning transaction:', err);
     980                        return;
     981                    }
     982
     983                    // Insert into users table
     984                    database.database.run(
     985                        `INSERT INTO users (id, username, email, password, user_type, force_password_change)
     986                         VALUES (?, ?, ?, ?, ?, ?)`,
     987                        [adminId, 'admin', 'admin@handcraft.com', adminPassword, 'admin', 1],
     988                        function(err) {
     989                            if (err) {
     990                                database.database.run('ROLLBACK');
     991                                console.error('Error inserting admin user:', err.message);
     992                                return;
     993                            }
     994
     995                            // Insert into personal table (required for boss table)
     996                            database.database.run(
     997                                `INSERT INTO personal (id, first_name, last_name, ssn, email, password)
     998                                 VALUES (?, ?, ?, ?, ?, ?)`,
     999                                [adminId, 'Admin', 'User', '0000000000000', 'admin@handcraft.com', adminPassword],
     1000                                function(err) {
     1001                                    if (err) {
     1002                                        database.database.run('ROLLBACK');
     1003                                        console.error('Error inserting admin personal:', err.message);
     1004                                        return;
     1005                                    }
     1006
     1007                                    // Insert into boss table (store owner)
     1008                                    database.database.run(
     1009                                        `INSERT INTO boss (boss_id, signature)
     1010                                         VALUES (?, ?)`,
     1011                                        [adminId, 'Admin Signature'],
     1012                                        function(err) {
     1013                                            if (err) {
     1014                                                database.database.run('ROLLBACK');
     1015                                                console.error('Error inserting admin boss:', err.message);
     1016                                                return;
     1017                                            }
     1018
     1019                                            // Insert into permissions
     1020                                            database.database.run(
     1021                                                `INSERT INTO permissions (personal_id, type, authorisation)
     1022                                                 VALUES (?, ?, ?)`,
     1023                                                [adminId, 'ADMIN', 'full_access'],
     1024                                                function(err) {
     1025                                                    if (err) {
     1026                                                        console.error('Error inserting admin permissions:', err.message);
     1027                                                        // Continue even if this fails
     1028                                                    }
     1029
     1030                                                    // Assign admin role
     1031                                                    database.database.get(
     1032                                                        'SELECT role_id FROM roles WHERE name = ?',
     1033                                                        ['admin'],
     1034                                                        (err, adminRole) => {
     1035                                                            if (!err && adminRole) {
     1036                                                                database.database.run(
     1037                                                                    'INSERT OR IGNORE INTO user_roles (user_id, role_id) VALUES (?, ?)',
     1038                                                                    [adminId, adminRole.role_id],
     1039                                                                    (err) => {
     1040                                                                        if (err) {
     1041                                                                            console.error('Error assigning admin role:', err.message);
     1042                                                                        }
     1043                                                                    }
     1044                                                                );
     1045                                                            }
     1046
     1047                                                            database.database.run('COMMIT', (commitErr) => {
     1048                                                                if (commitErr) {
     1049                                                                    console.error('Error committing transaction:', commitErr);
     1050                                                                    database.database.run('ROLLBACK');
     1051                                                                } else {
     1052                                                                    console.log('\n');
     1053                                                                    console.log('šŸ” ===== ADMIN CREDENTIALS =====');
     1054                                                                    console.log('šŸ†” ID: 000000');
     1055                                                                    console.log('šŸ‘¤ Username: admin');
     1056                                                                    console.log('šŸ“§ Email: admin@handcraft.com');
     1057                                                                    console.log('šŸ”‘ Password: Admin123!');
     1058                                                                    console.log('āš ļø This is a first-time login. You will be required to change your password after 2FA verification.');
     1059                                                                    console.log('================================\n');
     1060                                                                }
     1061                                                            });
     1062                                                        }
     1063                                                    );
     1064                                                }
     1065                                            );
     1066                                        }
     1067                                    );
     1068                                }
    10831069                            );
    10841070                        }
    1085                     }
    1086                 );
    1087             }
    1088         );
    1089     },
    1090 
    1091     getOrdersByClient(clientId, callback) {
    1092         dbQuery(
    1093             `SELECT o.*, LEFT(o.order_num,3) AS store_id,
    1094                     o.last_date_mod AS order_date,
    1095                     COALESCE(json_agg(json_build_object('product_code',i.product_code,'quantity',i.quantity,'price',p.price))
    1096                              FILTER(WHERE i.product_code IS NOT NULL),'[]') AS items
    1097              FROM "order" o
    1098              LEFT JOIN includes i ON i.order_num=o.order_num
    1099              LEFT JOIN product p ON p.code=i.product_code
    1100              WHERE o.client_ID=$1
    1101              GROUP BY o.order_num
    1102              ORDER BY o.last_date_mod DESC`,
    1103             [clientId],(err,result)=>callback(err,result?.rows||[])
    1104         );
    1105     },
    1106 
    1107     createReviewNew(data, callback) {
    1108         dbQuery(
    1109             `INSERT INTO review(order_num,comment,rating,last_mod_date)
    1110              VALUES($1,$2,$3,CURRENT_TIMESTAMP)
    1111              RETURNING order_num`,
    1112             [data.order_num,data.comment||null,data.rating],
    1113             (err,result)=>callback(err,result?.rows?.[0]?.order_num)
    1114         );
    1115     },
    1116 
    1117     createRequest(data, callback) {
    1118         dbQuery(
    1119             `INSERT INTO request(request_num,date_and_time,problem,notes_of_communication,customer_satisfaction)
    1120              VALUES($1,$2,$3,$4,0) RETURNING request_num`,
    1121             [data.request_num,data.date_and_time,data.problem,data.notes_of_communication||null],
    1122             (err,result)=>{
    1123                 if (err) return callback(err);
    1124                 dbQuery(
    1125                     `INSERT INTO for_store(request_num,store_ID) VALUES($1,$2)`,
    1126                     [data.request_num,data.store_id],
    1127                     storeErr=>{
    1128                         if (storeErr) return callback(storeErr);
    1129                         if (data.order_num) {
    1130                             dbQuery(
    1131                                 `INSERT INTO makes_request(client_ID,order_num) VALUES($1,$2)`,
    1132                                 [data.client_id,data.order_num],
    1133                                 e=>callback(e,data.request_num)
    1134                             );
    1135                         } else {
    1136                             callback(null,data.request_num);
    1137                         }
    1138                     }
    1139                 );
    1140             }
    1141         );
    1142     },
    1143 
    1144     createRefund(data, callback) {
    1145         const suppliedId = data.refund_id;
    1146         const query = suppliedId
    1147             ? `INSERT INTO refund(refund_id,order_num,reason,amount,status) VALUES($1,$2,$3,$4,$5) RETURNING refund_id`
    1148             : `INSERT INTO refund(order_num,reason,amount,status) VALUES($1,$2,$3,$4) RETURNING refund_id`;
    1149         const params = suppliedId
    1150             ? [Number.parseInt(String(suppliedId),10) || undefined,data.order_num,data.reason||null,data.amount,data.status||'requested refund']
    1151             : [data.order_num,data.reason||null,data.amount,data.status||'requested refund'];
    1152         dbQuery(query,params,(err,result)=>callback(err,result?.rows?.[0]?.refund_id));
    1153     },
    1154 
    1155     getAllUsers(callback) {
    1156         dbQuery(`SELECT u.*,COALESCE(json_agg(r.name) FILTER(WHERE r.role_id IS NOT NULL),'[]') roles
    1157                  FROM users u LEFT JOIN user_roles ur ON ur.user_id=u.id
    1158                               LEFT JOIN roles r ON r.role_id=ur.role_id GROUP BY u.id ORDER BY u.created_at DESC`,
    1159             [],(err,result)=>callback(err,result?.rows||[]));
    1160     },
    1161 
    1162     getAllOrders(callback) {
    1163         dbQuery(`SELECT o.*, LEFT(o.order_num,3) AS store_id, o.last_date_mod AS order_date,
    1164                         c.first_name,c.last_name,c.email
    1165                  FROM "order" o
    1166                  LEFT JOIN client c ON c.client_id=o.client_ID
    1167                  ORDER BY o.last_date_mod DESC`,
    1168             [],(err,result)=>callback(err,result?.rows||[]));
    1169     },
    1170 
    1171     getStoreProducts(storeId, callback) {
    1172         dbQuery(`SELECT p.*,c.name AS category_name,LEFT(p.code,3) AS store_id,s.discount
    1173                  FROM product p
    1174                  LEFT JOIN category c ON c.id=p.category_id
    1175                  LEFT JOIN sells s ON s.product_code=p.code AND s.store_ID=$1
    1176                  WHERE LEFT(p.code,3)=$1 OR EXISTS(SELECT 1 FROM sells sx WHERE sx.product_code=p.code AND sx.store_ID=$1)
    1177                  ORDER BY p.code`,[storeId],(err,result)=>callback(err,result?.rows||[]));
    1178     },
    1179 
    1180     getStoreOrders(storeId, callback) {
    1181         dbQuery(`SELECT o.*,LEFT(o.order_num,3) AS store_id,o.last_date_mod AS order_date,c.first_name,c.last_name
    1182                  FROM "order" o
    1183                  LEFT JOIN client c ON c.client_id=o.client_ID
    1184                  WHERE LEFT(o.order_num,3)=$1 ORDER BY o.last_date_mod DESC`,[storeId],
    1185             (err,result)=>callback(err,result?.rows||[]));
    1186     },
    1187 
    1188     getStoreEmployees(storeId, callback) {
    1189         dbQuery(`SELECT p.*,e.date_of_hire,per.type,per.authorisation
    1190                  FROM personal p JOIN works_in_store w ON w.personal_id=p.id
    1191                  LEFT JOIN employees e ON e.employee_id=p.id
    1192                  LEFT JOIN permissions per ON per.personal_is=p.id
    1193                  WHERE w.store_ID=$1 ORDER BY p.last_name,p.first_name`,
    1194             [storeId],(err,result)=>callback(err,result?.rows||[]));
    1195     },
    1196 
    1197     getStoreReports(storeId, callback) {
    1198         dbQuery(`SELECT * FROM report WHERE store_ID=$1 ORDER BY date DESC`,[storeId],
    1199             (err,result)=>callback(err,result?.rows||[]));
    1200     },
    1201 
    1202     getStoreStats(storeId, callback) {
    1203         const sql=`SELECT
    1204             (SELECT COUNT(*) FROM product WHERE LEFT(code,3)=$1)::int AS product_count,
    1205             (SELECT COUNT(*) FROM "order" WHERE LEFT(order_num,3)=$1)::int AS order_count,
    1206             (SELECT COALESCE(SUM(i.quantity*p.price*(1-o.discount/100)),0)
    1207              FROM "order" o JOIN includes i ON i.order_num=o.order_num JOIN product p ON p.code=i.product_code
    1208              WHERE LEFT(o.order_num,3)=$1) AS revenue,
    1209             (SELECT COUNT(*) FROM works_in_store WHERE store_ID=$1)::int AS employee_count,
    1210             (SELECT COUNT(*) FROM for_store WHERE store_ID=$1)::int AS request_count,
    1211             (SELECT COUNT(*) FROM refund r JOIN "order" o ON o.order_num=r.order_num WHERE LEFT(o.order_num,3)=$1)::int AS refund_count`;
    1212         dbQuery(sql,[storeId],(err,result)=>callback(err,result?.rows?.[0]||{}));
    1213     },
    1214 
    1215     getEmployeeTasks(personalId, storeId, callback) {
    1216         dbQuery(`SELECT r.*,a.personal_id AS answered_by
    1217                  FROM request r
    1218                  JOIN for_store fs ON fs.request_num=r.request_num
    1219                  LEFT JOIN answers a ON a.request_num=r.request_num
    1220                  WHERE fs.store_ID=$1 AND (a.personal_id=$2 OR a.personal_id IS NULL)
    1221                  ORDER BY r.date_and_time DESC`,
    1222             [storeId,personalId],(err,result)=>callback(err,result?.rows||[]));
    1223     },
    1224 
    1225     getClientStats(clientId, callback) {
    1226         dbQuery(`SELECT
    1227             (SELECT COUNT(*) FROM "order" WHERE client_ID=$1)::int AS order_count,
    1228             (SELECT COUNT(*) FROM review r JOIN "order" o ON o.order_num=r.order_num WHERE o.client_ID=$1)::int AS review_count,
    1229             (SELECT COUNT(*) FROM makes_request WHERE client_ID=$1)::int AS request_count,
    1230             (SELECT COUNT(*) FROM refund r JOIN "order" o ON o.order_num=r.order_num WHERE o.client_ID=$1)::int AS refund_count`,
    1231             [clientId],(err,result)=>callback(err,result?.rows?.[0]||{}));
    1232     }
    1233 };
    1234 
    1235 
    1236 
    1237 // PostgreSQL schema initialization.
    1238 // The schema is based on the supplied Pasted markdown.md. A few invalid PostgreSQL
    1239 // declarations in the original paste are corrected here (for example DECIMMAL,
    1240 // PARTIAL KEY, and composite-key column-name typos). The existing HTTP layer also
    1241 // uses only the project schema plus the four authentication/audit support tables.
    1242 (async () => {
     1071                    );
     1072                });
     1073            } else {
     1074                console.log('āœ… Admin user already exists with ID:', existingAdmin.id);
     1075            }
     1076        }
     1077    );
     1078}
     1079
     1080// Initialize database on startup
     1081(async function() {
    12431082    try {
    1244         await database.initializeDatabase();
     1083        await initializeDatabase();
    12451084        console.log('āœ… Database initialization completed');
    12461085    } catch (err) {
    12471086        console.error('āŒ Database initialization failed:', err);
    1248         process.exitCode = 1;
    12491087    }
    12501088})();
    … …  
    13201158
    13211159                database.database.get(
    1322                     'SELECT boss_id FROM boss WHERE boss_id = $1',
     1160                    'SELECT boss_id FROM boss WHERE boss_id = ?',
    13231161                    [personalId],
    13241162                    (err, boss) => {
    … …  
    15421380
    15431381                database.database.get(
    1544                     'SELECT store_id FROM store WHERE store_email = $1',
     1382                    'SELECT store_id FROM store WHERE store_email = ?',
    15451383                    [formData.storeEmail],
    15461384                    (err, existingStore) => {
    … …  
    19221760                    // Insert into store table (store_id is VARCHAR)
    19231761                    database.database.run(
    1924                         'INSERT INTO store (store_id, name, date_of_founding, physical_address, store_email, rating) VALUES ($1, $2, $3, $4, $5, $6)',
     1762                        'INSERT INTO store (store_id, name, date_of_founding, physical_address, store_email, rating) VALUES (?, ?, ?, ?, ?, ?)',
    19251763                        [
    19261764                            tempStoreData.storeId,
    … …  
    19421780                            // Insert into personal table (id is VARCHAR)
    19431781                            database.database.run(
    1944                                 'INSERT INTO personal (id, first_name, last_name, ssn, email, password) VALUES ($1, $2, $3, $4, $5, $6)',
     1782                                'INSERT INTO personal (id, first_name, last_name, ssn, email, password) VALUES (?, ?, ?, ?, ?, ?)',
    19451783                                [
    19461784                                    tempStoreData.personalId,
    … …  
    19711809                                    // Insert into boss table (boss_id is VARCHAR, references personal.id)
    19721810                                    database.database.run(
    1973                                         'INSERT INTO boss (boss_id) VALUES ($1)',
    1974                                         [tempStoreData.personalId],
     1811                                        'INSERT INTO boss (boss_id, signature) VALUES (?, ?)',
     1812                                        [tempStoreData.personalId, tempStoreData.signature],
    19751813                                        (err) => {
    19761814                                            if (err) {
    … …  
    19841822                                            // Insert into works_in_store table (personal_id is VARCHAR, store_id is VARCHAR)
    19851823                                            database.database.run(
    1986                                                 'INSERT INTO works_in_store (personal_id, store_id) VALUES ($1, $2)',
     1824                                                'INSERT INTO works_in_store (personal_id, store_id) VALUES (?, ?)',
    19871825                                                [tempStoreData.personalId, tempStoreData.storeId],
    19881826                                                (err) => {
    … …  
    19971835                                                    // Insert into permissions table (personal_id is VARCHAR)
    19981836                                                    database.database.run(
    1999                                                         'INSERT INTO permissions (personal_id, type, authorisation) VALUES ($1, $2, $3)',
     1837                                                        'INSERT INTO permissions (personal_id, type, authorisation) VALUES (?, ?, ?)',
    20001838                                                        [tempStoreData.personalId, 'BOSS', 'full_access'],
    20011839                                                        (err) => {
    … …  
    20061844                                                            // Also create entry in users table for login with force_password_change = 1
    20071845                                                            database.database.run(
    2008                                                                 'INSERT INTO users (id, username, email, password, user_type, force_password_change) VALUES ($1, $2, $3, $4, $5, $6)',
     1846                                                                'INSERT INTO users (id, username, email, password, user_type, force_password_change) VALUES (?, ?, ?, ?, ?, ?)',
    20091847                                                                [
    20101848                                                                    tempStoreData.personalId,
    … …  
    20991937                        if (tempUserData.address && tempUserData.city && tempUserData.postcode && tempUserData.country) {
    21001938                            database.database.run(
    2101                                 'INSERT INTO delivery_address (client_id, address, city, postcode, country, is_default) VALUES ($1, $2, $3, $4, $5, $6)',
     1939                                'INSERT INTO delivery_address (client_id, address, city, postcode, country, is_default) VALUES (?, ?, ?, ?, ?, ?)',
    21021940                                [
    21031941                                    clientId,
    … …  
    23202158                            // Check if this is a boss (store owner)
    23212159                            database.database.get(
    2322                                 'SELECT boss_id FROM boss WHERE boss_id = $1',
     2160                                'SELECT boss_id FROM boss WHERE boss_id = ?',
    23232161                                [personal.id],
    23242162                                (err, boss) => {
    … …  
    23312169                                        // Check if first time login from users table
    23322170                                        database.database.get(
    2333                                             'SELECT force_password_change FROM users WHERE email = $1',
     2171                                            'SELECT force_password_change FROM users WHERE email = ?',
    23342172                                            [email],
    23352173                                            (err, user) => {
    … …  
    23812219                                    // Check if this is an employee
    23822220                                    database.database.get(
    2383                                         'SELECT employee_id FROM employees WHERE employee_id = $1',
     2221                                        'SELECT employee_id FROM employees WHERE employee_id = ?',
    23842222                                        [personal.id],
    23852223                                        (err, employee) => {
    … …  
    23912229                                                // This is an employee
    23922230                                                database.database.get(
    2393                                                     'SELECT force_password_change FROM users WHERE email = $1',
     2231                                                    'SELECT force_password_change FROM users WHERE email = ?',
    23942232                                                    [email],
    23952233                                                    (err, user) => {
    … …  
    24422280                                            // Treat as regular user
    24432281                                            database.database.get(
    2444                                                 'SELECT * FROM users WHERE email = $1',
     2282                                                'SELECT * FROM users WHERE email = ?',
    24452283                                                [email],
    24462284                                                (err, user) => {
    … …  
    25842422
    25852423            database.database.get(
    2586                 'SELECT * FROM users WHERE email = $1',
     2424                'SELECT * FROM users WHERE email = ?',
    25872425                [email],
    25882426                (err, user) => {
    … …  
    28352673                                // Personal user (store owner/employee)
    28362674                                database.database.get(
    2837                                     'SELECT boss_id FROM boss WHERE boss_id = $1',
     2675                                    'SELECT boss_id FROM boss WHERE boss_id = ?',
    28382676                                    [userId],
    28392677                                    (err, boss) => {
    … …  
    29362774
    29372775                    database.database.get(
    2938                         'SELECT boss_id FROM boss WHERE boss_id = $1',
     2776                        'SELECT boss_id FROM boss WHERE boss_id = ?',
    29392777                        [personalId],
    29402778                        (err, boss) => {
    … …  
    29462784                                database.database.all(
    29472785                                    `SELECT s.* FROM store s
    2948                                                          JOIN works_in_store w ON s.store_id = w.store_id
    2949                                      WHERE w.personal_id = $1`,
     2786                                     JOIN works_in_store w ON s.store_id = w.store_id
     2787                                     WHERE w.personal_id = ?`,
    29502788                                    [personalId],
    29512789                                    (err, stores) => {
    … …  
    29712809                            } else {
    29722810                                database.database.get(
    2973                                     'SELECT employee_id FROM employees WHERE employee_id = $1',
     2811                                    'SELECT employee_id FROM employees WHERE employee_id = ?',
    29742812                                    [personalId],
    29752813                                    (err, employee) => {
    … …  
    29812819                                            database.database.all(
    29822820                                                `SELECT s.* FROM store s
    2983                                                                      JOIN works_in_store w ON s.store_id = w.store_id
    2984                                                  WHERE w.personal_id = $1`,
     2821                                                 JOIN works_in_store w ON s.store_id = w.store_id
     2822                                                 WHERE w.personal_id = ?`,
    29852823                                                [personalId],
    29862824                                                (err, stores) => {
    … …  
    31062944                                id: category.id,
    31072945                                name: category.name,
    3108                                 parent_id: category.parent_category_id,
     2946                                parent_id: category.parent_id,
    31092947                                description: category.description
    31102948                            }
    … …  
    31723010
    31733011                    database.database.get(
    3174                         'SELECT COUNT(*) AS order_count FROM "order" WHERE LEFT(order_num,3) = $1 AND SUBSTRING(order_num FROM 4 FOR 3)::INTEGER = ($2::INTEGER % 1000)',
     3012                        'SELECT COUNT(*) as order_count FROM "order" WHERE store_id = ? AND strftime("%Y", order_date) = ?',
    31753013                        [storeId, new Date().getFullYear().toString()],
    31763014                        (err, result) => {
    … …  
    33033141
    33043142                    database.database.get(
    3305                         'SELECT COUNT(*)::int AS request_count FROM request r JOIN for_store fs ON fs.request_num=r.request_num WHERE fs.store_ID = $1 AND EXTRACT(YEAR FROM r.date_and_time)::int = $2::int AND EXTRACT(MONTH FROM r.date_and_time)::int = $3::int',
     3143                        'SELECT COUNT(*) as request_count FROM request WHERE store_id = ? AND strftime("%Y", date_and_time) = ? AND strftime("%m", date_and_time) = ?',
    33063144                        [storeId, now.getFullYear().toString(), (now.getMonth() + 1).toString().padStart(2, '0')],
    33073145                        (err, result) => {
    … …  
    33613199
    33623200                    database.database.get(
    3363                         'SELECT LEFT(order_num,3) AS store_id FROM "order" WHERE order_num = $1',
     3201                        'SELECT store_id FROM "order" WHERE order_num = ?',
    33643202                        [refundData.order_num],
    33653203                        (err, result) => {
    … …  
    33763214
    33773215                            database.database.get(
    3378                                 'SELECT COUNT(*)::int AS refund_count FROM refund WHERE SUBSTRING(refund_id::text FROM 4 FOR 2)::int = $2::int AND SUBSTRING(refund_id::text FROM 6 FOR 3)::int = ($1::int % 1000)',
     3216                                'SELECT COUNT(*) as refund_count FROM refund WHERE strftime("%Y", request_date) = ? AND strftime("%m", request_date) = ?',
    33793217                                [now.getFullYear().toString(), (now.getMonth() + 1).toString().padStart(2, '0')],
    33803218                                (err, result) => {
    … …  
    34263264
    34273265                database.database.get(
    3428                     'SELECT store_id FROM works_in_store WHERE personal_id = $1',
     3266                    'SELECT store_id FROM works_in_store WHERE personal_id = ?',
    34293267                    [personalId],
    34303268                    (err, bossStore) => {
    … …  
    34443282
    34453283                        database.database.get(
    3446                             'SELECT store_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',
     3284                            'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
    34473285                            [personalId, storeId],
    34483286                            (err, ownsStore) => {
    … …  
    34533291                                }
    34543292
    3455                                 // PostgreSQL: extract the numeric product sequence from the store-prefixed product code
     3293                                // FIXED: Changed SQL syntax from SUBSTRING(code FROM 4) to SUBSTR(code, 4) for SQLite compatibility
    34563294                                database.database.get(
    3457                                     'SELECT MAX(CAST(SUBSTRING(code FROM 4) AS INTEGER)) AS max_product_num FROM product WHERE LEFT(code,3) = $1',
     3295                                    'SELECT MAX(CAST(SUBSTR(code, 4) AS INTEGER)) as max_product_num FROM product WHERE store_id = ?',
    34583296                                    [storeId],
    34593297                                    (err, result) => {
    … …  
    35243362
    35253363                database.database.get(
    3526                     'SELECT LEFT(code,3) AS store_id FROM product WHERE code = $1',
     3364                    'SELECT store_id FROM product WHERE code = ?',
    35273365                    [productData.code],
    35283366                    (err, product) => {
    … …  
    35343372
    35353373                        database.database.get(
    3536                             'SELECT store_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',
     3374                            'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
    35373375                            [personalId, product.store_id],
    35383376                            (err, ownsStore) => {
    … …  
    36613499
    36623500                        database.database.run(
    3663                             'UPDATE users SET password = $1, force_password_change = FALSE WHERE id = $2',
     3501                            'UPDATE users SET password = ?, force_password_change = 0 WHERE id = ?',
    36643502                            [hashedPassword, userId],
    36653503                            function(err) {
    … …  
    36733511                                // Also update password in personal table if it exists (for admin)
    36743512                                database.database.run(
    3675                                     'UPDATE personal SET password = $1 WHERE id = $2',
     3513                                    'UPDATE personal SET password = ? WHERE id = ?',
    36763514                                    [hashedPassword, userId],
    36773515                                    function(err) {
    … …  
    37473585
    37483586                                database.database.run(
    3749                                     'UPDATE personal SET password = $1 WHERE id = $2',
     3587                                    'UPDATE personal SET password = ? WHERE id = ?',
    37503588                                    [hashedPassword, userId],
    37513589                                    function(err) {
    … …  
    37593597                                        // Also update in users table if exists
    37603598                                        database.database.run(
    3761                                             'UPDATE users SET password = $1, force_password_change = FALSE WHERE email = $2',
     3599                                            'UPDATE users SET password = ?, force_password_change = 0 WHERE email = ?',
    37623600                                            [hashedPassword, personal.email],
    37633601                                            function(err) {
    … …  
    37703608                                        // Determine user type (boss/owner or employee)
    37713609                                        database.database.get(
    3772                                             'SELECT boss_id FROM boss WHERE boss_id = $1',
     3610                                            'SELECT boss_id FROM boss WHERE boss_id = ?',
    37733611                                            [userId],
    37743612                                            (err, boss) => {
    … …  
    38463684
    38473685            database.database.get(
    3848                 'SELECT boss_id FROM boss WHERE boss_id = $1',
     3686                'SELECT boss_id FROM boss WHERE boss_id = ?',
    38493687                [personalId],
    38503688                (err, boss) => {
    … …  
    39523790
    39533791                                        database.database.run(
    3954                                             'INSERT INTO personal (id, first_name, last_name, ssn, email, password) VALUES ($1, $2, $3, $4, $5, $6)',
     3792                                            'INSERT INTO personal (id, first_name, last_name, ssn, email, password) VALUES (?, ?, ?, ?, ?, ?)',
    39553793                                            [
    39563794                                                newPersonalId,
    … …  
    39803818
    39813819                                                database.database.run(
    3982                                                     'INSERT INTO employees (employee_id, date_of_hire) VALUES ($1, $2)',
     3820                                                    'INSERT INTO employees (employee_id, date_of_hire) VALUES (?, ?)',
    39833821                                                    [newPersonalId, dateOfHire],
    39843822                                                    (err) => {
    … …  
    39923830
    39933831                                                        database.database.run(
    3994                                                             'INSERT INTO works_in_store (personal_id, store_id) VALUES ($1, $2)',
     3832                                                            'INSERT INTO works_in_store (personal_id, store_id) VALUES (?, ?)',
    39953833                                                            [newPersonalId, storeId],
    39963834                                                            (err) => {
    … …  
    40043842
    40053843                                                                database.database.run(
    4006                                                                     'INSERT INTO permissions (personal_id, type, authorisation) VALUES ($1, $2, $3)',
     3844                                                                    'INSERT INTO permissions (personal_id, type, authorisation) VALUES (?, ?, ?)',
    40073845                                                                    [newPersonalId, 'EMPLOYEE', 'limited_access'],
    40083846                                                                    (err) => {
    … …  
    40133851                                                                        // Also create entry in users table for login with force_password_change = 1
    40143852                                                                        database.database.run(
    4015                                                                             'INSERT INTO users (id, username, email, password, user_type, force_password_change) VALUES ($1, $2, $3, $4, $5, $6)',
     3853                                                                            'INSERT INTO users (id, username, email, password, user_type, force_password_change) VALUES (?, ?, ?, ?, ?, ?)',
    40163854                                                                            [
    40173855                                                                                newPersonalId,
    … …  
    40863924
    40873925            database.database.get(
    4088                 'SELECT boss_id FROM boss WHERE boss_id = $1',
     3926                'SELECT boss_id FROM boss WHERE boss_id = ?',
    40893927                [personalId],
    40903928                (err, boss) => {
    … …  
    41093947
    41103948                        database.database.get(
    4111                             'SELECT store_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',
     3949                            'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
    41123950                            [personalId, storeId],
    41133951                            (err, bossStore) => {
    … …  
    41193957
    41203958                                database.database.get(
    4121                                     'SELECT personal_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',
     3959                                    'SELECT personal_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
    41223960                                    [employeeId, storeId],
    41233961                                    (err, employeeStore) => {
    … …  
    41293967
    41303968                                        database.database.get(
    4131                                             'SELECT boss_id FROM boss WHERE boss_id = $1',
     3969                                            'SELECT boss_id FROM boss WHERE boss_id = ?',
    41323970                                            [employeeId],
    41333971                                            (err, isBoss) => {
    … …  
    41513989
    41523990                                                    database.database.run(
    4153                                                         'DELETE FROM works_in_store WHERE personal_id = $1 AND store_id = $2',
     3991                                                        'DELETE FROM works_in_store WHERE personal_id = ? AND store_id = ?',
    41543992                                                        [employeeId, storeId],
    41553993                                                        (err) => {
    … …  
    41634001
    41644002                                                            database.database.run(
    4165                                                                 'DELETE FROM employees WHERE employee_id = $1',
     4003                                                                'DELETE FROM employees WHERE employee_id = ?',
    41664004                                                                [employeeId],
    41674005                                                                (err) => {
    … …  
    41714009
    41724010                                                                    database.database.run(
    4173                                                                         'DELETE FROM permissions WHERE personal_id = $1',
     4011                                                                        'DELETE FROM permissions WHERE personal_id = ?',
    41744012                                                                        [employeeId],
    41754013                                                                        (err) => {
    … …  
    41794017
    41804018                                                                            database.database.run(
    4181                                                                                 'DELETE FROM personal WHERE id = $1',
     4019                                                                                'DELETE FROM personal WHERE id = ?',
    41824020                                                                                [employeeId],
    41834021                                                                                (err) => {
    … …  
    41884026                                                                                    // Also delete from users table
    41894027                                                                                    database.database.run(
    4190                                                                                         'DELETE FROM users WHERE id = $1',
     4028                                                                                        'DELETE FROM users WHERE id = ?',
    41914029                                                                                        [employeeId],
    41924030                                                                                        (err) => {
    … …  
    42554093
    42564094            database.database.get(
    4257                 'SELECT boss_id FROM boss WHERE boss_id = $1',
     4095                'SELECT boss_id FROM boss WHERE boss_id = ?',
    42584096                [personalId],
    42594097                (err, boss) => {
    … …  
    42784116
    42794117                        database.database.get(
    4280                             'SELECT store_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',
     4118                            'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
    42814119                            [personalId, storeId],
    42824120                            (err, bossStore) => {
    … …  
    42884126
    42894127                                database.database.get(
    4290                                     'SELECT personal_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',
     4128                                    'SELECT personal_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
    42914129                                    [employeeId, storeId],
    42924130                                    (err, employeeStore) => {
    … …  
    43124150
    43134151                                        database.database.run(
    4314                                             'UPDATE permissions SET type = $1, authorisation = $2 WHERE personal_id = $3',
     4152                                            'UPDATE permissions SET type = ?, authorisation = ? WHERE personal_id = ?',
    43154153                                            [permissionType, authorization, employeeId],
    43164154                                            function(err) {
    … …  
    43614199
    43624200            database.database.get(
    4363                 'SELECT boss_id FROM boss WHERE boss_id = $1',
     4201                'SELECT boss_id FROM boss WHERE boss_id = ?',
    43644202                [personalId],
    43654203                (err, boss) => {
    … …  
    43844222
    43854223                        database.database.get(
    4386                             'SELECT store_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',
     4224                            'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
    43874225                            [personalId, storeId],
    43884226                            (err, bossStore) => {
    … …  
    43944232
    43954233                                database.database.get(
    4396                                     'SELECT personal_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',
     4234                                    'SELECT personal_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
    43974235                                    [employeeId, storeId],
    43984236                                    (err, employeeStore) => {
    … …  
    44074245
    44084246                                        if (firstName) {
    4409                                             updates.push(`first_name = $${params.length + 1}`);
     4247                                            updates.push('first_name = ?');
    44104248                                            params.push(firstName);
    44114249                                        }
    44124250
    44134251                                        if (lastName) {
    4414                                             updates.push(`last_name = $${params.length + 1}`);
     4252                                            updates.push('last_name = ?');
    44154253                                            params.push(lastName);
    44164254                                        }
    … …  
    44224260                                                return;
    44234261                                            }
    4424                                             updates.push(`email = $${params.length + 1}`);
     4262                                            updates.push('email = ?');
    44254263                                            params.push(email);
    44264264                                        }
    … …  
    44354273
    44364274                                        database.database.run(
    4437                                             `UPDATE personal SET ${updates.join(', ')} WHERE id = $${params.length}`,
     4275                                            `UPDATE personal SET ${updates.join(', ')} WHERE id = ?`,
    44384276                                            params,
    44394277                                            function(err) {
    … …  
    44484286                                                if (email) {
    44494287                                                    database.database.run(
    4450                                                         'UPDATE users SET email = $1 WHERE id = $2',
     4288                                                        'UPDATE users SET email = ? WHERE id = ?',
    44514289                                                        [email, employeeId],
    44524290                                                        (err) => {
    … …  
    44604298                                                if (firstName || lastName) {
    44614299                                                    database.database.get(
    4462                                                         'SELECT first_name, last_name FROM personal WHERE id = $1',
     4300                                                        'SELECT first_name, last_name FROM personal WHERE id = ?',
    44634301                                                        [employeeId],
    44644302                                                        (err, personal) => {
    … …  
    44664304                                                                const newUsername = `${personal.first_name} ${personal.last_name}`;
    44674305                                                                database.database.run(
    4468                                                                     'UPDATE users SET username = $1 WHERE id = $2',
     4306                                                                    'UPDATE users SET username = ? WHERE id = ?',
    44694307                                                                    [newUsername, employeeId],
    44704308                                                                    (err) => {
    … …  
    45044342            if (!storeId) {
    45054343                database.database.get(
    4506                     'SELECT store_id FROM works_in_store WHERE personal_id = $1 LIMIT 1',
     4344                    'SELECT store_id FROM works_in_store WHERE personal_id = ? LIMIT 1',
    45074345                    [personalId],
    45084346                    (err, store) => {
    … …  
    45294367
    45304368            database.database.get(
    4531                 'SELECT store_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',
     4369                'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
    45324370                [personalId, storeId],
    45334371                (err, ownsStore) => {
    … …  
    45584396            if (!storeId) {
    45594397                database.database.get(
    4560                     'SELECT store_id FROM works_in_store WHERE personal_id = $1 LIMIT 1',
     4398                    'SELECT store_id FROM works_in_store WHERE personal_id = ? LIMIT 1',
    45614399                    [personalId],
    45624400                    (err, store) => {
    … …  
    45834421
    45844422            database.database.get(
    4585                 'SELECT store_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',
     4423                'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
    45864424                [personalId, storeId],
    45874425                (err, ownsStore) => {
    … …  
    46124450            if (!storeId) {
    46134451                database.database.get(
    4614                     'SELECT store_id FROM works_in_store WHERE personal_id = $1 LIMIT 1',
     4452                    'SELECT store_id FROM works_in_store WHERE personal_id = ? LIMIT 1',
    46154453                    [personalId],
    46164454                    (err, store) => {
    … …  
    46374475
    46384476            database.database.get(
    4639                 'SELECT store_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',
     4477                'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
    46404478                [personalId, storeId],
    46414479                (err, ownsStore) => {
    … …  
    46664504            if (!storeId) {
    46674505                database.database.get(
    4668                     'SELECT store_id FROM works_in_store WHERE personal_id = $1 LIMIT 1',
     4506                    'SELECT store_id FROM works_in_store WHERE personal_id = ? LIMIT 1',
    46694507                    [personalId],
    46704508                    (err, store) => {
    … …  
    46914529
    46924530            database.database.get(
    4693                 'SELECT store_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',
     4531                'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
    46944532                [personalId, storeId],
    46954533                (err, ownsStore) => {
    … …  
    47204558            if (!storeId) {
    47214559                database.database.get(
    4722                     'SELECT store_id FROM works_in_store WHERE personal_id = $1 LIMIT 1',
     4560                    'SELECT store_id FROM works_in_store WHERE personal_id = ? LIMIT 1',
    47234561                    [personalId],
    47244562                    (err, store) => {
    … …  
    47454583
    47464584            database.database.get(
    4747                 'SELECT store_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',
     4585                'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
    47484586                [personalId, storeId],
    47494587                (err, ownsStore) => {
    … …  
    48464684
    48474685                database.database.get(
    4848                     'SELECT store_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',
     4686                    'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
    48494687                    [personalId, storeId],
    48504688                    (err, ownsStore) => {
    … …  
    49154753
    49164754                database.database.get(
    4917                     'SELECT store_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',
     4755                    'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
    49184756                    [personalId, storeId],
    49194757                    (err, ownsStore) => {
    … …  
    49274765
    49284766                        database.database.run(
    4929                             'INSERT INTO report (date, store_ID, overall_profit, sales_trend, marketing_growth, owner_signature) VALUES (CURRENT_TIMESTAMP, $1, 0, $2, $3, $4)',
    4930                             [storeId, period, type, 'Not signed yet'],
     4767                            'INSERT INTO report (id, store_id, period, start_date, end_date, type, generated_by, generated_at) VALUES (?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)',
     4768                            [reportId, storeId, period, startDate, endDate, type, personalId],
    49314769                            function(err) {
    49324770                                if (err) {
  • view-database.js

    r6149556 r6fea37e  
    33
    44const dbPath = path.join(__dirname, 'database', 'handcraft.db');
    5 const db = new sqlite3.Database(dbPath, (err) => {
    6     if (err) {
    7         console.error('Error opening database:', err.message);
    8         process.exit(1);
    9     }
    10 });
     5const db = new sqlite3.Database(dbPath);
    116
    127console.log('\nšŸŽØ HANDCRAFT MARKETPLACE - DATABASE CONTENTS\n');
    … …  
    3025}
    3126
    32 // Main execution chain
    33 function executeChecks() {
    34     let currentCheck = 0;
    35 
    36     const checks = [
    37         { name: 'clients', func: checkClients },
    38         { name: 'personal', func: checkPersonal },
    39         { name: 'store', func: checkStore },
    40         { name: 'product', func: checkProduct },
    41         { name: 'category', func: checkCategory },
    42         { name: 'works_in_store', func: checkWorksInStore },
    43         { name: 'permissions', func: checkPermissions },
    44         { name: 'employees', func: checkEmployees },
    45         { name: 'boss', func: checkBoss },
    46         { name: 'order', func: checkOrder },
    47         { name: 'report', func: checkReport },
    48         { name: 'refund', func: checkRefund },
    49         { name: 'image', func: checkImage },
    50         { name: 'color', func: checkColor }
    51     ];
    52 
    53     function next() {
    54         currentCheck++;
    55         if (currentCheck < checks.length) {
    56             checks[currentCheck].func(next);
     27// Display CLIENTS table
     28console.log('\nšŸ‘¤ CLIENTS TABLE:');
     29console.log('================================================================================');
     30tableExists('client', (exists) => {
     31    if (!exists) {
     32        console.log('Table does not exist');
     33        checkNext();
     34        return;
     35    }
     36
     37    db.all('SELECT * FROM client', [], (err, rows) => {
     38        if (err) {
     39            console.log(`Error: ${err.message}`);
     40            checkNext();
     41            return;
     42        }
     43
     44        if (!rows || rows.length === 0) {
     45            console.log('No clients found');
    5746        } else {
    58             finish();
    59         }
    60     }
    61 
    62     // Start with first check
    63     checks[0].func(next);
    64 }
    65 
    66 // Display CLIENTS table
    67 function checkClients(next) {
    68     console.log('\nšŸ‘¤ CLIENTS TABLE:');
    69     console.log('================================================================================');
    70 
    71     tableExists('client', (exists) => {
    72         if (!exists) {
    73             console.log('Table does not exist\n');
    74             if (next) next();
    75             return;
    76         }
    77 
    78         db.all('SELECT * FROM client', [], (err, rows) => {
    79             if (err) {
    80                 console.log(`Error: ${err.message}\n`);
    81                 if (next) next();
    82                 return;
    83             }
    84 
    85             if (!rows || rows.length === 0) {
    86                 console.log('No clients found\n');
    87             } else {
    88                 console.log(`Total: ${rows.length} clients\n`);
    89                 rows.forEach((client, index) => {
    90                     console.log(`ID: ${client.client_id} | Name: ${client.first_name || ''} ${client.last_name || ''}`);
    91                     console.log(`Email: ${client.email || 'N/A'}`);
    92                     if (index < rows.length - 1) console.log('-'.repeat(80));
    93                 });
    94                 console.log(); // Add blank line after table
    95             }
    96             if (next) next();
    97         });
    98     });
    99 }
     47            console.log(`Total: ${rows.length} clients\n`);
     48            rows.forEach((client, index) => {
     49                console.log(`ID: ${client.client_id} | Name: ${client.first_name || ''} ${client.last_name || ''}`);
     50                console.log(`Email: ${client.email || 'N/A'}`);
     51                if (index < rows.length - 1) console.log('-'.repeat(80));
     52            });
     53        }
     54        checkNext();
     55    });
     56});
    10057
    10158// Display PERSONAL table
    102 function checkPersonal(next) {
     59function checkPersonal() {
    10360    console.log('\nšŸ‘” PERSONAL TABLE (STORE OWNERS/EMPLOYEES):');
    10461    console.log('================================================================================');
    105 
    10662    tableExists('personal', (exists) => {
    10763        if (!exists) {
    108             console.log('Table does not exist\n');
    109             if (next) next();
     64            console.log('Table does not exist');
     65            checkStore();
    11066            return;
    11167        }
    … …  
    12177        `, [], (err, rows) => {
    12278            if (err) {
    123                 console.log(`Error: ${err.message}\n`);
    124                 if (next) next();
    125                 return;
    126             }
    127 
    128             if (!rows || rows.length === 0) {
    129                 console.log('No personal records found\n');
     79                console.log(`Error: ${err.message}`);
     80                checkStore();
     81                return;
     82            }
     83
     84            if (!rows || rows.length === 0) {
     85                console.log('No personal records found');
    13086            } else {
    13187                console.log(`Total: ${rows.length} personal records\n`);
    … …  
    13591                    if (index < rows.length - 1) console.log('-'.repeat(80));
    13692                });
    137                 console.log();
    138             }
    139             if (next) next();
     93            }
     94            checkStore();
    14095        });
    14196    });
    … …  
    14398
    14499// Display STORE table
    145 function checkStore(next) {
     100function checkStore() {
    146101    console.log('\nšŸŖ STORE TABLE:');
    147102    console.log('================================================================================');
    148 
    149103    tableExists('store', (exists) => {
    150104        if (!exists) {
    151             console.log('Table does not exist\n');
    152             if (next) next();
     105            console.log('Table does not exist');
     106            checkProduct();
    153107            return;
    154108        }
    … …  
    156110        db.all('SELECT * FROM store', [], (err, rows) => {
    157111            if (err) {
    158                 console.log(`Error: ${err.message}\n`);
    159                 if (next) next();
    160                 return;
    161             }
    162 
    163             if (!rows || rows.length === 0) {
    164                 console.log('No stores found\n');
     112                console.log(`Error: ${err.message}`);
     113                checkProduct();
     114                return;
     115            }
     116
     117            if (!rows || rows.length === 0) {
     118                console.log('No stores found');
    165119            } else {
    166120                console.log(`Total: ${rows.length} stores\n`);
    … …  
    172126                    if (index < rows.length - 1) console.log('-'.repeat(80));
    173127                });
    174                 console.log();
    175             }
    176             if (next) next();
     128            }
     129            checkProduct();
    177130        });
    178131    });
    … …  
    180133
    181134// Display PRODUCT table
    182 function checkProduct(next) {
     135function checkProduct() {
    183136    console.log('\nšŸ›ļø PRODUCT TABLE:');
    184137    console.log('================================================================================');
    185 
    186138    tableExists('product', (exists) => {
    187139        if (!exists) {
    188             console.log('Table does not exist\n');
    189             if (next) next();
     140            console.log('Table does not exist');
     141            checkCategory();
    190142            return;
    191143        }
    … …  
    197149        `, [], (err, rows) => {
    198150            if (err) {
    199                 console.log(`Error: ${err.message}\n`);
    200                 if (next) next();
    201                 return;
    202             }
    203 
    204             if (!rows || rows.length === 0) {
    205                 console.log('No products found\n');
     151                console.log(`Error: ${err.message}`);
     152                checkCategory();
     153                return;
     154            }
     155
     156            if (!rows || rows.length === 0) {
     157                console.log('No products found');
    206158            } else {
    207159                console.log(`Total: ${rows.length} products\n`);
    … …  
    213165                    if (index < rows.length - 1) console.log('-'.repeat(80));
    214166                });
    215                 console.log();
    216             }
    217             if (next) next();
     167            }
     168            checkCategory();
    218169        });
    219170    });
    … …  
    221172
    222173// Display CATEGORY table
    223 function checkCategory(next) {
     174function checkCategory() {
    224175    console.log('\nšŸ“ CATEGORY TABLE:');
    225176    console.log('================================================================================');
    226 
    227177    tableExists('category', (exists) => {
    228178        if (!exists) {
    229             console.log('Table does not exist\n');
    230             if (next) next();
     179            console.log('Table does not exist');
     180            checkWorksInStore();
    231181            return;
    232182        }
    … …  
    238188        `, [], (err, rows) => {
    239189            if (err) {
    240                 console.log(`Error: ${err.message}\n`);
    241                 if (next) next();
    242                 return;
    243             }
    244 
    245             if (!rows || rows.length === 0) {
    246                 console.log('No categories found\n');
     190                console.log(`Error: ${err.message}`);
     191                checkWorksInStore();
     192                return;
     193            }
     194
     195            if (!rows || rows.length === 0) {
     196                console.log('No categories found');
    247197            } else {
    248198                console.log(`Total: ${rows.length} categories\n`);
    … …  
    253203                    if (index < rows.length - 1) console.log('-'.repeat(80));
    254204                });
    255                 console.log();
    256             }
    257             if (next) next();
     205            }
     206            checkWorksInStore();
    258207        });
    259208    });
    … …  
    261210
    262211// Display WORKS_IN_STORE table
    263 function checkWorksInStore(next) {
     212function checkWorksInStore() {
    264213    console.log('\nšŸ”— WORKS_IN_STORE TABLE:');
    265214    console.log('================================================================================');
    266 
    267215    tableExists('works_in_store', (exists) => {
    268216        if (!exists) {
    269             console.log('Table does not exist\n');
    270             if (next) next();
     217            console.log('Table does not exist');
     218            checkPermissions();
    271219            return;
    272220        }
    … …  
    279227        `, [], (err, rows) => {
    280228            if (err) {
    281                 console.log(`Error: ${err.message}\n`);
    282                 if (next) next();
    283                 return;
    284             }
    285 
    286             if (!rows || rows.length === 0) {
    287                 console.log('No assignments found\n');
     229                console.log(`Error: ${err.message}`);
     230                checkPermissions();
     231                return;
     232            }
     233
     234            if (!rows || rows.length === 0) {
     235                console.log('No assignments found');
    288236            } else {
    289237                console.log(`Total: ${rows.length} assignments\n`);
    … …  
    293241                    if (index < rows.length - 1) console.log('-'.repeat(80));
    294242                });
    295                 console.log();
    296             }
    297             if (next) next();
     243            }
     244            checkPermissions();
    298245        });
    299246    });
    … …  
    301248
    302249// Display PERMISSIONS table
    303 function checkPermissions(next) {
     250function checkPermissions() {
    304251    console.log('\nšŸ” PERMISSIONS TABLE:');
    305252    console.log('================================================================================');
    306 
    307253    tableExists('permissions', (exists) => {
    308254        if (!exists) {
    309             console.log('Table does not exist\n');
    310             if (next) next();
     255            console.log('Table does not exist');
     256            checkEmployees();
    311257            return;
    312258        }
    … …  
    318264        `, [], (err, rows) => {
    319265            if (err) {
    320                 console.log(`Error: ${err.message}\n`);
    321                 if (next) next();
    322                 return;
    323             }
    324 
    325             if (!rows || rows.length === 0) {
    326                 console.log('No permissions found\n');
     266                console.log(`Error: ${err.message}`);
     267                checkEmployees();
     268                return;
     269            }
     270
     271            if (!rows || rows.length === 0) {
     272                console.log('No permissions found');
    327273            } else {
    328274                console.log(`Total: ${rows.length} permissions\n`);
    … …  
    332278                    if (index < rows.length - 1) console.log('-'.repeat(80));
    333279                });
    334                 console.log();
    335             }
    336             if (next) next();
     280            }
     281            checkEmployees();
    337282        });
    338283    });
    … …  
    340285
    341286// Display EMPLOYEES table
    342 function checkEmployees(next) {
     287function checkEmployees() {
    343288    console.log('\nšŸ‘· EMPLOYEES TABLE:');
    344289    console.log('================================================================================');
    345 
    346290    tableExists('employees', (exists) => {
    347291        if (!exists) {
    348             console.log('Table does not exist\n');
    349             if (next) next();
     292            console.log('Table does not exist');
     293            checkBoss();
    350294            return;
    351295        }
    … …  
    357301        `, [], (err, rows) => {
    358302            if (err) {
    359                 console.log(`Error: ${err.message}\n`);
    360                 if (next) next();
    361                 return;
    362             }
    363 
    364             if (!rows || rows.length === 0) {
    365                 console.log('No employees found\n');
     303                console.log(`Error: ${err.message}`);
     304                checkBoss();
     305                return;
     306            }
     307
     308            if (!rows || rows.length === 0) {
     309                console.log('No employees found');
    366310            } else {
    367311                console.log(`Total: ${rows.length} employees\n`);
    … …  
    371315                    if (index < rows.length - 1) console.log('-'.repeat(80));
    372316                });
    373                 console.log();
    374             }
    375             if (next) next();
     317            }
     318            checkBoss();
    376319        });
    377320    });
    … …  
    379322
    380323// Display BOSS table
    381 function checkBoss(next) {
     324function checkBoss() {
    382325    console.log('\nšŸ‘‘ BOSS TABLE:');
    383326    console.log('================================================================================');
    384 
    385327    tableExists('boss', (exists) => {
    386328        if (!exists) {
    387             console.log('Table does not exist\n');
    388             if (next) next();
     329            console.log('Table does not exist');
     330            checkOrder();
    389331            return;
    390332        }
    … …  
    396338        `, [], (err, rows) => {
    397339            if (err) {
    398                 console.log(`Error: ${err.message}\n`);
    399                 if (next) next();
    400                 return;
    401             }
    402 
    403             if (!rows || rows.length === 0) {
    404                 console.log('No bosses found\n');
     340                console.log(`Error: ${err.message}`);
     341                checkOrder();
     342                return;
     343            }
     344
     345            if (!rows || rows.length === 0) {
     346                console.log('No bosses found');
    405347            } else {
    406348                console.log(`Total: ${rows.length} bosses\n`);
    … …  
    410352                    if (index < rows.length - 1) console.log('-'.repeat(80));
    411353                });
    412                 console.log();
    413             }
    414             if (next) next();
     354            }
     355            checkOrder();
    415356        });
    416357    });
    … …  
    418359
    419360// Display ORDER table
    420 function checkOrder(next) {
     361function checkOrder() {
    421362    console.log('\nšŸ“¦ ORDERS TABLE:');
    422363    console.log('================================================================================');
    423 
    424364    tableExists('order', (exists) => {
    425365        if (!exists) {
    426             console.log('Table does not exist\n');
    427             if (next) next();
     366            console.log('Table does not exist');
     367            checkReport();
    428368            return;
    429369        }
    … …  
    436376        `, [], (err, rows) => {
    437377            if (err) {
    438                 console.log(`Error: ${err.message}\n`);
    439                 if (next) next();
    440                 return;
    441             }
    442 
    443             if (!rows || rows.length === 0) {
    444                 console.log('No orders found\n');
     378                console.log(`Error: ${err.message}`);
     379                checkReport();
     380                return;
     381            }
     382
     383            if (!rows || rows.length === 0) {
     384                console.log('No orders found');
    445385            } else {
    446386                console.log(`Total: ${rows.length} orders\n`);
    … …  
    452392                    if (index < rows.length - 1) console.log('-'.repeat(80));
    453393                });
    454                 console.log();
    455             }
    456             if (next) next();
     394            }
     395            checkReport();
    457396        });
    458397    });
    … …  
    460399
    461400// Display REPORT table
    462 function checkReport(next) {
     401function checkReport() {
    463402    console.log('\nšŸ“Š REPORTS TABLE:');
    464403    console.log('================================================================================');
    465 
    466404    tableExists('report', (exists) => {
    467405        if (!exists) {
    468             console.log('Table does not exist\n');
    469             if (next) next();
     406            console.log('Table does not exist');
     407            checkRefund();
    470408            return;
    471409        }
    … …  
    473411        db.all('SELECT * FROM report', [], (err, rows) => {
    474412            if (err) {
    475                 console.log(`Error: ${err.message}\n`);
    476                 if (next) next();
    477                 return;
    478             }
    479 
    480             if (!rows || rows.length === 0) {
    481                 console.log('No reports found\n');
     413                console.log(`Error: ${err.message}`);
     414                checkRefund();
     415                return;
     416            }
     417
     418            if (!rows || rows.length === 0) {
     419                console.log('No reports found');
    482420            } else {
    483421                console.log(`Total: ${rows.length} reports\n`);
    … …  
    487425                    if (index < rows.length - 1) console.log('-'.repeat(80));
    488426                });
    489                 console.log();
    490             }
    491             if (next) next();
     427            }
     428            checkRefund();
    492429        });
    493430    });
    … …  
    495432
    496433// Display REFUND table
    497 function checkRefund(next) {
     434function checkRefund() {
    498435    console.log('\nšŸ’° REFUND TABLE:');
    499436    console.log('================================================================================');
    500 
    501437    tableExists('refund', (exists) => {
    502438        if (!exists) {
    503             console.log('Table does not exist\n');
    504             if (next) next();
     439            console.log('Table does not exist');
     440            checkImage();
    505441            return;
    506442        }
    … …  
    508444        db.all('SELECT * FROM refund', [], (err, rows) => {
    509445            if (err) {
    510                 console.log(`Error: ${err.message}\n`);
    511                 if (next) next();
    512                 return;
    513             }
    514 
    515             if (!rows || rows.length === 0) {
    516                 console.log('No refunds found\n');
     446                console.log(`Error: ${err.message}`);
     447                checkImage();
     448                return;
     449            }
     450
     451            if (!rows || rows.length === 0) {
     452                console.log('No refunds found');
    517453            } else {
    518454                console.log(`Total: ${rows.length} refunds\n`);
    … …  
    523459                    if (index < rows.length - 1) console.log('-'.repeat(80));
    524460                });
    525                 console.log();
    526             }
    527             if (next) next();
     461            }
     462            checkImage();
    528463        });
    529464    });
    … …  
    531466
    532467// Display IMAGE table
    533 function checkImage(next) {
     468function checkImage() {
    534469    console.log('\nšŸ–¼ļø IMAGE TABLE:');
    535470    console.log('================================================================================');
    536 
    537471    tableExists('image', (exists) => {
    538472        if (!exists) {
    539             console.log('Table does not exist\n');
    540             if (next) next();
     473            console.log('Table does not exist');
     474            checkColor();
    541475            return;
    542476        }
    … …  
    544478        db.all('SELECT * FROM image', [], (err, rows) => {
    545479            if (err) {
    546                 console.log(`Error: ${err.message}\n`);
    547                 if (next) next();
    548                 return;
    549             }
    550 
    551             if (!rows || rows.length === 0) {
    552                 console.log('No images found\n');
     480                console.log(`Error: ${err.message}`);
     481                checkColor();
     482                return;
     483            }
     484
     485            if (!rows || rows.length === 0) {
     486                console.log('No images found');
    553487            } else {
    554488                console.log(`Total: ${rows.length} images\n`);
    … …  
    557491                    if (index < rows.length - 1) console.log('-'.repeat(80));
    558492                });
    559                 console.log();
    560             }
    561             if (next) next();
     493            }
     494            checkColor();
    562495        });
    563496    });
    … …  
    565498
    566499// Display COLOR table
    567 function checkColor(next) {
     500function checkColor() {
    568501    console.log('\nšŸŽØ COLOR TABLE:');
    569502    console.log('================================================================================');
    570 
    571503    tableExists('color', (exists) => {
    572504        if (!exists) {
    573             console.log('Table does not exist\n');
    574             if (next) next();
     505            console.log('Table does not exist');
     506            finish();
    575507            return;
    576508        }
    … …  
    578510        db.all('SELECT * FROM color', [], (err, rows) => {
    579511            if (err) {
    580                 console.log(`Error: ${err.message}\n`);
    581                 if (next) next();
    582                 return;
    583             }
    584 
    585             if (!rows || rows.length === 0) {
    586                 console.log('No colors found\n');
     512                console.log(`Error: ${err.message}`);
     513                finish();
     514                return;
     515            }
     516
     517            if (!rows || rows.length === 0) {
     518                console.log('No colors found');
    587519            } else {
    588520                console.log(`Total: ${rows.length} colors\n`);
    … …  
    591523                    if (index < rows.length - 1) console.log('-'.repeat(80));
    592524                });
    593                 console.log();
    594             }
    595             if (next) next();
    596         });
    597     });
    598 }
    599 
    600 // Finish and close database
     525            }
     526            finish();
     527        });
     528    });
     529}
     530
     531// Start the chain of checks
     532function checkNext() {
     533    checkPersonal();
     534}
     535
    601536function finish() {
    602     console.log('='.repeat(80));
     537    console.log('\n' + '='.repeat(80));
    603538    console.log('āœ… Database inspection complete');
    604539    console.log('='.repeat(80) + '\n');
    605540
    606     db.close((err) => {
    607         if (err) {
    608             console.error('Error closing database:', err.message);
    609         }
    610     });
    611 }
    612 
    613 // Start the execution chain
    614 executeChecks();
     541    db.close();
     542}
     543
     544// Start with CLIENTS
     545checkNext();
Note: See TracChangeset for help on using the changeset viewer.