瀏覽代碼

Closing_final_stage

svalavuo 8 小時之前
父節點
當前提交
923fc8781c
共有 8 個文件被更改,包括 835 次插入100 次删除
  1. 36 0
      .env.with-services
  2. 116 32
      DOCKER_DEPLOYMENT.md
  3. 149 40
      DOCKER_README.md
  4. 101 7
      README.md
  5. 25 4
      backend/config/database.php
  6. 311 4
      backend/setup_database.sql
  7. 97 0
      docker-compose-with-services.yml
  8. 0 13
      docker-compose.yml

+ 36 - 0
.env.with-services

@@ -0,0 +1,36 @@
+# Database Configuration (for docker-compose-with-services.yml)
+DB_HOST=mariadb
+DB_PORT=3306
+DB_NAME=inventory_db
+DB_USER=inventory_user
+DB_PASS=inventory_password
+MYSQL_ROOT_PASSWORD=root_password
+
+# Redis Configuration
+REDIS_HOST=redis
+REDIS_PORT=6379
+REDIS_PASSWORD=redis_password
+
+# Frontend Configuration
+FRONTEND_PORT=80
+
+# Company Information
+COMPANY_NAME=Inventory Management
+COMPANY_ADDRESS=123 Business St
+COMPANY_CITY=Helsinki
+COMPANY_POSTAL_CODE=00100
+COMPANY_COUNTRY=Finland
+COMPANY_PHONE=+358 123 456 789
+COMPANY_EMAIL=info@company.com
+COMPANY_Y_TUNNUS=1234567-8
+
+# File Upload Configuration
+UPLOAD_MAX_SIZE=10M
+ALLOWED_FILE_TYPES=pdf,doc,docx,xls,xlsx,jpg,jpeg,png,gif
+UPLOADS_PATH=./uploads
+
+# MariaDB Port (optional, for external access)
+MARIADB_PORT=3306
+
+# Redis Port (optional, for external access)
+REDIS_PORT=6379

+ 116 - 32
DOCKER_DEPLOYMENT.md

@@ -1,87 +1,170 @@
 # Docker Deployment Guide
 
-## 🐳 Complete Inventory Management System in One Container
+## 🐳 Complete Inventory Management System with Docker
 
-This guide explains how to deploy the complete inventory management system using Docker and Docker Compose.
+This guide explains how to deploy the complete inventory management system using Docker and Docker Compose with two deployment options.
 
 ## 📋 Prerequisites
 
 - Docker 20.10+ and Docker Compose 2.0+
-- At least 2GB RAM available
+- At least 2GB RAM available (4GB recommended for full setup)
 - At least 10GB free disk space
 
-## 🚀 Quick Start
+## 🚀 Deployment Options
 
-### 1. Clone and Setup
+### Option 1: Basic Setup (External Database)
+Perfect for production environments with existing database infrastructure.
+
+#### 1. Clone and Setup
 ```bash
 git clone <repository-url>
 cd inventory
 ```
 
-### 2. Configure Environment
+#### 2. Configure Environment
 ```bash
-# Copy environment file
-cp .env.example .env
+# Copy and edit environment file
+cp backend/.env.local backend/.env.local
+nano backend/.env.local
+```
 
-# Edit configuration (optional)
-nano .env
+Configure your external database:
+```bash
+DB_HOST=your-external-db-host
+DB_PORT=3306
+DB_NAME=inventory_db
+DB_USER=your-username
+DB_PASS=your-password
 ```
 
-### 3. Build and Deploy
+#### 3. Deploy
 ```bash
-# Make build script executable
-chmod +x build.sh
+docker-compose up -d
+```
 
-# Run the build script
-./build.sh
+#### 4. Access Application
+- **Frontend**: http://localhost
+- **API**: http://localhost/api
+- **Database**: External database (configured in .env.local)
+
+### Option 2: Full Setup (Built-in Database + Cache)
+Perfect for development, testing, or all-in-one deployments.
+
+#### 1. Clone and Setup
+```bash
+git clone <repository-url>
+cd inventory
+```
+
+#### 2. Deploy with Built-in Services
+```bash
+docker-compose -f docker-compose-with-services.yml --env-file .env.with-services up -d
 ```
 
-### 4. Access Application
+#### 3. Access Application
 - **Frontend**: http://localhost
 - **API**: http://localhost/api
-- **Database**: External database (configured in .env)
+- **Database**: MariaDB on port 3306
+- **Redis Cache**: Redis on port 6379
+
+#### 4. Default Credentials
+- **Database User**: `inventory_user` / `inventory_password`
+- **Database Root**: `root` / `root_password`
+- **Redis**: Password protected with `redis_password`
 
 ## 🏗️ Architecture
 
-### Single Container Solution
-The Docker setup creates a single container that includes:
+### Basic Setup Architecture
+Single container solution that includes:
 - **Apache Web Server** with PHP 8.1
 - **Vue.js Frontend** (built and served)
 - **PHP Backend API** with all endpoints
-- **External Database** (user-provided MySQL/MariaDB)
-- **Redis Cache** (optional)
-
+- **External Database Connectivity**
+
+### Full Setup Architecture
+Multi-container solution that includes:
+- **inventory-app**: Complete application (PHP + Apache + Vue.js)
+- **mariadb**: MariaDB 10.11 database server
+- **redis**: Redis 7 cache server
+- **Persistent Volumes**: Database, Redis, and uploads storage
 ### Container Structure
+
+#### Basic Setup Structure
+```
+inventory-app (single container)
+├── Apache Web Server
+├── PHP Backend (/var/www/html/api)
+├── Vue.js Frontend (/var/www/html/frontend/dist)
+├── Uploads (/var/www/html/uploads)
+└── External Database Connection
+```
+
+#### Full Setup Structure
 ```
 inventory-app (main container)
 ├── Apache Web Server
 ├── PHP Backend (/var/www/html/api)
 ├── Vue.js Frontend (/var/www/html/frontend/dist)
 ├── Uploads (/var/www/html/uploads)
-└── Configuration
 
-inventory-redis (optional)
-└── Redis 7 Cache
+inventory-mariadb (database container)
+├── MariaDB 10.11
+├── Auto-initialized Database
+└── Persistent Storage
 
-External Database (user-provided)
-└── MySQL/MariaDB Database
+inventory-redis (cache container)
+├── Redis 7
+├── Password Protected
+└── Persistent Storage
 ```
 
 ## ⚙️ Configuration
 
