# M1 ERP - System Architecture

Complete overview of the M1 ERP system architecture, design patterns, and technical implementation.

## Table of Contents

- [Overview](#overview)
- [Technology Stack](#technology-stack)
- [MVC Architecture](#mvc-architecture)
- [Database Design](#database-design)
- [Routing System](#routing-system)
- [Authentication & Authorization](#authentication--authorization)
- [Module Structure](#module-structure)
- [Core Components](#core-components)
- [Design Patterns](#design-patterns)
- [Performance & Monitoring](#performance--monitoring)

## Overview

M1 ERP (MERPH) is a **custom-built PHP MVC framework** designed specifically for enterprise resource planning. It follows modern PHP best practices while maintaining simplicity and flexibility.

**Key Characteristics:**
- Custom MVC framework (not Laravel/Symfony/CodeIgniter)
- Database-driven routing for menu-based pages
- Modular architecture with 178 controllers
- Multi-location support with data scoping
- Session-based authentication with MFA
- Role-based access control (RBAC)
- RESTful API endpoints

## Technology Stack

### Backend
- **PHP**: 8.2+ (8.5 compatible)
- **Database**: MariaDB 10.5+ / MySQL 8.0+
- **Web Server**: Apache 2.4+ or Nginx 1.20+
- **Session**: PHP native sessions
- **Email**: PHP mail() or SMTP

### Frontend
- **Template Engine**: Native PHP
- **CSS Framework**: Bootstrap 5.x
- **JavaScript**: jQuery 3.x + Vanilla JS
- **Icons**: Font Awesome 6.x
- **Charts**: Chart.js

### Development Tools
- **Dependency Manager**: Composer 2.5+
- **Testing**: PHPUnit 10.x
- **Code Quality**: PHPStan, PHPCS, PHPMD
- **Version Control**: Git + GitHub
- **CI/CD**: GitHub Actions

## MVC Architecture

### Request Flow

```
HTTP Request
    ↓
public/index.php (Entry Point)
    ↓
Router (core/Router.php)
    ↓
Controller (controllers/*Controller.php)
    ↓
Model (models/*.php) ←→ Database (core/Database.php)
    ↓
View (views/*.php)
    ↓
HTTP Response
```

### Directory Structure

```
m1_erp_web/
├── public/              # Web root (document root)
│   ├── index.php        # Entry point, all requests route through here
│   ├── assets/          # CSS, JS, images
│   └── uploads/         # User uploaded files
├── core/                # Core framework classes
│   ├── Controller.php   # Base controller
│   ├── Router.php       # URL routing
│   ├── Database.php     # Database singleton
│   ├── Auth.php         # Authentication
│   ├── Session.php      # Session management
│   ├── ErrorHandler.php # Error/exception handling
│   └── PerformanceMonitor.php # Performance tracking
├── controllers/         # Application controllers (178 files)
│   ├── AuthController.php
│   ├── CustomerController.php
│   └── ...
├── models/              # Data models
│   ├── Customer.php
│   ├── Invoice.php
│   └── ...
├── views/               # View templates
│   ├── layouts/         # Page layouts
│   ├── customers/       # Customer views
│   └── ...
├── includes/            # Helper functions
│   ├── helpers.php      # Global helpers
│   └── auto_routes.php  # Database route loader
├── database/
│   └── migrations/      # SQL migration files (274+)
├── scripts/             # CLI scripts
│   ├── run_all_migrations.php
│   └── monitor_health.php
├── tests/               # PHPUnit tests
│   ├── Unit/
│   └── Integration/
└── docs/                # Documentation
```

### Controllers

**Base Controller** (`core/Controller.php`):

```php
class Controller
{
    // Render view with layout (header/footer)
    protected function layout($view, $data = [])
    
    // Render standalone view
    protected function view($view, $data = [])
    
    // Return JSON response
    protected function json($data, $code = 200)
    
    // Validate input data
    protected function validate($data, $rules)
    
    // Check user permission
    protected function checkPermission($permission)
    
    // Require authentication
    protected function requireAuth()
}
```

**Example Controller:**

```php
class CustomerController extends Controller
{
    private $model;
    
    public function __construct()
    {
        $this->model = new Customer();
    }
    
    public function index()
    {
        $this->requireAuth();
        $this->checkPermission('customers.view');
        
        $customers = $this->model->getAll();
        $this->layout('customers/index', compact('customers'));
    }
    
    public function show($id)
    {
        $this->requireAuth();
        $this->checkPermission('customers.view');
        
        $customer = $this->model->getById($id);
        $this->layout('customers/show', compact('customer'));
    }
}
```

### Models

**No Base Model Class** - Models directly use Database singleton:

```php
class Customer
{
    private $db;
    
    public function __construct()
    {
        $this->db = Database::getInstance();
    }
    
    public function getAll()
    {
        $sql = "SELECT * FROM customers WHERE is_active = 1";
        $sql = applyDataScopeFilter($sql); // Multi-location filtering
        return $this->db->fetchAll($sql);
    }
    
    public function getById($id)
    {
        $sql = "SELECT * FROM customers WHERE id = ?";
        return $this->db->fetchOne($sql, [$id]);
    }
    
    public function create($data)
    {
        return $this->db->insert('customers', $data);
    }
}
```

### Views

**Layout Structure:**

```
views/layouts/app.php
├── header (views/layouts/header.php)
├── sidebar (views/layouts/sidebar.php)
├── content (actual view)
└── footer (views/layouts/footer.php)
```

**Example View** (`views/customers/index.php`):

```php
<?php
// View has access to variables from controller
// e.g., $customers from compact('customers')
?>

<div class="container">
    <h1>Customers</h1>
    
    <table class="table">
        <thead>
            <tr>
                <th>ID</th>
                <th>Name</th>
                <th>Email</th>
                <th>Actions</th>
            </tr>
        </thead>
        <tbody>
            <?php foreach ($customers as $customer): ?>
            <tr>
                <td><?= e($customer['id']) ?></td>
                <td><?= e($customer['name']) ?></td>
                <td><?= e($customer['email']) ?></td>
                <td>
                    <a href="<?= base_url('customers/view/' . $customer['id']) ?>">View</a>
                </td>
            </tr>
            <?php endforeach; ?>
        </tbody>
    </table>
</div>
```

## Database Design

### Schema Overview

**Core Tables:**
- `users` - System users
- `roles` - User roles
- `permissions` - Granular permissions
- `role_permissions` - Role-permission mapping
- `locations` - Physical locations/warehouses
- `menu_items` - Dynamic menu + routing

**Business Modules:** (100+ tables)
- Customers, Contacts, Opportunities
- Products, Inventory, Locations
- Sales Orders, Quotes, Invoices
- Purchase Orders, Suppliers
- Manufacturing, Work Orders, BOM
- HR, Employees, Payroll
- Accounting, Chart of Accounts, Journal Entries
- Projects, Tasks, Resources

### Data Access Layer

**Database Singleton** (`core/Database.php`):

```php
class Database
{
    private static $instance = null;
    private $pdo;
    
    public static function getInstance()
    {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }
    
    // Execute query with prepared statements
    public function query($sql, $params = [])
    
    // Fetch all rows
    public function fetchAll($sql, $params = [])
    
    // Fetch single row
    public function fetchOne($sql, $params = [])
    
    // Insert and return ID
    public function insert($table, $data)
    
    // Update rows
    public function update($table, $data, $where, $whereParams)
    
    // Delete rows
    public function delete($table, $where, $whereParams)
    
    // Transactions
    public function beginTransaction()
    public function commit()
    public function rollback()
}
```

### Migrations

**Sequential Numbering:**
- `001_initial_schema.sql`
- `002_add_customers.sql`
- `274_latest_feature.sql`

**Migration Format:**

```sql
-- Migration: Add customer preferences
-- Date: 2026-01-15
-- Description: Adds preference storage for customer portal settings

-- Check if column exists before adding
SET @col_exists = (
    SELECT COUNT(*) 
    FROM information_schema.columns 
    WHERE table_schema = DATABASE() 
    AND table_name = 'customers' 
    AND column_name = 'preferences'
);

-- Add column if it doesn't exist
SET @sql = IF(@col_exists = 0,
    'ALTER TABLE customers ADD COLUMN preferences JSON DEFAULT NULL',
    'SELECT "Column already exists"'
);

PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
```

### Data Scoping

**Multi-Location Support:**

```php
// Apply data scope filtering
$sql = "SELECT * FROM inventory WHERE product_id = ?";
$sql = applyDataScopeFilter($sql); // Adds location filtering

// With table alias
$sql = "SELECT i.* FROM inventory i WHERE product_id = ?";
$sql = applyDataScopeFilter($sql, 'i');

// User's accessible locations automatically filtered
```

## Routing System

### Database-Driven Routing

**Primary routing method** for GET routes (95% of routes):

```sql
-- Define route in database
INSERT INTO menu_items (
    label, url, controller, action, http_method,
    icon, permission_required, is_active
) VALUES (
    'Customers',
    '/customers',
    'CustomerController',
    'index',
    'GET',
    'fas fa-users',
    'customers.view',
    1
);
```

**Auto-registration** in `includes/auto_routes.php`:

```php
function registerDatabaseRoutes($router)
{
    $db = Database::getInstance();
    $routes = $db->fetchAll("
        SELECT url, controller, action, http_method
        FROM menu_items
        WHERE is_active = 1 
        AND controller IS NOT NULL
    ");
    
    foreach ($routes as $route) {
        $method = strtolower($route['http_method']);
        $router->$method(
            '/' . ltrim($route['url'], '/'),
            $route['controller'] . '@' . $route['action']
        );
    }
}
```

### Manual Routing

**For special cases** (regex patterns, POST/PUT/DELETE, APIs):

```php
// In public/index.php

// Regex pattern for dynamic routes
$router->get('/customers/([0-9]+)', 'CustomerController@show');

// POST routes (forms, APIs)
$router->post('/customers/store', 'CustomerController@store');
$router->put('/customers/([0-9]+)', 'CustomerController@update');
$router->delete('/customers/([0-9]+)', 'CustomerController@destroy');

// API endpoints
$router->get('/api/customers', 'APICustomerController@index');

// Webhooks (public, no auth)
$router->post('/webhook/payment', 'WebhookController@payment');
```

### Router Implementation

**Key Methods:**

```php
class Router
{
    public function get($pattern, $callback)
    public function post($pattern, $callback)
    public function put($pattern, $callback)
    public function delete($pattern, $callback)
    public function dispatch()
    public function notFound($callback)
}
```

## Authentication & Authorization

### Authentication

**Session-Based with MFA:**

```php
// core/Auth.php
class Auth
{
    // Login user
    public static function login($email, $password)
    
    // Logout user
    public static function logout()
    
    // Check if authenticated
    public static function check()
    
    // Get current user
    public static function user()
    
    // Verify MFA token
    public static function verifyMFA($code)
}
```

**Login Flow:**

```
1. User enters email/password
2. Credentials validated
3. MFA token sent (email/SMS)
4. User enters MFA code
5. MFA verified
6. Session created
7. Redirect to dashboard
```

### Authorization

**Role-Based Access Control (RBAC):**

```php
// Check permission
$this->checkPermission('customers.create');

// In view
<?php if (hasPermission('customers.edit')): ?>
    <button>Edit</button>
<?php endif; ?>

// Permission format: module.action
// Examples:
// - customers.view
// - invoices.create
// - reports.financial
// - system.admin
```

**Permission Hierarchy:**

```
Role (e.g., "Sales Manager")
  └─ Has Permissions
       ├─ customers.view
       ├─ customers.create
       ├─ customers.edit
       ├─ quotes.view
       ├─ quotes.create
       └─ sales_orders.view
```

## Module Structure

### Core ERP Modules

**13 Major Modules** (178 controllers total):

1. **Accounting** (Chart of Accounts, Journal, Invoices, Fixed Assets)
2. **HR & Payroll** (Employees, Recruitment, Training, Reviews)
3. **Manufacturing** (Work Orders, BOM, Equipment, Quality Control)
4. **Inventory** (Products, Stock Movements, Transfers, Lot Tracking)
5. **Sales** (Orders, Quotes, Delivery Notes, Pricing)
6. **Purchases** (Purchase Orders, Suppliers, Goods Receipts)
7. **CRM** (Customers, Contacts, Opportunities, Support Tickets)
8. **Projects** (Project Management, Tasks, Gantt, Resources)
9. **Financial Planning** (Budgets, Forecasts, Pro Forma)
10. **Payments** (Customer/Supplier Payments, Bank Reconciliation)
11. **Help Desk** (Ticketing System)
12. **Messaging** (Internal Communication)
13. **Calendar** (Organization-wide Events)

### Module Components

**Each module typically includes:**

```
Module (e.g., "Customers")
├── Controller (CustomerController.php)
├── Model (Customer.php)
├── Views
│   ├── index.php (list)
│   ├── create.php (create form)
│   ├── edit.php (edit form)
│   └── show.php (detail view)
├── Migrations
│   ├── XXX_create_customers_table.sql
│   └── YYY_add_customer_fields.sql
├── Permissions
│   ├── customers.view
│   ├── customers.create
│   ├── customers.edit
│   └── customers.delete
└── Menu Items (database)
```

## Core Components

### Session Management

```php
// core/Session.php
class Session
{
    public static function start()
    public static function set($key, $value)
    public static function get($key, $default = null)
    public static function has($key)
    public static function remove($key)
    public static function destroy()
    public static function setFlash($type, $message)
    public static function getFlash($type)
}
```

### Error Handling

```php
// core/ErrorHandler.php
class ErrorHandler
{
    // Capture all errors/exceptions
    public function register()
    
    // Handle PHP errors
    public function handleError($severity, $message, $file, $line)
    
    // Handle uncaught exceptions
    public function handleException($exception)
    
    // Handle fatal errors
    public function handleFatalError()
    
    // Log with context
    public function log($level, $message, array $context = [])
}
```

### Performance Monitoring

```php
// core/PerformanceMonitor.php
class PerformanceMonitor
{
    // Start tracking request
    public function startRequest()
    
    // Log database query
    public function logQuery($sql, $params, $duration)
    
    // Get current metrics
    public function getMetrics()
    
    // Display metrics (dev only)
    public function displayMetrics()
}
```

## Design Patterns

### Singleton Pattern

**Database connection:**

```php
$db = Database::getInstance(); // Always returns same instance
```

### Factory Pattern

**Controller instantiation:**

```php
$controllerName = 'CustomerController';
$controller = new $controllerName();
$controller->index();
```

### Registry Pattern

**Session data storage:**

```php
Session::set('user_id', 123);
$userId = Session::get('user_id');
```

### Template Method Pattern

**Controller base class:**

```php
abstract class Controller
{
    // Template method
    protected function layout($view, $data = [])
    {
        $this->loadHeader();
        $this->loadView($view, $data);
        $this->loadFooter();
    }
}
```

## Performance & Monitoring

### Monitoring System

**ErrorHandler** - Centralized error logging  
**PerformanceMonitor** - Request/query tracking  
**Health Check** - `/api/health` endpoint  
**Internal Monitor** - Cron-based health checks

### Performance Optimization

**Techniques Used:**
- Database indexing on frequently queried columns
- Query result caching (where applicable)
- Lazy loading of related data
- Data scope filtering at database level
- Prepared statements (SQL injection prevention)
- Session-based authentication (no DB lookup per request)

### Caching Strategy

**Session Cache:**
- User data cached in session
- Permissions cached per session
- Menu structure cached per user

**Application Cache:**
- Configuration values
- System settings
- Dropdown options

### Scalability Considerations

**Current Architecture Supports:**
- Multi-location deployment
- Horizontal scaling (multiple web servers)
- Database read replicas
- CDN for static assets
- Session storage (files or Redis)

## Security Architecture

### Input Validation

```php
// CSRF protection
<?= csrf_field() ?> // In forms

// XSS prevention
echo e($untrustedData); // Escapes output

// SQL injection prevention
$db->query("SELECT * FROM users WHERE id = ?", [$id]); // Prepared statements
```

### Data Protection

- Passwords: bcrypt hashing
- Sessions: Secure, HTTPOnly cookies
- CSRF tokens: Per-session tokens
- MFA: Two-factor authentication
- Audit logs: User action tracking

### Permission Checks

```php
// In controller
$this->requireAuth(); // Must be logged in
$this->checkPermission('customers.view'); // Must have permission

// In view
<?php if (hasPermission('action')): ?>
    // Show content
<?php endif; ?>
```

## API Architecture

**RESTful Endpoints:**

```
GET    /api/customers          # List
GET    /api/customers/{id}     # Show
POST   /api/customers          # Create
PUT    /api/customers/{id}     # Update
DELETE /api/customers/{id}     # Delete
```

**Response Format:**

```json
{
  "success": true,
  "data": {
    "id": 123,
    "name": "John Doe"
  },
  "message": "Customer retrieved successfully"
}
```

## Testing Architecture

**Test Types:**
- **Unit Tests**: Test individual classes/methods
- **Integration Tests**: Test component interaction
- **Feature Tests**: Test end-to-end workflows

**Test Location:**
```
tests/
├── Unit/
│   ├── DatabaseTest.php
│   └── CustomerModelTest.php
└── Integration/
    └── CustomerFlowTest.php
```

## Deployment Architecture

**Environments:**
- **Development**: Local machines
- **Production**: merph.mavrixone

**Deployment Process:**
1. Code changes committed to GitHub
2. Tests run via GitHub Actions
3. Pull to production server
4. Run migrations
5. Clear caches
6. Health check verification

## Future Considerations

**Potential Enhancements:**
- GraphQL API layer
- WebSocket for real-time updates
- Docker containerization
- Microservices for heavy modules
- Event-driven architecture
- CQRS for reporting

---

## Quick Reference

**Key Files:**
- Entry: `public/index.php`
- Base Controller: `core/Controller.php`
- Database: `core/Database.php`
- Router: `core/Router.php`
- Auth: `core/Auth.php`

**Key Concepts:**
- Custom MVC framework
- Database-driven routing
- No base model class
- Session-based auth + MFA
- RBAC permissions
- Multi-location data scoping

**Documentation:**
- Setup: [SETUP.md](SETUP.md)
- Contributing: [CONTRIBUTING.md](CONTRIBUTING.md)
- Testing: [TESTING.md](TESTING.md)
- Deployment: [DEPLOYMENT_GUIDE.md](DEPLOYMENT_GUIDE.md)
- Monitoring: [MONITORING.md](MONITORING.md)

---

**Last Updated:** 2026-02-02  
**Version:** 1.x  
**Maintainer:** M1 ERP Development Team
