ManufactureERPTechnical Docs
Complete technical documentation for developers building on ManufactureERP.
ManufactureERP is a multi-tenant SaaS platform providing modular ERP functionality for manufacturing companies. This documentation covers architecture, development, and deployment.
System Overview
ManufactureERP is built with the following principles:
- Multi-tenancy: Each organization has isolated data storage
- Modularity: Features are organized into independent, subscribable modules
- JWT Authentication: Stateless, client-side authentication
- Serverless: Runs on Google Cloud Run with auto-scaling
- Pay-per-module: Organizations subscribe only to modules they need
Key Features
| Feature | Description |
|---|---|
| Project Management | Gantt charts, task tracking, team collaboration |
| Press Production | Operation norms, efficiency tracking, daily planning |
| Maintenance | Tool inventory, PM schedules, MTBF analytics |
| Future Modules | Inventory, Quality Control, Costing & Analytics |
Architecture
System Architecture Diagram
βββββββββββββββββββββββββββββββββββββββββββββββββββ
β CLIENT (Browser) β
β ββββββββββββ ββββββββββββ ββββββββββββ β
β βHTML/CSS/JSβ βLocalStorageβ βAPI Clientβ β
β ββββββββββββ ββββββββββββ ββββββββββββ β
βββββββββββββββββββββ¬ββββββββββββββββββββββββββββββ
β HTTPS (JWT in Authorization)
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββ
β BACKEND (Flask on Cloud Run) β
β ββββββββββββββββββββββββββββββββββββββββββ β
β β app.py (Factory) β β
β ββββββββββββββββββββββββββββββββββββββββββ β
β β β
β ββββββββββββββββββ΄βββββββββββββββββ β
β β Blueprints β Services β β
β β β’ auth β β’ auth β β
β β β’ org β β’ org β β
β β β’ frontend β β’ storage β β
β β β β β
β β Modules β Decorators β β
β β β’ projects β @login β β
β β β’ press β @module β β
β β β’ maintenance β @admin β β
β βββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββ¬ββββββββββββββββββββββββββββββ
β Google Cloud APIs
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββ
β STORAGE (Google Cloud Storage) β
β β
β manufacture-erp-data-prod/ β
β βββ organizations/ β
β βββ {org-slug}/ β
β βββ organization.json β
β βββ users.json β
β βββ projects/ β
β βββ press_production/ β
β βββ maintenance/ β
βββββββββββββββββββββββββββββββββββββββββββββββββββ
Technology Stack
Backend
- Framework: Flask 3.0
- Language: Python 3.11
- Authentication: PyJWT
- Storage: Google Cloud Storage
- Hosting: Google Cloud Run (Serverless)
Frontend
- JavaScript: Vanilla JS (No frameworks)
- CSS: Custom stylesheets
- Charts: Chart.js
- Icons: Lucide Icons
Core Components
Application Factory (app.py)
def create_app():
"""
Creates Flask app with:
- Configuration loading
- Blueprint registration
- Error handlers
- Logging setup
"""
app = Flask(__name__)
app.config.from_object(get_config())
register_blueprints(app)
setup_logging(app)
return app
Configuration (config.py)
class Config:
# JWT Settings
JWT_SECRET_KEY = os.getenv('JWT_SECRET_KEY')
JWT_ACCESS_TOKEN_EXPIRES = timedelta(hours=24)
# GCS Settings
GCS_BUCKET_NAME = os.getenv('GCS_BUCKET_NAME')
# Module Pricing
MODULE_PRICING = {
'project_management': {
'name': 'Project Management',
'price': 3999,
'trial_days': 30
}
}
Decorators (core/decorators.py)
@login_required
def login_required(f):
"""Validates JWT token and loads user + org"""
@wraps(f)
def decorated(*args, **kwargs):
# 1. Extract token from Authorization header
# 2. Verify JWT
# 3. Load organization
# 4. Load user
# 5. Store in flask.g
return f(*args, **kwargs)
return decorated
@module_required
def module_required(module_name):
"""Checks if org has active subscription"""
def decorator(f):
@wraps(f)
def decorated(*args, **kwargs):
org = g.current_organization
if module_name not in org.active_modules:
# Check trial period
if module_name in org.trial_modules:
# Allow if in trial
pass
else:
return jsonify({
'error': 'Module not active'
}), 403
return f(*args, **kwargs)
return decorated
return decorator
Always use @module_required after @login_required on ALL module endpoints. Missing this decorator allows unauthorized access.
Module System Deep Dive
What is a Module?
A module is a self-contained feature set that can be:
- Independently subscribed to
- Enabled/disabled per organization
- Developed without affecting other modules
Module Structure
modules/
βββ project_management/
βββ __init__.py # Exports blueprint
βββ routes.py # API endpoints
βββ services.py # Business logic
βββ models.py # Data models
βββ gantt_service.py # Specialized services
How to Add New Modules
- Create module directory structure
- Define data models
- Create routes with
@module_required - Register blueprint in app.py
- Add to MODULE_PRICING in config.py
- Create frontend templates
- Add to sidebar navigation
- Deploy to Cloud Run
Step 1: Create Module Structure
mkdir -p modules/inventory
touch modules/inventory/__init__.py
touch modules/inventory/routes.py
touch modules/inventory/services.py
touch modules/inventory/models.py
Step 2: Define Routes
from flask import Blueprint
from core.decorators import login_required, module_required
inventory_bp = Blueprint('inventory', __name__,
url_prefix='/api/inventory')
@inventory_bp.route('/items', methods=['GET'])
@login_required
@module_required('inventory') # β CRITICAL
def get_items():
# Implementation
pass
Step 3: Register Blueprint
# In app.py
from modules.inventory import inventory_bp
app.register_blueprint(inventory_bp)
Step 4: Add to Config
# In config.py
MODULE_PRICING = {
'inventory': {
'name': 'Inventory Management',
'price': 2499,
'trial_days': 30
}
}
Authentication & Authorization
Authentication Flow
1. User submits login (email + password)
β
2. Server verifies credentials
β
3. Server generates JWT tokens
β’ Access Token (24 hours)
β’ Refresh Token (30 days)
β
4. Client stores tokens in localStorage
β
5. Client includes token in all API requests
Authorization: Bearer <access_token>
β
6. Server validates token on each request
β’ Decodes JWT
β’ Loads user + organization
β’ Checks module access
JWT Token Structure
{
"id": "user-uuid",
"email": "user@company.com",
"organization_id": "acme-manufacturing",
"role": "admin",
"exp": 1730400000
}
Data Storage Architecture
Storage Layout
manufacture-erp-data-prod/
βββ organizations/
βββ acme-manufacturing/
βββ organization.json
βββ users.json
βββ subscription.json
βββ projects/
β βββ projects.json
β βββ project-123/
β βββ tasks.json
β βββ comments.json
βββ press_production/
βββ operations.json
βββ machines.json
Why JSON in GCS?
| Advantage | Trade-off |
|---|---|
| β Simple - no schema migrations | β No complex queries |
| β Cost-effective | β Not for high-frequency writes |
| β Scalable per tenant | β Must load and filter |
| β Built-in versioning | β No transactions |
API Reference
Authentication Endpoints
POST /api/auth/register
Create new organization with owner user.
// Request
{
"organization_name": "Acme Manufacturing",
"owner_name": "John Doe",
"owner_email": "john@acme.com",
"owner_password": "SecurePass123"
}
// Response
{
"message": "Organization created successfully",
"access_token": "...",
"refresh_token": "..."
}
POST /api/auth/login
Login with email and password.
// Request
{
"email": "john@acme.com",
"password": "SecurePass123"
}
// Response
{
"message": "Login successful",
"user": {...},
"organization": {...},
"access_token": "...",
"refresh_token": "..."
}
GET /api/auth/me
Get current user information.
// Headers
Authorization: Bearer <access_token>
// Response
{
"user": {...},
"organization": {...}
}
Project Management Endpoints
GET /api/projects
// Response
{
"projects": [
{
"id": "uuid",
"name": "Product Launch",
"status": "active",
"progress": 45.5
}
]
}
POST /api/projects
// Request
{
"name": "New Product Launch",
"project_manager": "manager@company.com",
"start_date": "2025-11-01",
"target_end_date": "2025-12-31"
}
Frontend Architecture
API Client (api.js)
class APIClient {
constructor() {
this.baseURL = '/api';
}
getHeaders() {
const headers = {
'Content-Type': 'application/json'
};
const token = localStorage.getItem('access_token');
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
return headers;
}
async get(endpoint) {
return this.request(endpoint, { method: 'GET' });
}
async post(endpoint, body) {
return this.request(endpoint, {
method: 'POST',
body: JSON.stringify(body)
});
}
}
const api = new APIClient();
Authentication Manager (auth.js)
class AuthManager {
checkAuthOnProtectedPages() {
const publicPaths = ['/login', '/register', '/'];
if (publicPaths.includes(window.location.pathname)) {
return;
}
const token = localStorage.getItem('access_token');
if (!token) {
window.location.href = '/login';
}
}
async login(email, password) {
const response = await api.post('/auth/login', {
email, password
});
localStorage.setItem('access_token', response.access_token);
localStorage.setItem('refresh_token', response.refresh_token);
window.location.href = '/dashboard';
}
}
const auth = new AuthManager();
Deployment to Cloud Run
Prerequisites
- Google Cloud account
gcloudCLI installed- Project created in GCP
Setup Steps
# Login and configure
gcloud auth login
gcloud config set project YOUR_PROJECT_ID
# Enable APIs
gcloud services enable run.googleapis.com
gcloud services enable storage.googleapis.com
# Create GCS bucket
gcloud storage buckets create \
gs://manufacture-erp-data-prod \
--location=asia-south1
# Deploy to Cloud Run
gcloud run deploy manufacture-erp \
--source . \
--region asia-south1 \
--allow-unauthenticated \
--set-env-vars "GCS_BUCKET_NAME=manufacture-erp-data-prod"
Environment Variables
GCS_BUCKET_NAME=manufacture-erp-data-prod
JWT_SECRET_KEY=your-secret-key
FLASK_ENV=production
DEBUG=false
Custom Domain with Cloudflare
- Add domain to Cloudflare
- Update nameservers at registrar
- Add DNS record:
Type: CNAME Name: @ Target: manufacture-erp-xxx.a.run.app Proxy: ON - Set SSL to "Full (strict)"
- Done! π
Troubleshooting
Issue: 403 Forbidden on Module Endpoints
Cause: Missing @module_required decorator
Fix: Add decorator to ALL module endpoints
@press_production_bp.route('/operations', methods=['GET'])
@login_required
@module_required('press_production') # β Add this
def get_operations():
pass
Issue: Token Expired Errors
Cause: Access token expired (24 hours)
Fix: API client auto-refreshes tokens
Issue: GCS Permission Denied
Cause: Service account lacks permissions
gcloud storage buckets add-iam-policy-binding \
gs://manufacture-erp-data-prod \
--member="serviceAccount:${SERVICE_ACCOUNT}" \
--role="roles/storage.objectAdmin"
For questions or support, contact the development team or visit our GitHub repository.