-### Environment Variables (.env)
+### Environment Variables
+
+#### Basic Setup (.env.local)
 ```bash
 # Database Configuration (External)
-DB_HOST=your-external-db-host # Your database server
+DB_HOST=your-external-db-host
 DB_PORT=3306
 DB_NAME=inventory_db
 DB_USER=your-db-username
 DB_PASS=your-db-password
 
-# Application Port
-APP_PORT=80                   # Web server port
+# Company Information
+COMPANY_NAME=Your Company Name
+COMPANY_ADDRESS=123 Business Street
+COMPANY_CITY=Helsinki
+COMPANY_POSTAL_CODE=00100
+COMPANY_COUNTRY=Finland
+COMPANY_PHONE=+358 123 456 789
+COMPANY_EMAIL=info@yourcompany.com
+COMPANY_Y_TUNNUS=1234567-8
 
-# Redis (optional)
+# File Upload Configuration
+UPLOAD_MAX_SIZE=10M
+ALLOWED_FILE_TYPES=pdf,doc,docx,xls,xlsx,jpg,jpeg,png,gif
+UPLOADS_PATH=./uploads
+```
+
+#### Full Setup (.env.with-services)
+```bash
+# Database Configuration (Built-in)
+DB_HOST=mariadb
+DB_PORT=3306
+DB_NAME=inventory_db
+DB_USER=inventory_user
+DB_PASS=inventory_password
+MYSQL_ROOT_PASSWORD=root_password
+
+# Redis Configuration (Built-in)
+REDIS_HOST=redis
+REDIS_PORT=6379
+REDIS_PASSWORD=redis_password
+
+# Frontend Configuration
+FRONTEND_PORT=80
+MARIADB_PORT=3306
 REDIS_PORT=6379
 
 # Company Information
@@ -99,6 +182,7 @@ UPLOAD_MAX_SIZE=10M
 ALLOWED_FILE_TYPES=pdf,doc,docx,xls,xlsx,jpg,jpeg,png,gif
 UPLOADS_PATH=./uploads
 ```
+```
 
 ## 🛠️ Docker Compose Services
 

+ 149 - 40
DOCKER_README.md

@@ -1,29 +1,30 @@
 # Docker Containerization Guide
 
 ## Overview
-This inventory management system has been containerized with Docker for easy deployment and scaling. The setup includes:
-- Backend PHP service with Apache
-- Frontend Vue.js service with Nginx
-- **External database connectivity** (MySQL/PostgreSQL/etc.)
-- Redis cache (optional)
-- External volume mounts for uploads
+This inventory management system provides two Docker deployment options:
+- **Basic Setup**: Single container using external database
+- **Full Setup**: Complete stack with built-in MariaDB and Redis
 
 ## Quick Start
 
 ### Prerequisites
 - Docker and Docker Compose installed
-- At least 2GB RAM available
-- Sufficient disk space for uploads
-- **External database server** (MySQL 8.0+ recommended)
+- At least 2GB RAM available (4GB recommended for full setup)
+- Sufficient disk space for uploads and database
 
-### 1. Environment Configuration
-For external database setup, use the external database template:
+## Deployment Options
 
+### Option 1: Basic Setup (External Database)
+Perfect for production environments with existing database infrastructure.
+
+#### 1. Environment Configuration
 ```bash
-cp .env.external .env
+# Copy and edit environment file
+cp backend/.env.local backend/.env.local
+nano backend/.env.local
 ```
 
-Edit the `.env` file with your external database configuration:
+Edit the `.env.local` file with your external database configuration:
 
 ```bash
 # External Database Configuration
@@ -47,55 +48,163 @@ COMPANY_Y_TUNNUS=1234567-8
 UPLOAD_MAX_SIZE=10M
 ALLOWED_FILE_TYPES=pdf,doc,docx,xls,xlsx,jpg,jpeg,png,gif
 UPLOADS_PATH=./uploads
+```
+
+#### 2. Start the Application
+```bash
+docker-compose up -d
+```
+
+#### 3. Access the Application
+- Frontend: `http://localhost:80`
+- Uses your external database
 
-# Frontend Configuration
-VUE_APP_API_URL=http://localhost:8080
+### Option 2: Full Setup (Built-in Database + Cache)
+Perfect for development, testing, or all-in-one deployments.
 
-# Port Configuration
-BACKEND_PORT=8080
-FRONTEND_PORT=3000
-REDIS_PORT=6379
+#### 1. Start with Built-in Services
+```bash
+docker-compose -f docker-compose-with-services.yml --env-file .env.with-services up -d
 ```
 
