cash-report-system/backend/database/init.js

241 lines
7.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 };