241 lines
7.9 KiB
JavaScript
241 lines
7.9 KiB
JavaScript
const sqlite3 = require("sqlite3").verbose();
|
||
const bcrypt = require("bcrypt");
|
||
const path = require("path");
|
||
const fs = require("fs");
|
||
|
||
const DB_PATH =
|
||
process.env.DB_PATH || path.join(__dirname, "../../data/database.sqlite");
|
||
|
||
// Создаем директорию для БД если не существует
|
||
const dbDir = path.dirname(DB_PATH);
|
||
if (!fs.existsSync(dbDir)) {
|
||
fs.mkdirSync(dbDir, { recursive: true });
|
||
}
|
||
|
||
const db = new sqlite3.Database(DB_PATH);
|
||
|
||
async function initDatabase() {
|
||
return new Promise((resolve, reject) => {
|
||
db.serialize(async () => {
|
||
// Таблица пользователей
|
||
db.run(`CREATE TABLE IF NOT EXISTS users (
|
||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
username TEXT UNIQUE NOT NULL,
|
||
password TEXT NOT NULL,
|
||
fullName TEXT DEFAULT '',
|
||
role TEXT NOT NULL DEFAULT 'employee',
|
||
isActive BOOLEAN DEFAULT 1,
|
||
createdAt DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||
updatedAt DATETIME DEFAULT CURRENT_TIMESTAMP
|
||
)`);
|
||
|
||
db.run(`ALTER TABLE users ADD COLUMN plaintextPassword TEXT;`, (err) => {
|
||
if (err && !err.message.toLowerCase().includes("duplicate")) {
|
||
console.error("Error adding plaintextPassword column:", err);
|
||
}
|
||
});
|
||
|
||
// Таблица магазинов
|
||
db.run(`CREATE TABLE IF NOT EXISTS stores (
|
||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
name TEXT UNIQUE NOT NULL,
|
||
address TEXT,
|
||
isActive BOOLEAN DEFAULT 1,
|
||
createdAt DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||
updatedAt DATETIME DEFAULT CURRENT_TIMESTAMP
|
||
)`);
|
||
|
||
// Таблица прав доступа пользователей к магазинам
|
||
db.run(`CREATE TABLE IF NOT EXISTS user_store_access (
|
||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
userId INTEGER NOT NULL,
|
||
storeId INTEGER NOT NULL,
|
||
FOREIGN KEY (userId) REFERENCES users (id) ON DELETE CASCADE,
|
||
FOREIGN KEY (storeId) REFERENCES stores (id) ON DELETE CASCADE,
|
||
UNIQUE(userId, storeId)
|
||
)`);
|
||
|
||
// Таблица отчетов
|
||
db.run(`CREATE TABLE IF NOT EXISTS reports (
|
||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
storeId INTEGER NOT NULL,
|
||
userId INTEGER NOT NULL,
|
||
reportDate DATE NOT NULL,
|
||
income DECIMAL(10,2) NOT NULL,
|
||
initialCash DECIMAL(10,2) NOT NULL,
|
||
totalIncome DECIMAL(10,2) NOT NULL,
|
||
wages TEXT, -- JSON array of wages
|
||
totalWages DECIMAL(10,2) DEFAULT 0,
|
||
expenses TEXT, -- JSON array of expenses
|
||
totalExpenses DECIMAL(10,2) DEFAULT 0,
|
||
envelope DECIMAL(10,2) NOT NULL,
|
||
finalCash DECIMAL(10,2) NOT NULL,
|
||
isVerified BOOLEAN DEFAULT 0,
|
||
verifiedBy INTEGER,
|
||
verifiedAt DATETIME,
|
||
createdAt DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||
updatedAt DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||
FOREIGN KEY (storeId) REFERENCES stores (id),
|
||
FOREIGN KEY (userId) REFERENCES users (id),
|
||
FOREIGN KEY (verifiedBy) REFERENCES users (id),
|
||
UNIQUE(storeId, reportDate, userId)
|
||
)`);
|
||
|
||
// 4. Backfill plaintext passwords for all existing users
|
||
db.run(
|
||
`UPDATE users SET plaintextPassword = 'admin123' WHERE username = 'admin'`
|
||
);
|
||
db.run(
|
||
`UPDATE users SET plaintextPassword = 'password123' WHERE username != 'admin'`
|
||
);
|
||
|
||
// Создаем тестовые данные
|
||
try {
|
||
await createTestData();
|
||
console.log("✅ Database initialized successfully");
|
||
resolve();
|
||
} catch (error) {
|
||
console.error("❌ Error creating test data:", error);
|
||
reject(error);
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
async function createTestData() {
|
||
const adminPassword = await bcrypt.hash("admin123", 10);
|
||
const cashierPassword = await bcrypt.hash("password123", 10);
|
||
|
||
// Создаем пользователей
|
||
const users = [
|
||
{
|
||
username: "admin",
|
||
password: adminPassword,
|
||
fullName: "Администратор",
|
||
role: "admin",
|
||
},
|
||
{
|
||
username: "cashier1",
|
||
password: cashierPassword,
|
||
fullName: "Кассир Иванов И.И.",
|
||
role: "employee",
|
||
},
|
||
{
|
||
username: "cashier2",
|
||
password: cashierPassword,
|
||
fullName: "Кассир Петров П.П.",
|
||
role: "employee",
|
||
},
|
||
{
|
||
username: "manager1",
|
||
password: cashierPassword,
|
||
fullName: "Менеджер Сидоров С.С.",
|
||
role: "manager",
|
||
},
|
||
];
|
||
|
||
for (const user of users) {
|
||
db.run(
|
||
`INSERT OR IGNORE INTO users (username, password, plaintextPassword, fullName, role) VALUES (?, ?, ?, ?, ?)`,
|
||
[
|
||
user.username,
|
||
user.password,
|
||
user.username === "admin" ? "admin123" : "password123",
|
||
user.fullName,
|
||
user.role,
|
||
]
|
||
);
|
||
}
|
||
|
||
// Создаем магазины
|
||
const stores = [
|
||
{ name: 'Магазин "Центральный"', address: "ул. Ленина, 15" },
|
||
{ name: 'Магазин "Восточный"', address: "ул. Гагарина, 22" },
|
||
{ name: 'Магазин "Западный"', address: "пр. Мира, 8" },
|
||
{ name: 'Магазин "Южный"', address: "ул. Пушкина, 45" },
|
||
];
|
||
|
||
for (const store of stores) {
|
||
db.run(`INSERT OR IGNORE INTO stores (name, address) VALUES (?, ?)`, [
|
||
store.name,
|
||
store.address,
|
||
]);
|
||
}
|
||
|
||
// Создаем права доступа
|
||
const accessRights = [
|
||
{ userId: 2, storeId: 1 }, // cashier1 -> Центральный
|
||
{ userId: 2, storeId: 2 }, // cashier1 -> Восточный
|
||
{ userId: 3, storeId: 3 }, // cashier2 -> Западный
|
||
{ userId: 3, storeId: 4 }, // cashier2 -> Южный
|
||
];
|
||
|
||
for (const access of accessRights) {
|
||
db.run(
|
||
`INSERT OR IGNORE INTO user_store_access (userId, storeId) VALUES (?, ?)`,
|
||
[access.userId, access.storeId]
|
||
);
|
||
}
|
||
|
||
// Создаем тестовые отчеты за последние 30 дней
|
||
const today = new Date();
|
||
for (let i = 1; i < 30; i++) {
|
||
const date = new Date(today);
|
||
date.setDate(date.getDate() - i);
|
||
const dateString = date.toISOString().split("T")[0];
|
||
|
||
for (let storeId = 1; storeId <= 4; storeId++) {
|
||
const income = 5000 + Math.random() * 10000; // 5000-15000
|
||
const initialCash = 1000 + Math.random() * 2000; // 1000-3000
|
||
const totalIncome = income + initialCash;
|
||
|
||
const wages = JSON.stringify([
|
||
{ name: "Кассир", amount: 500 + Math.random() * 300 },
|
||
{ name: "Продавец", amount: 400 + Math.random() * 200 },
|
||
]);
|
||
const totalWages = JSON.parse(wages).reduce(
|
||
(sum, w) => sum + w.amount,
|
||
0
|
||
);
|
||
|
||
const expenses = JSON.stringify([
|
||
{ name: "Аренда", amount: 300 + Math.random() * 200 },
|
||
{ name: "Коммунальные", amount: 100 + Math.random() * 100 },
|
||
{ name: "Расходники", amount: 50 + Math.random() * 150 },
|
||
]);
|
||
const totalExpenses = JSON.parse(expenses).reduce(
|
||
(sum, e) => sum + e.amount,
|
||
0
|
||
);
|
||
|
||
const envelope = 8000 + Math.random() * 4000; // 8000-12000
|
||
const finalCash = totalIncome - totalWages - totalExpenses - envelope;
|
||
|
||
db.run(
|
||
`INSERT OR IGNORE INTO reports
|
||
(storeId, userId, reportDate, income, initialCash, totalIncome, wages, totalWages,
|
||
expenses, totalExpenses, envelope, finalCash, isVerified, verifiedBy)
|
||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||
[
|
||
storeId,
|
||
storeId === 1 || storeId === 2 ? 2 : 3,
|
||
dateString,
|
||
Math.round(income),
|
||
Math.round(initialCash),
|
||
Math.round(totalIncome),
|
||
wages,
|
||
Math.round(totalWages),
|
||
expenses,
|
||
Math.round(totalExpenses),
|
||
Math.round(envelope),
|
||
Math.round(finalCash),
|
||
Math.random() > 0.3 ? 1 : 0,
|
||
1,
|
||
]
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
module.exports = { db, initDatabase };
|