-### 2. Database Setup
-Before starting the containers, ensure your external database is ready:
+#### 2. Access the Application
+- Frontend: `http://localhost:80`
+- Database: MariaDB on port 3306
+- Redis Cache: Redis on port 6379
+
+#### 3. Default Credentials
+- Database: `inventory_user` / `inventory_password`
+- Root Database: `root` / `root_password`
+- Redis: Password protected with `redis_password`
+
+## Services Overview
+
+### Basic Setup Services
+- **inventory-app**: Complete application (PHP + Apache + Vue.js)
+- **Volumes**: Uploads directory mounted to host
+
+### Full Setup Services
+- **inventory-app**: Complete application
+- **mariadb**: MariaDB 10.11 database server
+- **redis**: Redis 7 cache server
+- **Volumes**: Database persistence, Redis persistence, Uploads directory
+
+## Configuration Details
+
+### Environment Variables
+
+#### Basic Setup Variables
+- `DB_HOST`: External database server hostname (required)
+- `DB_PORT`: Database port (default: 3306)
+- `DB_NAME`: Database name (default: inventory_db)
+- `DB_USER`: Database username (required)
+- `DB_PASS`: Database password (required)
+
+#### Full Setup Variables
+- `DB_HOST`: Database hostname (default: mariadb)
+- `DB_NAME`: Database name (default: inventory_db)
+- `DB_USER`: Database user (default: inventory_user)
+- `DB_PASS`: Database password (default: inventory_password)
+- `MYSQL_ROOT_PASSWORD`: MariaDB root password (default: root_password)
+- `REDIS_HOST`: Redis hostname (default: redis)
+- `REDIS_PORT`: Redis port (default: 6379)
+- `REDIS_PASSWORD`: Redis password (default: redis_password)
+
+#### Company Configuration
+- `COMPANY_NAME`: Company name for invoices and reports
+- `COMPANY_ADDRESS`: Company address
+- `COMPANY_CITY`: Company city
+- `COMPANY_COUNTRY`: Company country
+- `COMPANY_PHONE`: Company phone number
+- `COMPANY_EMAIL`: Company email
+- `COMPANY_Y_TUNNUS`: Finnish business ID
+
+#### File Upload Configuration
+- `UPLOAD_MAX_SIZE`: Maximum file upload size (default: 10M)
+- `ALLOWED_FILE_TYPES`: Allowed file extensions
+- `UPLOADS_PATH`: Uploads directory path
+
+## Database Setup
+
+### For Basic Setup (External Database)
+Create database and user on your external database server:
 
 ```sql
 -- Create database and user (MySQL example)
 CREATE DATABASE inventory_db;
-CREATE USER 'your-db-username'@'%' IDENTIFIED BY 'your-db-password';
-GRANT ALL PRIVILEGES ON inventory_db.* TO 'your-db-username'@'%';
+CREATE USER 'your-username'@'%' IDENTIFIED BY 'your-password';
+GRANT ALL PRIVILEGES ON inventory_db.* TO 'your-username'@'%';
 FLUSH PRIVILEGES;
 ```
 
-Import the database schema:
+Import the complete database schema:
 ```bash
-mysql -h your-external-db-host -u your-db-username -p inventory_db < database/init.sql
+mysql -h your-db-host -u your-username -p inventory_db < backend/setup_database.sql
 ```
 
-### 3. Build and Start Containers
+### For Full Setup (Built-in Database)
+Database is automatically created when containers start with the schema from `backend/setup_database.sql`.
+
+## Container Management
+
+### Start Services
+```bash
+# Basic setup
+docker-compose up -d
+
+# Full setup
+docker-compose -f docker-compose-with-services.yml --env-file .env.with-services up -d
+```
+
+### Stop Services
+```bash
+# Basic setup
+docker-compose down
+
+# Full setup
+docker-compose -f docker-compose-with-services.yml down
+```
+
+### View Logs
+```bash
+# Basic setup
+docker-compose logs -f inventory-app
+
+# Full setup
+docker-compose -f docker-compose-with-services.yml logs -f
+```
+
+### Rebuild Container
 ```bash
 docker-compose up -d --build
 ```
 
-### 4. Access the Application
-- Frontend: http://localhost:${FRONTEND_PORT:-3000}
-- Backend API: http://localhost:${BACKEND_PORT:-8080}
-- Database: your-external-db-host:3306 (with your configured credentials)
-- Redis: localhost:${REDIS_PORT:-6379} (if enabled)
+## Troubleshooting
 
-## Configuration Details
+### Database Connection Issues
+1. Verify database credentials in environment file
+2. Check if database server is accessible
+3. Ensure database user has proper permissions
+4. For local development, check if MySQL socket exists
 
-### Environment Variables
+### Port Conflicts
+1. Change ports in environment variables
+2. Stop conflicting services
+3. Use `docker ps` to check running containers
 
-#### Database Configuration
-- `DB_HOST`: External database server hostname (required)
-- `DB_PORT`: Database port (default: 3306)
-- `DB_NAME`: Database name (default: inventory_db)
-- `DB_USER`: Database username (required)
-- `DB_PASS`: Database password (required)
+### File Upload Issues
+1. Ensure uploads directory exists and is writable
+2. Check file permissions on mounted volume
+3. Verify UPLOAD_MAX_SIZE setting
 
-#### Port Configuration
+### Health Checks
+```bash
+# Check container health
+docker-compose ps
+
+# View health check logs
+docker-compose logs inventory-app | grep health
+```
 - `BACKEND_PORT`: Backend service port (default: 8080)
 - `FRONTEND_PORT`: Frontend service port (default: 3000)
 - `REDIS_PORT`: Redis service port (default: 6379)

+ 101 - 7
README.md

@@ -99,14 +99,61 @@ The system uses a normalized database structure with proper relationships:
 ## 🚀 **Getting Started**
 
 ### **Prerequisites**
-- PHP 7.4+ with MySQL/MariaDB extension
-- Node.js 14+ and npm
+- Docker and Docker Compose (recommended)
+- OR PHP 7.4+ with MySQL/MariaDB extension
+- Node.js 14+ and npm (for development)
 - Modern web browser (Chrome, Firefox, Safari, Edge)
-- MySQL server or MariaDB
 
 ### **Quick Setup**
 
-#### **Option 1: Fresh Installation**
+#### **Option 1: Docker Deployment (Recommended)**
+
+##### **Basic Setup (External Database)**
+1. **Clone the repository:**
+   ```bash
+   git clone <repository-url>
+   cd inventory
+   ```
+
+2. **Configure database connection:**
+   ```bash
+   # Copy and edit environment file
+   cp backend/.env.local backend/.env.local
+   nano backend/.env.local
+   
+   # Set your external database credentials:
+   DB_HOST=your_database_host
+   DB_NAME=inventory_db
+   DB_USER=your_username
+   DB_PASS=your_password
+   ```
+
+3. **Start the application:**
+   ```bash
+   docker-compose up -d
+   ```
+
+4. **Access Application:**
+   - Frontend: `http://localhost:80`
+
+##### **Full Setup (Built-in Database + Cache)**
+1. **Clone the repository:**
+   ```bash
+   git clone <repository-url>
+   cd inventory
+   ```
+
+2. **Start with built-in services:**
+   ```bash
+   docker-compose -f docker-compose-with-services.yml --env-file .env.with-services up -d
+   ```
+
+3. **Access Application:**
+   - Frontend: `http://localhost:80`
+   - Database: MariaDB on port 3306
+   - Redis Cache: Redis on port 6379
+
+#### **Option 2: Manual Installation**
 1. **Clone the repository:**
    ```bash
    git clone <repository-url>
@@ -116,7 +163,7 @@ The system uses a normalized database structure with proper relationships:
 2. **Database Setup:**
    ```bash
    # Create database and import complete schema
