Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • database.js

    r4dff800 r33517cc  
    1 const sqlite3 = require('sqlite3').verbose();
    2 const path = require('path');
     1const { Pool } = require('pg');
    32const bcrypt = require('bcryptjs');
    4 const crypto = require('crypto');
    5 
    6 const dbPath = path.join(__dirname, 'database', 'handcraft.db');
    7 const dbDir = path.dirname(dbPath);
    8 
    9 // Create database directory if it doesn't exist
    10 const fs = require('fs');
    11 if (!fs.existsSync(dbDir)) {
    12     fs.mkdirSync(dbDir, { recursive: true });
    13 }
    14 
    15 const 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     }
     3require('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
     24const 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
    2133});
    2234
    23 // Enable foreign keys
    24 database.run('PRAGMA foreign_keys = ON');
    25 
    26 // Helper function to ensure general category exists with ID 1
     35pool.on('connect', () => {
     36    console.log('✅ Connected to PostgreSQL database');
     37});
     38
     39pool.on('error', (err) => {
     40    console.error('❌ Unexpected PostgreSQL pool error:', err);
     41});
     42
     43/*
     44 * ============================================================
     45 * HELPER
     46 * ============================================================
     47 */
     48
     49function query(text, params = [], callback) {
     50    pool.query(text, params)
     51        .then(result => {
     52            callback(null, result);
     53        })
     54        .catch(err => {
     55            callback(err, null);
     56        });
     57}
     58
     59
     60/*
     61 * ============================================================
     62 * GENERAL CATEGORY
     63 * ============================================================
     64 */
     65
    2766function 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) {
     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
     131function 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
    42158                    if (err) {
    43                         callback(err);
    44                     } else {
    45                         console.log('✅ Updated category ID 1 to General');
    46                         callback(null);
     159                        callback(err, null);
     160                        return;
    47161                    }
    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);
     162
     163                    if (result.rows.length > 0) {
     164                        callback(null, result.rows[0].category_id);
     165                        return;
    74166                    }
    75                 }
    76             );
    77         }
    78     });
    79 }
    80 
    81 // Helper function to get the General category ID
    82 function 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) {
    86             callback(err, null);
    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 (?, ?)',
     167
     168                    query(
     169                        `INSERT INTO category
     170                            (name, description)
     171                         VALUES
     172                            ($1, $2)
     173                         RETURNING category_id`,
    100174                        ['General', 'General products category'],
    101                         function(err) {
     175                        (err, result) => {
     176
    102177                            if (err) {
    103178                                callback(err, null);
    104179                            } else {
    105                                 const newId = this.lastID;
    106                                 console.log(`✅ Created new General category with ID: ${newId}`);
     180                                const newId = result.rows[0].category_id;
     181
     182                                console.log(
     183                                    `✅ Created new General category with ID: ${newId}`
     184                                );
     185
    107186                                callback(null, newId);
    108187                            }
    … …  
    110189                    );
    111190                }
    112             });
    113         }
    114     });
    115 }
    116 
    117 // User functions
     191            );
     192        }
     193    );
     194}
     195
     196
     197/*
     198 * ============================================================
     199 * USER FUNCTIONS
     200 * ============================================================
     201 */
     202
    118203function getUserByUsername(username, callback) {
    119     database.get('SELECT * FROM users WHERE username = ?', [username], (err, row) => {
    120         callback(err, row);
    121     });
    122 }
     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
    123220
    124221function getUserById(id, callback) {
    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);
     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                    }
    143252                }
    144             }
    145         );
    146     });
    147 }
     253            );
     254        }
     255    );
     256}
     257
    148258
    149259function createUser(id, username, email, password, userType, callback) {
     260
    150261    const hashedPassword = bcrypt.hashSync(password, 10);
    151     database.run(
    152         'INSERT INTO users (id, username, email, password, user_type) VALUES (?, ?, ?, ?, ?)',
     262
     263    query(
     264        `INSERT INTO users
     265            (id, username, email, password, user_type)
     266         VALUES
     267            ($1, $2, $3, $4, $5)`,
    153268        [id, username, email, hashedPassword, userType],
    154         function(err) {
     269        (err) => {
     270
    155271            if (err) {
    156272                callback(err, null);
    … …  
    162278}
    163279
    164 // Client functions
     280
     281/*
     282 * ============================================================
     283 * CLIENT FUNCTIONS
     284 * ============================================================
     285 */
     286
    165287function getClientByEmail(email, callback) {
    166     database.get('SELECT * FROM client WHERE email = ?', [email], (err, row) => {
    167         callback(err, row);
    168     });
    169 }
     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
    170304
    171305function getClientById(id, callback) {
    172     database.get('SELECT * FROM client WHERE client_id = ?', [id], (err, row) => {
    173         callback(err, row);
    174     });
    175 }
     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
    176322
    177323function createClient(clientData, callback) {
    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) {
     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
    183342            if (err) {
    184343                callback(err, null);
    185344            } else {
    186                 callback(null, this.lastID);
    187             }
    188         }
    189     );
    190 }
    191 
    192 function verifyClientPassword(password, hashedPassword, callback) {
     345                callback(
     346                    null,
     347                    result.rows[0].client_id
     348                );
     349            }
     350        }
     351    );
     352}
     353
     354
     355function verifyClientPassword(
     356    password,
     357    hashedPassword,
     358    callback
     359) {
     360
    193361    try {
    194         const isValid = bcrypt.compareSync(password, hashedPassword);
     362
     363        const isValid =
     364            bcrypt.compareSync(password, hashedPassword);
     365
    195366        callback(null, isValid);
     367
    196368    } catch (err) {
     369
    197370        callback(err, false);
    198371    }
    199372}
    200373
    201 // Personal functions
     374
     375/*
     376 * ============================================================
     377 * PERSONAL / EMPLOYEE FUNCTIONS
     378 * ============================================================
     379 */
     380
    202381function getPersonalByEmail(email, callback) {
    203     database.get('SELECT * FROM personal WHERE email = ?', [email], (err, row) => {
    204         callback(err, row);
    205     });
    206 }
     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
    207398
    208399function getPersonalById(id, callback) {
    209     database.get('SELECT * FROM personal WHERE id = ?', [id], (err, row) => {
    210         callback(err, row);
    211     });
    212 }
    213 
    214 // Password verification for regular users
     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
    215417function verifyPassword(password, hashedPassword) {
    216418    return bcrypt.compareSync(password, hashedPassword);
    217419}
    218420
    219 // Password update
    220 function 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 = ?',
     421
     422function 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`,
    224436        [hashedPassword, userId],
    225         function(err) {
     437        (err) => {
     438
    226439            callback(err);
    227440        }
    … …  
    229442}
    230443
    231 // Product functions
     444
     445/*
     446 * ============================================================
     447 * PRODUCT FUNCTIONS
     448 * ============================================================
     449 */
     450
    232451function getProducts(categoryId, searchTerm, callback) {
    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   `;
     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
    240466    const params = [];
     467    let paramIndex = 1;
    241468
    242469    if (categoryId && categoryId !== 'all') {
    243         query += ' AND p.category_id = ?';
     470
     471        queryText +=
     472            ` AND p.category_id = $${paramIndex}`;
     473
    244474        params.push(categoryId);
     475        paramIndex++;
    245476    }
    246477
    247478    if (searchTerm) {
    248         query += ' AND (p.description LIKE ? OR p.code LIKE ?)';
    249         params.push(`%${searchTerm}%`, `%${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;
    250490    }
    251491
    252     database.all(query, params, (err, rows) => {
    253         if (err) {
    254             callback(err, null);
    255         } else {
    256             callback(null, rows || []);
    257         }
    258     });
    259 }
    260 
    261 function 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) {
     492    queryText += ` ORDER BY p.code`;
     493
     494    query(
     495        queryText,
     496        params,
     497        (err, result) => {
     498
     499            if (err) {
    271500                callback(err, null);
    272501            } else {
    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                             );
     502                callback(null, result.rows || []);
     503            }
     504        }
     505    );
     506}
     507
     508
     509function 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) {
     526                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;
     542                    }
     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                            }
    295561                        }
     562                    );
     563                }
     564            );
     565        }
     566    );
     567}
     568
     569
     570function 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;
    296603                    }
    297                 );
    298             }
    299         }
    300     );
    301 }
    302 
    303 function 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) {
    313                 callback(err, null);
    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                             );
     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                            }
    337623                        }
    338                     }
    339                 );
    340             }
    341         }
    342     );
    343 }
     624                    );
     625                }
     626            );
     627        }
     628    );
     629}
     630
    344631
    345632function addProduct(personalId, productData, callback) {
     633
    346634    getGeneralCategoryId((err, generalCategoryId) => {
     635
    347636        if (err) {
    348637            callback(err, null);
    … …  
    350639        }
    351640
    352         const categoryId = productData.category_id || generalCategoryId;
    353 
    354         // FIXED: Added validation for required fields
     641        const categoryId =
     642            productData.category_id || generalCategoryId;
     643
    355644        if (!productData.code) {
    356             callback(new Error('Product code is required'), null);
     645            callback(
     646                new Error('Product code is required'),
     647                null
     648            );
    357649            return;
    358650        }
    359651
    360652        if (!productData.store_id) {
    361             callback(new Error('Store ID is required'), null);
     653            callback(
     654                new Error('Store ID is required'),
     655                null
     656            );
    362657            return;
    363658        }
    364659
    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'))`,
     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`,
    370694            [
    371                 'PROD_' + Date.now().toString().slice(-8), // Generate a unique ID
     695                productId,
    372696                productData.code,
    373697                productData.description || 'No description',
    … …  
    380704                productData.store_id
    381705            ],
    382             function(err) {
     706            (err, result) => {
     707
    383708                if (err) {
    384709                    callback(err, null);
    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                             }
     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
     793                                    if (err) {
     794                                        console.error(
     795                                            'Error inserting image:',
     796                                            err
     797                                        );
     798                                    }
     799                                }
     800                            );
    397801                        }
    398802                    );
    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);
     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                                }
    408833                            }
     834                        );
     835                    });
     836                }
     837
     838                callback(null, returnedId);
     839            }
     840        );
     841    });
     842}
     843
     844
     845function 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;
    409987                        }
    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) {
     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
    4201057                                    if (err) {
    421                                         console.error('Error inserting image:', err);
     1058                                        console.error(
     1059                                            'Error inserting color:',
     1060                                            err
     1061                                        );
    4221062                                    }
    4231063                                }
    … …  
    4251065                        });
    4261066                    }
    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                                 }
     1067                );
     1068            }
     1069
     1070            callback(null, result.rowCount);
     1071        }
     1072    );
     1073}
     1074
     1075
     1076function 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
    4401143                            );
    4411144                        });
    442                     }
    443 
    444                     callback(null, productId);
    445                 }
    446             }
    447         );
    448     });
    449 }
    450 
    451 function 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) {
     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
     1169function 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
     1187function 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
     1210function 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
    5011229            if (err) {
    5021230                callback(err, null);
    5031231            } else {
    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 
    566 function 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
    618 function getCategories(callback) {
    619     database.all('SELECT * FROM category ORDER BY name', [], (err, rows) => {
    620         callback(err, rows || []);
    621     });
    622 }
    623 
    624 function 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 
    637 function 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 {
     1232
    6451233                callback(null, {
    646                     id: this.lastID,
     1234                    id: result.rows[0].category_id,
    6471235                    name: categoryData.name,
    6481236                    parent_id: categoryData.parent_id,
    … …  
    6541242}
    6551243
    656 // Store functions
     1244
     1245/*
     1246 * ============================================================
     1247 * STORE FUNCTIONS
     1248 * ============================================================
     1249 */
     1250
    6571251function getStores(callback) {
    658     database.all('SELECT * FROM store ORDER BY name', [], (err, rows) => {
    659         callback(err, rows || []);
    660     });
    661 }
     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
    6621268
    6631269function getStoreProducts(storeId, callback) {
    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`,
     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`,
    6701280        [storeId],
    671         (err, rows) => {
     1281        (err, result) => {
     1282
     1283            callback(
     1284                err,
     1285                result ? result.rows : []
     1286            );
     1287        }
     1288    );
     1289}
     1290
     1291
     1292function 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
    6721307            if (err) {
    6731308                callback(err, null);
    674             } else {
    675                 callback(null, rows || []);
    676             }
    677         }
    678     );
    679 }
    680 
    681 function 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`,
     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
     1354function 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`,
    6881370        [storeId],
    689         (err, rows) => {
     1371        (err, result) => {
     1372
     1373            callback(
     1374                err,
     1375                result ? result.rows : []
     1376            );
     1377        }
     1378    );
     1379}
     1380
     1381
     1382function 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
     1401function 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
    6901412            if (err) {
    6911413                callback(err, null);
    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 = [];
     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`,
     1426                [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`,
     1452                        [storeId],
     1453                        (err, result) => {
     1454
     1455                            if (err) {
     1456                                callback(err, null);
     1457                                return;
    7141458                            }
    715                             completed++;
    716                             if (completed === orders.length) {
    717                                 callback(null, orders);
    718                             }
    719                         }
    720                     );
    721                 });
    722             }
    723         }
    724     );
    725 }
    726 
    727 function 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 
    742 function 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 
    754 function 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 = ?',
    767                 [storeId],
    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 = ?`,
    777                         [storeId],
    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 = ?`,
     1459
     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`,
    7871477                                [storeId],
    788                                 (err, row) => {
    789                                     stats.avg_rating = row && row.avg_rating ? row.avg_rating : 0;
    790                                     callback(null, stats);
     1478                                (err, result) => {
     1479
     1480                                    if (err) {
     1481                                        callback(err, null);
     1482                                        return;
     1483                                    }
     1484
     1485                                    stats.avg_rating =
     1486                                        Number(
     1487                                            result.rows[0]
     1488                                                .avg_rating || 0
     1489                                        );
     1490
     1491                                    callback(
     1492                                        null,
     1493                                        stats
     1494                                    );
    7911495                                }
    7921496                            );
    … …  
    7991503}
    8001504
    801 // Order functions
     1505
     1506/*
     1507 * ============================================================
     1508 * ORDER FUNCTIONS
     1509 * ============================================================
     1510 */
     1511
    8021512function createOrderNew(orderData, callback) {
    803     database.run('BEGIN TRANSACTION', (err) => {
    804         if (err) {
     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                        });
     1614                });
     1615        })
     1616        .catch(err => {
    8051617            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) {
    844                             if (err) {
    845                                 database.run('ROLLBACK');
    846                                 callback(err, null);
    847                                 return;
    848                             }
    849 
    850                             itemsInserted++;
    851                             if (itemsInserted === items.length) {
    852                                 database.run('COMMIT', (err) => {
    853                                     if (err) {
    854                                         callback(err, null);
    855                                     } else {
    856                                         callback(null, orderData.order_num);
    857                                     }
    858                                 });
    859                             }
    860                         }
    861                     );
    862                 });
    863             }
    864         );
    865     });
    866 }
     1618        });
     1619}
     1620
    8671621
    8681622function getOrdersByClient(clientId, callback) {
    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`,
     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`,
    8751633        [clientId],
    876         (err, rows) => {
     1634        (err, result) => {
     1635
    8771636            if (err) {
    8781637                callback(err, null);
    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                             }
     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 = [];
    9061668                        }
    907                     );
    908                 });
    909             }
    910         }
    911     );
    912 }
     1669
     1670                        completed++;
     1671
     1672                        if (completed === orders.length) {
     1673                            callback(null, orders);
     1674                        }
     1675                    }
     1676                );
     1677            });
     1678        }
     1679    );
     1680}
     1681
    9131682
    9141683function getAllOrders(callback) {
    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`,
     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`,
    9211697        [],
    922         (err, rows) => {
    923             callback(err, rows || []);
    924         }
    925     );
    926 }
    927 
    928 // Review functions
     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
    9291715function createReviewNew(reviewData, callback) {
    930     database.run(
    931         `INSERT INTO review (review_id, client_id, product_code, rating, comment, review_date)
    932      VALUES (?, ?, ?, ?, ?, datetime('now'))`,
     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`,
    9331734        [
    934             'REV' + Date.now().toString().slice(-8),
     1735            reviewId,
    9351736            reviewData.client_id,
    9361737            reviewData.product_code,
    … …  
    9381739            reviewData.comment || ''
    9391740        ],
    940         function(err) {
     1741        (err, result) => {
     1742
    9411743            if (err) {
    9421744                callback(err, null);
    9431745            } else {
    944                 callback(null, this.lastID);
    945             }
    946         }
    947     );
    948 }
    949 
    950 // Request functions
     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
    9511762function createRequest(requestData, callback) {
    952     database.run(
    953         `INSERT INTO request (request_num, date_and_time, problem, client_id, store_id)
    954      VALUES (?, ?, ?, ?, ?)`,
     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)`,
    9551775        [
    9561776            requestData.request_num,
    … …  
    9601780            requestData.store_id
    9611781        ],
    962         function(err) {
     1782        (err) => {
     1783
    9631784            if (err) {
    9641785                callback(err, null);
    9651786            } else {
    966                 callback(null, requestData.request_num);
    967             }
    968         }
    969     );
    970 }
    971 
    972 // Refund functions
     1787                callback(
     1788                    null,
     1789                    requestData.request_num
     1790                );
     1791            }
     1792        }
     1793    );
     1794}
     1795
     1796
     1797/*
     1798 * ============================================================
     1799 * REFUND FUNCTIONS
     1800 * ============================================================
     1801 */
     1802
    9731803function createRefund(refundData, callback) {
    974     database.run(
    975         `INSERT INTO refund (refund_id, order_num, amount, reason, request_date)
    976      VALUES (?, ?, ?, ?, datetime('now'))`,
     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())`,
    9771816        [
    9781817            refundData.refund_id,
    … …  
    9811820            refundData.reason
    9821821        ],
    983         function(err) {
     1822        (err) => {
     1823
    9841824            if (err) {
    9851825                callback(err, null);
    9861826            } else {
    987                 callback(null, refundData.refund_id);
    988             }
    989         }
    990     );
    991 }
    992 
    993 // Employee task functions
    994 function getEmployeeTasks(personalId, storeId, callback) {
     1827                callback(
     1828                    null,
     1829                    refundData.refund_id
     1830                );
     1831            }
     1832        }
     1833    );
     1834}
     1835
     1836
     1837/*
     1838 * ============================================================
     1839 * EMPLOYEE TASKS
     1840 * ============================================================
     1841 */
     1842
     1843function getEmployeeTasks(
     1844    personalId,
     1845    storeId,
     1846    callback
     1847) {
     1848
    9951849    const tasks = {
    9961850        pending_orders: [],
    … …  
    9991853    };
    10001854
    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) => {
     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
    10101875            if (!err) {
    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) => {
     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
    10231900                    if (!err) {
    1024                         tasks.pending_requests = rows || [];
     1901                        tasks.pending_requests =
     1902                            result.rows || [];
    10251903                    }
    10261904
    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) => {
     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
    10371930                            if (!err) {
    1038                                 tasks.pending_refunds = rows || [];
     1931                                tasks.pending_refunds =
     1932                                    result.rows || [];
    10391933                            }
    1040                             callback(null, tasks);
     1934
     1935                            callback(
     1936                                null,
     1937                                tasks
     1938                            );
    10411939                        }
    10421940                    );
    … …  
    10471945}
    10481946
    1049 // Client stats functions
     1947
     1948/*
     1949 * ============================================================
     1950 * CLIENT STATISTICS
     1951 * ============================================================
     1952 */
     1953
    10501954function getClientStats(clientId, callback) {
     1955
    10511956    const stats = {};
    10521957
    1053     // Get total orders
    1054     database.get(
    1055         'SELECT COUNT(*) as total_orders FROM "order" WHERE client_id = ?',
     1958    query(
     1959        `SELECT COUNT(*) AS total_orders
     1960         FROM "order"
     1961         WHERE client_id = $1`,
    10561962        [clientId],
    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 = ?`,
     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`,
    10661989                [clientId],
    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);
     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                                    );
    10842057                                }
    10852058                            );
    … …  
    10922065}
    10932066
    1094 // User functions for admin
     2067
     2068/*
     2069 * ============================================================
     2070 * ADMIN USER FUNCTIONS
     2071 * ============================================================
     2072 */
     2073
    10952074function getAllUsers(callback) {
    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
     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
    11022085         FROM client
    11032086         ORDER BY client_id`,
    11042087        [],
    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
     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
    11262121                 FROM personal p
    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
     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
    11302128                 ORDER BY p.id`,
    11312129                [],
    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                         });
     2130                (err, result) => {
     2131
     2132                    if (err) {
     2133                        callback(err, null);
     2134                        return;
    11412135                    }
    11422136
    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
     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
    11502171                         FROM users
    11512172                         ORDER BY id`,
    11522173                        [],
    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);
     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';
    11732212                                    }
     2213
     2214                                    usersMap.set(
     2215                                        row.id,
     2216                                        userData
     2217                                    );
     2218                                }
     2219                            });
     2220
     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;
    11742232                                });
    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;
    1181                             });
    1182 
    1183                             callback(null, users);
     2233
     2234                            callback(
     2235                                null,
     2236                                users
     2237                            );
    11842238                        }
    11852239                    );
    … …  
    11902244}
    11912245
    1192 // Audit log function
    1193 function 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],
     2246
     2247/*
     2248 * ============================================================
     2249 * AUDIT LOG
     2250 * ============================================================
     2251 */
     2252
     2253function 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        ],
    11982282        (err) => {
     2283
    11992284            if (err) {
    1200                 console.error('Error logging audit:', err);
    1201             }
    1202         }
    1203     );
    1204 }
     2285                console.error(
     2286                    'Error logging audit:',
     2287                    err
     2288                );
     2289            }
     2290        }
     2291    );
     2292}
     2293
     2294
     2295/*
     2296 * ============================================================
     2297 * EXPORTS
     2298 * ============================================================
     2299 */
    12052300
    12062301module.exports = {
    1207     database,
     2302
     2303    pool,
     2304
     2305    query,
     2306
    12082307    ensureGeneralCategory,
    12092308    getGeneralCategoryId,
     2309
    12102310    getUserByUsername,
    12112311    getUserById,
    12122312    createUser,
     2313
    12132314    getClientByEmail,
    12142315    getClientById,
    12152316    createClient,
    12162317    verifyClientPassword,
     2318
    12172319    getPersonalByEmail,
    12182320    getPersonalById,
    12192321    verifyPassword,
    12202322    updatePasswordAndClearForce,
     2323
    12212324    getProducts,
    12222325    getProductById,
    … …  
    12252328    updateProduct,
    12262329    deleteProduct,
     2330
    12272331    getCategories,
    12282332    getCategoriesWithParents,
    12292333    createCategory,
     2334
    12302335    getStores,
    12312336    getStoreProducts,
    … …  
    12342339    getStoreReports,
    12352340    getStoreStats,
     2341
    12362342    createOrderNew,
    12372343    getOrdersByClient,
    12382344    getAllOrders,
     2345
    12392346    createReviewNew,
     2347
    12402348    createRequest,
     2349
    12412350    createRefund,
     2351
    12422352    getEmployeeTasks,
     2353
    12432354    getClientStats,
     2355
    12442356    getAllUsers,
     2357
    12452358    logAudit
    12462359};
Note: See TracChangeset for help on using the changeset viewer.