-   mysql -u root -p < backend/migrate_complete.sql
+   mysql -u root -p < backend/setup_database.sql
    
    # Update database credentials
    nano backend/config/database.php
@@ -160,8 +207,55 @@ The system uses a normalized database structure with proper relationships:
 
 ## 🔧 **Configuration**
 
-### **Database Configuration**
-Update `backend/config/database.php` with your database credentials:
+### **Docker Environment Variables**
+
+#### **Basic Setup (.env.local)**
+```bash
+# Database Configuration
+DB_HOST=your_database_host
+DB_PORT=3306
+DB_NAME=inventory_db
+DB_USER=your_username
+DB_PASS=your_password
+
+# Company Information
+COMPANY_NAME=Inventory Management
+COMPANY_ADDRESS=123 Business St
+COMPANY_CITY=Helsinki
+COMPANY_COUNTRY=Finland
+COMPANY_PHONE=+358 123 456 789
+COMPANY_EMAIL=info@company.com
+COMPANY_Y_TUNNUS=1234567-8
+
+# File Upload Configuration
+UPLOAD_MAX_SIZE=10M
+ALLOWED_FILE_TYPES=pdf,doc,docx,xls,xlsx,jpg,jpeg,png,gif
+UPLOADS_PATH=./uploads
+```
+
+#### **Full Services Setup (.env.with-services)**
+```bash
+# Database Configuration (Built-in MariaDB)
+DB_HOST=mariadb
+DB_PORT=3306
+DB_NAME=inventory_db
+DB_USER=inventory_user
+DB_PASS=inventory_password
+MYSQL_ROOT_PASSWORD=root_password
+
+# Redis Configuration (Built-in Redis)
+REDIS_HOST=redis
+REDIS_PORT=6379
+REDIS_PASSWORD=redis_password
+
+# Frontend Configuration
+FRONTEND_PORT=80
+MARIADB_PORT=3306
+REDIS_PORT=6379
+```
+
+### **Manual Database Configuration**
+For manual installation, update `backend/config/database.php`:
 ```php
 <?php
 class Database {

+ 25 - 4
backend/config/database.php

@@ -35,7 +35,27 @@ class Database {
     public function getConnection() {
         $this->conn = null;
 
-        // Validate required database configuration
+        // Check for local MySQL socket first
+        $localSocketPath = '/var/run/mysqld/mysqld.sock';
+        $localSocketExists = file_exists($localSocketPath);
+        
+        // Try local database connection first if socket exists
+        if ($localSocketExists) {
+            try {
+                $dsn = "mysql:unix_socket=" . $localSocketPath . ";dbname=" . $this->db_name . ";charset=utf8mb4";
+                error_log("Attempting local database connection via socket: $dsn with user " . $this->username);
+                $this->conn = new PDO($dsn, $this->username, $this->password);
+                $this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+                $this->conn->exec("set names utf8");
+                error_log("Local database connection successful via socket");
+                return $this->conn;
+            } catch(PDOException $exception) {
+                error_log("Local database connection failed: " . $exception->getMessage());
+                // Fall through to external connection
+            }
+        }
+
+        // Try external database connection
         if (empty($this->host) || empty($this->db_name) || empty($this->username)) {
             error_log("Database configuration missing: Host=" . ($this->host ?: 'MISSING') . 
                      ", DB=" . ($this->db_name ?: 'MISSING') . 
@@ -45,13 +65,14 @@ class Database {
 
         try {
             $dsn = "mysql:host=" . $this->host . ";dbname=" . $this->db_name . ";charset=utf8mb4";
-            error_log("Attempting database connection: $dsn with user " . $this->username);
+            $connectionType = $localSocketExists ? "external (local socket failed)" : "external";
+            error_log("Attempting $connectionType database connection: $dsn with user " . $this->username);
             $this->conn = new PDO($dsn, $this->username, $this->password);
             $this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
             $this->conn->exec("set names utf8");
-            error_log("Database connection successful");
+            error_log("$connectionType database connection successful");
         } catch(PDOException $exception) {
-            error_log("Database connection error: " . $exception->getMessage());
+            error_log("External database connection error: " . $exception->getMessage());
             error_log("Connection details - Host: " . $this->host . ", DB: " . $this->db_name . ", User: " . $this->username);
             throw new Exception("Database connection failed: " . $exception->getMessage());
         }

+ 311 - 4
backend/setup_database.sql

@@ -230,7 +230,314 @@ CREATE TABLE IF NOT EXISTS subprojects (
     INDEX idx_dates (start_date, end_date)
 );
 
-INSERT INTO items (name, description, quantity, price) VALUES 
-('Laptop', 'Dell XPS 15 laptop', 5, 1299.99),
-('Mouse', 'Wireless optical mouse', 20, 25.50),
-('Keyboard', 'Mechanical keyboard', 15, 89.99);
+-- Accounting tables
+CREATE TABLE IF NOT EXISTS accounting_entries (
+    id INT AUTO_INCREMENT PRIMARY KEY,
+    entry_date DATE NOT NULL,
+    description TEXT NOT NULL,
+    entry_type ENUM('Tulo', 'Kulu') NOT NULL,
+    category VARCHAR(100),
+    tax_free_amount DECIMAL(12,2) DEFAULT 0.00,
+    vat_percentage DECIMAL(5,2) DEFAULT 0.00,
+    vat_25_5 DECIMAL(12,2) DEFAULT 0.00,
+    vat_14 DECIMAL(12,2) DEFAULT 0.00,
+    vat_10 DECIMAL(12,2) DEFAULT 0.00,
+    total_amount DECIMAL(12,2) DEFAULT 0.00,
+    net_amount DECIMAL(12,2) DEFAULT 0.00,
+    vat_amount DECIMAL(12,2) DEFAULT 0.00,
+    reference_number VARCHAR(50),
+    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    INDEX idx_entry_date (entry_date),
+    INDEX idx_entry_type (entry_type),
+    INDEX idx_category (category)
+);
+
+CREATE TABLE IF NOT EXISTS accounting_categories (
+    id INT AUTO_INCREMENT PRIMARY KEY,
+    category_code VARCHAR(20) NOT NULL,
+    category_name VARCHAR(200) NOT NULL,
+    category_type ENUM('Tulo', 'Kulu') NOT NULL,
+    vat_percentage DECIMAL(5,2) DEFAULT 0.00,
+    is_active BOOLEAN DEFAULT TRUE,
+    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    UNIQUE KEY unique_category_code (category_code)
+);
+
+CREATE TABLE IF NOT EXISTS accounting_category_group_names (
+    id INT AUTO_INCREMENT PRIMARY KEY,
+    category_group_code VARCHAR(3) NOT NULL,
+    category_group_name VARCHAR(200) NOT NULL,
+    description TEXT NULL,
+    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    UNIQUE KEY unique_category_group_code (category_group_code)
+);
+
+CREATE TABLE IF NOT EXISTS monthly_summaries (
+    id INT AUTO_INCREMENT PRIMARY KEY,
+    year INT NOT NULL,
+    month INT NOT NULL,
+    total_income DECIMAL(12,2) DEFAULT 0.00,
+    total_expenses DECIMAL(12,2) DEFAULT 0.00,
+    net_result DECIMAL(12,2) DEFAULT 0.00,
+    vat_payable DECIMAL(12,2) DEFAULT 0.00,
+    vat_deductible DECIMAL(12,2) DEFAULT 0.00,
+    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    UNIQUE KEY unique_year_month (year, month)
+);
+
+-- Additional tables for project management
+CREATE TABLE IF NOT EXISTS costs (
+    id INT(11) AUTO_INCREMENT PRIMARY KEY,
+    description VARCHAR(255) NOT NULL,
+    amount DECIMAL(10,2) NOT NULL,
+    cost_date DATE NOT NULL,
+    category VARCHAR(100) NOT NULL DEFAULT 'other',
+    supplier VARCHAR(255) NULL,
+    invoice_number VARCHAR(100) NULL,
+    account_id INT(11) NULL,
+    payment_method VARCHAR(50) NULL,
+    receipt_number VARCHAR(100) NULL,
+    notes TEXT NULL,
+    created_by INT(11) NULL,
+    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    INDEX idx_cost_date (cost_date),
+    INDEX idx_category (category),
+    INDEX idx_supplier (supplier),
+    INDEX idx_account_id (account_id),
+    INDEX idx_created_by (created_by)
+);
+
+CREATE TABLE IF NOT EXISTS timers (
+    id INT(11) AUTO_INCREMENT PRIMARY KEY,
+    task_id INT(11) NULL,
+    user_id INT(11) NOT NULL,
+    start_time TIMESTAMP NULL,
+    end_time TIMESTAMP NULL,
+    duration VARCHAR(8) NULL,
+    description TEXT NULL,
+    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    INDEX idx_task_id (task_id),
+    INDEX idx_user_id (user_id),
+    INDEX idx_start_time (start_time)
+);
+
+CREATE TABLE IF NOT EXISTS expense_categories (
+    id INT(11) AUTO_INCREMENT PRIMARY KEY,
+    category_code VARCHAR(50) NOT NULL UNIQUE,
+    category_name VARCHAR(200) NOT NULL,
+    parent_id INT(11) NULL,
+    description TEXT NULL,
+    is_active TINYINT(1) DEFAULT 1,
+    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    INDEX idx_parent_id (parent_id)
+);
+
+CREATE TABLE IF NOT EXISTS income_categories (
+    id INT(11) AUTO_INCREMENT PRIMARY KEY,
+    category_code VARCHAR(50) NOT NULL UNIQUE,
+    category_name VARCHAR(200) NOT NULL,
+    parent_id INT(11) NULL,
+    description TEXT NULL,
+    is_active TINYINT(1) DEFAULT 1,
+    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    INDEX idx_parent_id (parent_id)
+);
+
+CREATE TABLE IF NOT EXISTS incomes (
+    id INT(11) AUTO_INCREMENT PRIMARY KEY,
+    description VARCHAR(255) NOT NULL,
+    amount DECIMAL(10,2) NOT NULL,
+    income_date DATE NOT NULL,
+    category VARCHAR(100) NOT NULL DEFAULT 'other',
+    client_id INT(11) NULL,
+    invoice_id INT(11) NULL,
+    account_id INT(11) NULL,
+    payment_method VARCHAR(50) NULL,
+    reference_number VARCHAR(100) NULL,
+    notes TEXT NULL,
+    created_by INT(11) NULL,
+    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    INDEX idx_income_date (income_date),
+    INDEX idx_category (category),
+    INDEX idx_client_id (client_id),
+    INDEX idx_invoice_id (invoice_id),
+    INDEX idx_account_id (account_id),
+    INDEX idx_created_by (created_by)
+);
+
+CREATE TABLE IF NOT EXISTS work_hours (
+    id INT(11) AUTO_INCREMENT PRIMARY KEY,
+    task_id INT(11) NOT NULL,
+    user_id INT(11) NOT NULL,
+    date DATE NOT NULL,
+    hours DECIMAL(5,2) NOT NULL,
+    description TEXT NULL,
+    rate DECIMAL(10,2) NULL,
+    total_amount DECIMAL(10,2) NULL,
+    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    INDEX idx_task_id (task_id),
+    INDEX idx_user_id (user_id),
+    INDEX idx_date (date)
+);
+
+-- Insert wavium admin user
+INSERT INTO users (username, email, password_hash, first_name, last_name, role, is_active) VALUES 
+('weikka', 'weikka@wavium.fi', '$2y$12$ZFHPKWs2AcaaYlwno7kkWOoaD0cHNWikf8kwBnetYo/QXvG8smcD6', 'Weikka', 'Valavuo', 'admin', TRUE);
+
+-- Insert expense categories data
+INSERT IGNORE INTO expense_categories (category_code, category_name, parent_id, description, is_active) VALUES
+('333', 'Ostot ja ulkopuoliset palvelut', NULL, 'Purchases and external services', 1),
+('334', 'Henkilöstökulut', NULL, 'Personnel costs', 1),
+('335', 'Poistot, edustuskulut ja vuokrat', NULL, 'Depreciation, representation and rental costs', 1),
+('336', 'Muut vähennyskelpoiset kulut', NULL, 'Other deductible expenses', 1),
+('337', 'Rahoituskulut', NULL, 'Financing costs', 1),
+('341', 'Varausten muutokset', NULL, 'Asset changes', 1),
+('343', 'Varausten myynnit', NULL, 'Asset sales', 1),
+('344', 'Matkakulut', NULL, 'Travel expenses', 1),
+('344 Ajoneuvo', 'Matkakulut - Kulkuneuvo', NULL, 'Vehicle travel expenses', 1),
+('344 Kokous', 'Matkakulut - Kokous', NULL, 'Meeting travel expenses', 0),
+('344 Matka-auto', 'Matkakulut - Auto', NULL, 'Car travel expenses', 1),
+('344 Muu', 'Matkakulut - Muu', NULL, 'Other travel expenses', 1),
+('346', 'Muut kulut', NULL, 'Other expenses', 1),
+('349', 'Poistot', NULL, 'Depreciation', 1),
+('353', 'Varainsiirrot', NULL, 'Asset transfers', 1),
+('354', 'Vähennyskelvottomat kulut', NULL, 'Non-deductible expenses', 1),
+('365', 'Vakuutukset', NULL, 'Insurance', 1),
+('366', 'Verot ja maksut', NULL, 'Taxes and fees', 1),
+('367', 'Yhtiölainat ja muut rahoituskulut', NULL, 'Company loans and other financing costs', 1);
+
+-- Insert income categories data
+INSERT IGNORE INTO income_categories (category_code, category_name, parent_id, description, is_active) VALUES
+('300', 'Myynti - EU', NULL, 'Sales - EU', 1),
+('300 - 255', 'Myynti - EU 25,5%', NULL, 'Sales - EU 25,5%', 1),
+('301', 'Myynti - Muut maat', NULL, 'Sales - Other countries', 1),
+('302', 'Myynti - Käänteinen vero', NULL, 'Sales - Reverse VAT', 1),
+('303', 'Myynti - Suomi 0%', NULL, 'Sales - Finland 0%', 1),
+('303 - 255', 'Myynti - Suomi 25,5%', NULL, 'Sales - Finland 25,5%', 1),
+('308', 'Myyntitavarat', NULL, 'Sales of fixed assets', 1),
+('309', 'Osuuskorvaukset', NULL, 'Share compensation', 1),
+('310', 'Korkotulot ja muut rahoitustuotot', NULL, 'Interest income and other financial income', 1),
+('311', 'Voitonjako', NULL, 'Profit distribution', 1),
+('312', 'Sijoitustoiminnan tuotot', NULL, 'Investment income', 1),
+('313', 'Apurahat ja muut avustukset', NULL, 'Grants and other subsidies', 1),
+('314', 'Muu tuotto', NULL, 'Other income', 1);
+
+-- Insert default accounting categories
+INSERT IGNORE INTO accounting_categories (category_code, category_name, category_type, vat_percentage) VALUES
+('300 - E0', 'Myynti - EU 0%', 'Tulo', 0.00),
+('300 - E255', 'Myynti - EU 25,5%', 'Tulo', 25.50),
+('300 - K', 'Myynti - Käänteinen vero', 'Tulo', 0.00),
+('300 - M0', 'Myynti - Muut maat', 'Tulo', 0.00),
+('300 - S0', 'Myynti - Suomi 0%', 'Tulo', 0.00),
+('300 - S255', 'Myynti - Suomi 25,5%', 'Tulo', 25.50),
+('301', 'Muut tuotot', 'Tulo', 0.00),
+('312', 'Varausten vähennys', 'Tulo', 0.00),
+('313', 'Auton yksityiskäyttö', 'Tulo', 0.00),
+('314', 'Tavaroiden yksityiskäyttö', 'Tulo', 0.00),
+('315', 'Muu yksityiskäyttö', 'Tulo', 0.00),
+('317', 'Tuloslaskelman verovapaat tuotot', 'Tulo', 0.00),
+('318', 'Saadut avustukset ja tuet', 'Tulo', 0.00),
+('319', 'Saadut osingot', 'Tulo', 0.00),
+('320', 'Osinkojen veronalainen osuus', 'Tulo', 0.00),
+('323', 'Korkotuotot ja muut rahoitustuotot', 'Tulo', 0.00),
+('324', 'Muut veronalaiset tuotot', 'Tulo', 0.00),
+('333', 'Ostot ja varastojen muutokset', 'Kulu', 25.50),
+('334', 'Ulkopuoliset palvelut', 'Kulu', 25.50),
+('335', 'Palkat ja palkkiot', 'Kulu', 0.00),
+('336', 'Eläke- ja henkilösivukulut', 'Kulu', 0.00),
+('337', 'Poistot', 'Kulu', 0.00),
+('341', 'Edustuskulut', 'Kulu', 0.00),
+('343', 'Vuokrat', 'Kulu', 25.50),
+('344 - A', 'Ajoneuvokulut', 'Kulu', 25.50),
+('344 - K', 'Kokous- ja neuvottelukulut', 'Kulu', 25.50),
+('344 - M', 'Matkakulut', 'Kulu', 25.50),
+('344 - O', 'Muut vähennyskelpoiset kulut', 'Kulu', 25.50),
+('346', 'Korkokulut', 'Kulu', 0.00),
+('349', 'Muut rahoituskulut', 'Kulu', 0.00),
+('353', 'Varausten lisäykset', 'Kulu', 0.00),
+('354', 'Kirjanpidon ulkopuoliset vähennyskelpoiset kulut', 'Kulu', 0.00),
+('365', 'Välittömät verot', 'Kulu', 0.00),
+('366', 'Sakot ja muut rangaistusmaksut', 'Kulu', 0.00),
+('367', 'Muut vähennyskelvottomat kulut', 'Kulu', 0.00),
+('368', 'Julkinen liikenne', 'Kulu', 10.00);
+
+-- Insert default category groups
+INSERT IGNORE INTO accounting_category_group_names (category_group_code, category_group_name, description) VALUES
+('300', 'Tuotot ammatista', NULL),
+('301', 'Muut tuotot', NULL),
+('312', 'Varausten vähennys', NULL),
+('313', 'Auton yksityiskäyttö', NULL),
+('314', 'Tavaroiden yksityiskäyttö', NULL),
+('315', 'Muu yksityiskäyttö', NULL),
+('317', 'Tuloslaskelman verovapaat tuotot', NULL),
+('318', 'Saadut avustukset ja tuet', NULL),
+('319', 'Saadut osingot', NULL),
+('323', 'Korkotuotot ja muut rahoitustuotot', NULL),
+('324', 'Muut veronalaiset tuotot', NULL),
+('333', 'Ostot ja varastojen muutokset', NULL),
+('334', 'Ulkopuoliset palvelut', NULL),
+('335', 'Palkat ja palkkiot', NULL),
+('336', 'Eläke- ja henkilösivukulut', NULL),
+('337', 'Poistot', NULL),
+('341', 'Edustuskulut', NULL),
+('343', 'Vuokrat', NULL),
+('344', 'Matkakulut', NULL),
+('346', 'Korkokulut', NULL),
+('349', 'Muut rahoituskulut', NULL),
+('353', 'Varausten lisäykset', NULL),
+('354', 'Kirjanpidon ulkopuoliset vähennyskelpoiset kulut', NULL),
+('358', 'Elinkeinotoiminnan tulos', NULL),
+('359', 'Elinkeinotoiminnan tappio', NULL),
+('365', 'Välittömät verot', NULL),
+('366', 'Sakot ja muut rangaistusmaksut', NULL),
+('367', 'Muut vähennyskelvottomat kulut', NULL),
+('368', 'Julkinen liikenne', NULL);
+
+-- Insert chart of accounts
+INSERT IGNORE INTO chart_of_accounts (account_number, account_name, account_type, parent_id, description, opening_balance, current_balance, is_active) VALUES
+('1000', 'Käyttöpääoma', 'asset', NULL, 'Pääomaisuus', 0.00, 0.00, TRUE),
+('1100', 'Saamiset ja vaatimukset', 'asset', NULL, 'Tase-erät ja poistot', 0.00, 0.00, TRUE),
+('1200', 'Aineelliset vaihto-omaisuudet', 'asset', NULL, 'Varasto ja raaka-aineet', 0.00, 0.00, TRUE),
+('1300', 'Pitkän aikaiset sijoitukset', 'asset', NULL, 'Pitkän aikaiset sijoitukset', 0.00, 0.00, TRUE),
+('1400', 'Kiinteistöt', 'asset', NULL, 'Kiinteistöt ja rakennukset', 0.00, 0.00, TRUE),
+('1500', 'Koneet ja kalusto', 'asset', NULL, 'Koneet, kalusto ja laitteet', 0.00, 0.00, TRUE),
+('1600', 'Aineettomat varat', 'asset', NULL, 'Aineettomat varat', 0.00, 0.00, TRUE),
+('1700', 'Sijoitukset rahoituslaitoksissa', 'asset', NULL, 'Rahoituslaitokset', 0.00, 0.00, TRUE),
+('1800', 'Erityiset vaihto-omaisuudet', 'asset', NULL, 'Erityiset vaihto-omaisuudet', 0.00, 0.00, TRUE),
+('1900', 'Vaihto-omaisuudet luovutukseen', 'asset', NULL, 'Myyntikohteiset varat', 0.00, 0.00, TRUE),
+('2000', 'Oma pääoma', 'liability', NULL, 'Oma pääoma', 0.00, 0.00, TRUE),
+('2100', 'Pitkän aikaiset velat', 'liability', NULL, 'Pitkän aikaiset velat', 0.00, 0.00, TRUE),
+('2200', 'Verovelat', 'liability', NULL, 'Verovelat ja vakuudet', 0.00, 0.00, TRUE),
+('2300', 'Ostovelat ja saamiset', 'liability', NULL, 'Ostovelat ja saamiset', 0.00, 0.00, TRUE),
+('2400', 'Maksuvelat ja ennakot', 'liability', NULL, 'Maksuvelat ja saadut ennakot', 0.00, 0.00, TRUE),
+('2500', 'Verovelat konserneille', 'liability', NULL, 'Verovelat konserneille', 0.00, 0.00, TRUE),
+('2600', 'Muut velat', 'liability', NULL, 'Muut velat', 0.00, 0.00, TRUE),
+('2700', 'Siirrot konserninointiin', 'liability', NULL, 'Siirrot konserninointiin', 0.00, 0.00, TRUE),
+('2800', 'Tilikauden tuloverot', 'liability', NULL, 'Tilikauden tuloverot', 0.00, 0.00, TRUE),
+('3000', 'Osakepääoma', 'equity', NULL, 'Osakepääoma', 0.00, 0.00, TRUE),
+('3100', 'Sijoitetun pääoman rahastot', 'equity', NULL, 'Sijoitetun pääoman rahastot', 0.00, 0.00, TRUE),
+('3200', 'Muut pääomat rahastot', 'equity', NULL, 'Muut pääomat rahastot', 0.00, 0.00, TRUE),
+('3300', 'Tilikauden voitto', 'equity', NULL, 'Tilikauden voitto/tappio', 0.00, 0.00, TRUE),
+('4000', 'Myyntituotot', 'revenue', NULL, 'Myyntituotot', 0.00, 0.00, TRUE),
+('4100', 'Palvelutuotot', 'revenue', NULL, 'Palvelutuotot', 0.00, 0.00, TRUE),
+('4200', 'Vuokratuotot', 'revenue', NULL, 'Vuokratuotot', 0.00, 0.00, TRUE),
+('4300', 'Korko- ja valuuttatuotot', 'revenue', NULL, 'Korko- ja valuuttatuotot', 0.00, 0.00, TRUE),
+('4400', 'Muu tuotto', 'revenue', NULL, 'Muu tuotto', 0.00, 0.00, TRUE),
+('4500', 'Siirrot konserninointiin', 'revenue', NULL, 'Siirrot konserninointiin', 0.00, 0.00, TRUE),
+('5000', 'Aineelliset kulut', 'expense', NULL, 'Aineelliset kulut ja palvelut', 0.00, 0.00, TRUE),
+('5100', 'Henkilöstökulut', 'expense', NULL, 'Henkilöstökulut', 0.00, 0.00, TRUE),
+('5200', 'Poistot ja alennukset', 'expense', NULL, 'Poistot ja alennukset', 0.00, 0.00, TRUE),
+('5300', 'Vuokrat ja vuokrakulut', 'expense', NULL, 'Vuokrat ja vuokrakulut', 0.00, 0.00, TRUE),
+('5400', 'Korko- ja rahoituskulut', 'expense', NULL, 'Korko- ja rahoituskulut', 0.00, 0.00, TRUE),
+('5500', 'Tase-erät ja poistot', 'expense', NULL, 'Tase-erät ja poistot', 0.00, 0.00, TRUE),
+('5600', 'Arvonmäärityksen alennukset', 'expense', NULL, 'Arvonmäärityksen alennukset', 0.00, 0.00, TRUE),
+('5700', 'Verotukset ja maksut', 'expense', NULL, 'Verotukset ja maksut', 0.00, 0.00, TRUE),
+('5800', 'Muut kulut', 'expense', NULL, 'Muut kulut', 0.00, 0.00, TRUE),
+('5900', 'Siirrot konserninointiin', 'expense', NULL, 'Siirrot konserninointiin', 0.00, 0.00, TRUE);

+ 97 - 0
docker-compose-with-services.yml

@@ -0,0 +1,97 @@
+version: '3.8'
+
+services:
+  # Complete Inventory Solution (Single Container)
+  inventory-app:
+    build:
+      context: .
+      dockerfile: Dockerfile
+    container_name: inventory-app
+    ports:
+      - "${FRONTEND_PORT:-80}:80"
+    environment:
+      - DB_HOST=${DB_HOST:-mariadb}
+      - DB_PORT=${DB_PORT:-3306}
+      - DB_NAME=${DB_NAME:-inventory_db}
+      - DB_USER=${DB_USER:-inventory_user}
+      - DB_PASS=${DB_PASS:-inventory_password}
+      - REDIS_HOST=${REDIS_HOST:-redis}
+      - REDIS_PORT=${REDIS_PORT:-6379}
+      - COMPANY_NAME=${COMPANY_NAME:-Inventory Management}
+      - COMPANY_ADDRESS=${COMPANY_ADDRESS:-123 Business St}
+      - COMPANY_CITY=${COMPANY_CITY:-Helsinki}
+      - COMPANY_POSTAL_CODE=${COMPANY_POSTAL_CODE:-00100}
+      - COMPANY_COUNTRY=${COMPANY_COUNTRY:-Finland}
+      - COMPANY_PHONE=${COMPANY_PHONE:-+358 123 456 789}
+      - COMPANY_EMAIL=${COMPANY_EMAIL:-info@company.com}
+      - COMPANY_Y_TUNNUS=${COMPANY_Y_TUNNUS:-1234567-8}
+      - UPLOAD_MAX_SIZE=${UPLOAD_MAX_SIZE:-10M}
+      - ALLOWED_FILE_TYPES=${ALLOWED_FILE_TYPES:-pdf,doc,docx,xls,xlsx,jpg,jpeg,png,gif}
+    volumes:
+      - ./uploads:/var/www/html/uploads
+    restart: unless-stopped
+    depends_on:
+      - mariadb
+      - redis
+    healthcheck:
+      test: ["CMD", "curl", "-f", "http://localhost/api/company.php"]
+      interval: 30s
+      timeout: 10s
+      retries: 3
+      start_period: 40s
+
+  # MariaDB Database Service
+  mariadb:
+    image: mariadb:10.11
+    container_name: inventory-mariadb
+    ports:
+      - "${MARIADB_PORT:-3306}:3306"
+    environment:
+      - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-root_password}
+      - MYSQL_DATABASE=${DB_NAME:-inventory_db}
+      - MYSQL_USER=${DB_USER:-inventory_user}
+      - MYSQL_PASSWORD=${DB_PASS:-inventory_password}
+    volumes:
+      - mariadb_data:/var/lib/mysql
+      - ./backend/setup_database.sql:/docker-entrypoint-initdb.d/01-setup_database.sql:ro
+    restart: unless-stopped
+    healthcheck:
+      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${MYSQL_ROOT_PASSWORD:-root_password}"]
+      interval: 30s
+      timeout: 10s
+      retries: 3
+      start_period: 40s
+
+  # Redis Cache Service
+  redis:
+    image: redis:7-alpine
+    container_name: inventory-redis
+    ports:
+      - "${REDIS_PORT:-6379}:6379"
+    volumes:
+      - redis_data:/data
+    restart: unless-stopped
+    command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD:-redis_password}
+    healthcheck:
+      test: ["CMD", "redis-cli", "--raw", "incr", "ping"]
+      interval: 30s
+      timeout: 10s
+      retries: 3
+      start_period: 40s
+
+volumes:
+  mariadb_data:
+    driver: local
+  redis_data:
+    driver: local
+  uploads_data:
+    driver: local
+    driver_opts:
+      type: none
+      o: bind
+      device: ${UPLOADS_PATH:-./uploads}
+
+networks:
+  default:
+    name: inventory-network
+    driver: bridge

+ 0 - 13
docker-compose.yml

@@ -35,20 +35,7 @@ services:
       retries: 3
       start_period: 40s
 
-  # Redis Cache Service (Optional)
-  redis:
-    image: redis:7-alpine
-    container_name: inventory-redis
-    ports:
-      - "${REDIS_PORT:-6379}:6379"
-    volumes:
-      - redis_data:/data
-    restart: unless-stopped
-    command: redis-server --appendonly yes
-
 volumes:
-  redis_data:
-    driver: local
   uploads_data:
     driver: local
     driver_opts: