Installs into .claude/skills of the current project.
Are you the author of Local Frappe?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/majiayu000-local-frappe)
---
name: local-frappe
description: Local Frappe development environment for testing APIs before production deployment. Use when developing or testing Python/API changes locally.
allowed-tools: Bash, Read, Grep, Edit, Write
user-invocable: true
---
# /local-frappe - Local Frappe Development
## CRITICAL: TEST LOCALLY FIRST - NO EXCEPTIONS
**NEVER deploy Frappe/Python changes directly to production.**
| Action | Time | When to Use |
|--------|------|-------------|
| Local test | **Seconds** | ALWAYS - every code change |
| Production deploy | 15-20 min | ONLY after local testing passes |
**The 15-20 minute production build/deploy cycle is UNACCEPTABLE for iterative development.**
## What This Command Does
Sets up and manages a local Frappe development environment for testing BEI custom APIs before deploying to production.
**MANDATORY:** Always test changes locally BEFORE pushing to production. The local environment matches production (Frappe v15, ERPNext v15, HRMS v15).
## Quick Start
```bash
cd docker-dev
dev.bat start # Windows (first run takes 10-15 minutes)
./dev.sh start # Linux/Mac
```
## Access
| URL | Purpose |
|-----|---------|
| http://localhost:8000 | Frappe web UI |
| http://localhost:8000/api/method/frappe.ping | API test endpoint |
| http://localhost:8000/api/method/hrms.api.hello.hello | BEI test API |
| localhost:3307 | MariaDB (user: root, pass: admin) |
**Login:** Administrator / admin
## Development Workflow
### Standard Workflow (Edit → Sync → Test)
```bash
# 1. Edit your API file locally
# Example: hrms/api/employee_clearance.py
# 2. Sync to container and clear cache
dev.bat sync
# 3. Test your API
curl http://localhost:8000/api/method/hrms.api.employee_clearance.get_status
# 4. When ready, commit and push to production
git add .
git commit -m "feat: Add employee clearance API"
git push origin production
```
### Helper Commands
| Command | Description |
|---------|-------------|
| `dev.bat start` | Start all containers (first run: 10-15 min) |
| `dev.bat stop` | Stop all containers |
| `dev.bat restart` | Restart Frappe to pick up code changes |
| `dev.bat sync` | Copy API files and clear cache (quick reload) |
| `dev.bat shell` | Open bash shell in container |
| `dev.bat bench` | Run bench command (e.g., `dev.bat bench migrate`) |
| `dev.bat logs` | Follow Frappe logs |
| `dev.bat test` | Test hello API endpoint |
| `dev.bat migrate` | Run database migrations |
| `dev.bat status` | Show container status |
| `dev.bat reset` | Delete everything and start fresh (DESTRUCTIVE) |
## File Structure
```
docker-dev/
├── docker-compose.yml # Container orchestration
├── dev.bat # Windows helper script
└── dev.sh # Linux/Mac helper script
hrms/api/ # BEI custom API files (synced to container)
├── __init__.py # API exports (CRITICAL - update when adding new APIs)
├── hello.py # Test endpoint
├── employee_clearance.py # Clearance APIs
├── enrichment.py # Data enrichment APIs
├── google_chat.py # Google Chat integration
├── google_drive.py # Google Drive integration
├── google_login.py # Google OAuth
├── oauth_tokens.py # OAuth token management
├── onboarding.py # Employee onboarding
└── roster.py # Shift roster APIs
```
## Adding a New API
### 1. Create your API file
```python
# hrms/api/my_new_api.py
import frappe
@frappe.whitelist(allow_guest=True)
def hello():
"""Test endpoint."""
return {"message": "Hello from my new API!"}
@frappe.whitelist()
def get_data():
"""Requires authentication."""
return {"user": frappe.session.user}
```
### 2. Update __init__.py
```python
# hrms/api/__init__.py
from hrms.api.my_new_api import (
hello,
get_data,
)
```
### 3. Sync and test
```bash
dev.bat sync
curl http://localhost:8000/api/method/hrms.api.my_new_api.hello
```
## Container Configuration
| Component | Value |
|-----------|-------|
| Frappe Image | `frappe/bench:v5.25.11` |
| Bench Location | `/workspace/frappe-bench` |
| API Mount | `/bei-api` (read-only from `hrms/api/`) |
| Site Name | `dev.localhost` |
| MariaDB Port | 3307 (external), 3306 (internal) |
| Web Port | 8000 |
| Socket.IO Port | 9000 |
## Volumes
| Volume | Purpose |
|--------|---------|
| `docker-dev_frappe-bench` | Frappe bench directory (apps, sites, etc.) |
| `docker-dev_mariadb-data` | Database data |
**Note:** Volumes persist data across container restarts. To completely reset, use `docker compose down -v`.
## Troubleshooting
### Container won't start
```bash
# Check logs
dev.bat logs
# Or directly
docker logs frappe-dev
```
### API changes not visible
```bash
# Force sync and clear cache
dev.bat sync
# If still not working, restart Frappe
dev.bat restart
```
### Database connection errors
```bash
# Check MariaDB is healthy
docker ps
# Should show mariadb-dev as (healthy)
```
### Need fresh start
```bash
# WARNING: This deletes all data
dev.bat reset
# Or manually:
docker compose -f docker-dev/docker-compose.yml down -v
dev.bat start
```
### First run taking too long
First run downloads and installs:
- Frappe Framework (~3 min)
- ERPNext (~5 min)
- HRMS (~3 min)
- Site creation (~2 min)
Total: 10-15 minutes. Subsequent starts are fast (~30 seconds).
## Testing APIs
### Without authentication
```bash
# APIs with @frappe.whitelist(allow_guest=True)
curl http://localhost:8000/api/method/hrms.api.hello.hello
```
### With authentication
```bash
# Get session cookie first
curl -c cookies.txt -X POST http://localhost:8000/api/method/login \
-d "usr=Administrator" -d "pwd=admin"
# Then use the cookie
curl -b cookies.txt http://localhost:8000/api/method/hrms.api.roster.get_roster
```
### Using bench execute
```bash
# Run Python code directly
dev.bat bench execute hrms.api.hello.hello
# With arguments
dev.bat bench execute "hrms.api.employee_clearance.get_status" --args '["HR-EMP-0001"]'
```
## When to Deploy to Production
After testing locally:
1. **Commit changes:**
```bash
git add hrms/api/
git commit -m "feat: Add employee clearance API"
```
2. **Push to production branch:**
```bash
git push origin production
```
3. **Monitor GitHub Actions:** https://github.com/Bebang-Enterprise-Inc/hrms/actions
4. **Verify production:**
```bash
curl https://hq.bebang.ph/api/method/hrms.api.hello.hello
```
See `/deploy-frappe` skill for full production deployment options.
## DO NOT DO
1. **NEVER edit files directly in the container** - Changes are lost on restart
2. **NEVER use `docker commit`** - Corrupts the Python environment
3. **NEVER push untested code to production** - Always test locally first
4. **NEVER skip updating `__init__.py`** - API won't be whitelisted
5. **NEVER assume production deploy will pick up code changes** - Use `no_cache=true` (see below)
---
## CRITICAL: Local vs Production Asset Handling
### Why Local Works But Production Breaks
**Local development** allows `bench build` because:
- Single container environment
- No persistent asset volumes (fresh start each time)
- Development mode reloads assets on change
**Production** prohibits `bench build` because:
- Multi-container architecture (frontend + backend)
- Persistent volumes don't auto-update
- Assets baked into Docker image at build time
### What This Means For You
| Action | Local | Production |
|--------|-------|------------|
| `bench build` | ✅ OK | ❌ NEVER |
| `bench migrate` | ✅ OK | ⚠️ Only via isolated container |
| Edit Python files | ✅ Sync + restart | ❌ Rebuild image |
| Edit CSS/JS | ✅ Sync + restart | ❌ Rebuild image |
### The Production Rule
**All code and asset changes MUST go through Docker image rebuild.**
When you push to production:
1. GitHub Actions rebuilds the entire Docker image
2. New CSS/JS hashes are baked into image
3. Assets volume should be deleted before deploy
4. Fresh containers get consistent assets
**Never "hot-fix" production by running bench commands.**
See `/deploy-frappe` for the full CSS 404 root cause analysis and fix.
## Testing Custom WWW Pages Locally
### Creating Custom Pages (like login)
1. Create your page file:
```
hrms/www/bei-login.html # The HTML template
hrms/www/bei-login.py # Optional Python context handler
```
2. If redirecting from an existing route, add to `hrms/hooks.py`:
```python
website_redirects = [
{"source": "/login", "target": "/bei-login", "redirect_http_status": 302},
]
```
3. Sync to container:
```bash
dev.bat sync
```
4. Test locally:
```bash
# Check redirect
curl -sI http://localhost:8000/login | grep -i location
# Check page renders
curl -s http://localhost:8000/bei-login | grep -o "<title>.*</title>"
```
### Important: Production Deployment Uses Docker Cache
**After testing locally, you MUST use `no_cache=true` when deploying:**
```bash
gh workflow run "Build and Deploy Frappe HRMS" --repo Bebang-Enterprise-Inc/hrms -f no_cache=true -f run_migrate=true
```
**Why:** Docker cache doesn't detect when git repo contents change. Without `no_cache=true`, the build serves cached layers from previous builds.
**How to verify fresh build:** Check GitHub Actions build time:
- ~2 min = CACHED (old code)
- ~5-10 min = FRESH (new code)
## Running bei-tasks Frontend Locally (Added 2026-01-24)
For full-stack local development, run both Frappe AND bei-tasks locally:
### 1. Start Local Frappe
```bash
cd F:\Dropbox\Projects\BEI-ERP\docker-dev
powershell -Command "docker compose up -d"
# Wait for Frappe to start (~30 seconds if already initialized)
```
### 2. Generate API Keys (First Time Only)
```bash
# Login to get session cookie
curl -c cookies.txt -X POST "http://localhost:8000/api/method/login" \
-d "usr=Administrator&pwd=admin"
# Generate API keys
curl -b cookies.txt -X POST \
"http://localhost:8000/api/method/frappe.core.doctype.user.user.generate_keys?user=Administrator"
# Returns: {"message":{"api_key":"xxx","api_secret":"yyy"}}
```
### 3. Configure bei-tasks for Local Frappe
Create/update `F:\Dropbox\Projects\bei-tasks\.env.development.local`:
```env
# Local Development Environment
NEXT_PUBLIC_FRAPPE_URL=http://localhost:8000
# Local Frappe API credentials (from step 2)
FRAPPE_API_KEY=your-api-key
FRAPPE_API_SECRET=your-api-secret
# App Configuration
NEXT_PUBLIC_APP_NAME=BEI Tasks (LOCAL)
```
### 4. Start Local bei-tasks
```bash
cd F:\Dropbox\Projects\bei-tasks
npm run dev
# Frontend runs at http://localhost:3000
```
### 5. Test the Connection
```bash
# Test Frappe API directly
curl -H "Authorization: token api-key:api-secret" \
"http://localhost:8000/api/method/hrms.api.onboarding.get_session?token=test"
# Expected: {"message":{"success":false,"error":"Session not found",...}}
```
### Local Development Stack
| Component | URL | Purpose |
|-----------|-----|---------|
| Frappe Backend | http://localhost:8000 | API + Admin UI |
| bei-tasks Frontend | http://localhost:3000 | React/Next.js app |
| MariaDB | localhost:3307 | Database |
| Socket.IO | localhost:9000 | Realtime |
### Syncing Code Changes
```bash
# After editing hrms/api/*.py files:
cd F:\Dropbox\Projects\BEI-ERP\docker-dev
# Sync files and clear cache
powershell -Command "docker exec frappe-dev bash -c 'cp -f /bei-api/*.py /workspace/frappe-bench/apps/hrms/hrms/api/ && cd /workspace/frappe-bench && bench --site dev.localhost clear-cache'"
# If whitelist decorators changed, restart Frappe
powershell -Command "docker restart frappe-dev"
```
### Important Notes
1. **Authentication Required:** Local APIs need authentication (no `allow_guest=True`)
2. **Session Cookie OR Token Auth:** Use either method for local testing
3. **Different Site:** Local uses `dev.localhost`, production uses `hq.bebang.ph`
4. **No Test Data:** Local DB is empty - create test data as needed
## Related Skills
- `/deploy-frappe` - Production deployment (ONLY after local testing)
- `/frappe-sql-bulk` - Bulk data operations
- `/frappe-doctype` - DocType development
---
## Production Testing Infrastructure (Added 2026-01-23)
### CRITICAL: Database Architecture Discovery
**The Frappe system at `hq.bebang.ph` uses a LOCAL Docker database via Docker Swarm (migrated 2026-01-29).**
| Component | Service Name | Purpose |
|-----------|--------------|---------|
| Backend | `frappe_backend` | Frappe/ERPNext/HRMS (Gunicorn) |
| Database | `frappe_db` | MariaDB (LOCAL) |
| Frontend | `frappe_frontend` | Nginx proxy |
| WebSocket | `frappe_websocket` | Socket.IO |
| Queue | `frappe_queue-short`, `frappe_queue-long` | Background workers |
| Scheduler | `frappe_scheduler` | Cron jobs |
**Note:** Production now uses Docker Swarm (9 services). Container names changed from `frappe_docker-backend-1` to dynamic names under `frappe_backend.1.xxxx`.
**Common Mistake:** AWS SSM commands often connect to the RDS database (`frappe-hrms-db.ctmwomgscn66.ap-southeast-1.rds.amazonaws.com`), but the live Frappe instance uses the local Docker database. This causes "no employees found" issues when testing.
### Production Test Accounts
| Employee ID | Email | Password | Role |
|-------------|-------|----------|------|
| TEST-STAFF-001 | test.staff@bebang.ph | BeiTest2026! | Store Staff |
| TEST-SUPERVISOR-001 | test.supervisor@bebang.ph | BeiTest2026! | Store Supervisor |
| TEST-AREA-001 | test.area@bebang.ph | BeiTest2026! | Area Supervisor |
| TEST-HR-001 | test.hr@bebang.ph | BeiTest2026! | HR User |
### Inserting Test Data into Production
```bash
# 1. Get AWS credentials
export AWS_ACCESS_KEY_ID=$(doppler secrets get AWS_ACCESS_KEY_ID --project bei-erp --config dev --plain)
export AWS_SECRET_ACCESS_KEY=$(doppler secrets get AWS_SECRET_ACCESS_KEY --project bei-erp --config dev --plain)
export AWS_DEFAULT_REGION=ap-southeast-1
# 2. Run SQL on the CORRECT database (via Swarm backend container)
aws ssm send-command \
--instance-ids "i-026b7477d27bd46d6" \
--document-name "AWS-RunShellScript" \
--parameters 'commands=[
"docker exec $(docker ps -qf name=frappe_backend) bench --site hq.bebang.ph mariadb --execute \"INSERT INTO tabEmployee (name, employee_name, first_name, status, gender, date_of_birth, date_of_joining, company, user_id, creation, modified, owner, modified_by) VALUES ('TEST-STAFF-001', 'Test Staff', 'Test', 'Active', 'Male', '1990-01-01', '2024-01-01', 'Bebang Enterprise Inc.', 'test.staff@bebang.ph', NOW(), NOW(), 'Administrator', 'Administrator');\""
]'
# 3. Verify
aws ssm send-command \
--instance-ids "i-026b7477d27bd46d6" \
--document-name "AWS-RunShellScript" \
--parameters 'commands=[
"docker exec $(docker ps -qf name=frappe_backend) bench --site hq.bebang.ph mariadb --execute \"SELECT name, employee_name, user_id FROM tabEmployee WHERE name LIKE 'TEST-%';\""
]'
```
### API Credentials for bei-tasks
| Environment | Key | Secret | Source |
|-------------|-----|--------|--------|
| Production (Vercel) | `4a17c23aca83560` | `38ecc0e1054b1d2` | Doppler bei-erp |
| Local (.env.local) | Same | Same | Doppler bei-erp |
**IMPORTANT:** HR API routes use token auth (not session cookies) for Employee lookups because regular Frappe users don't have Employee read permission.
### Verifying API Connection
```bash
# Test from production
curl -X POST https://hq.bebang.ph/api/resource/Employee \
-H "Authorization: token 4a17c23aca83560:38ecc0e1054b1d2" \
-H "Content-Type: application/json" \
-d '{"filters": [["user_id", "=", "test.staff@bebang.ph"]]}'
# Expected: Returns employee record
```
### Troubleshooting Production Tests
1. **"No employee found" but employee exists:**
- Check you're querying the Docker database, not RDS
- Use `bench --site hq.bebang.ph mariadb` not direct RDS connection
2. **API returns 401:**
- Verify Vercel env vars match Doppler
- Redeploy after env var changes
3. **Container not found:**
- Production now uses Docker Swarm (migrated 2026-01-29)
- Service names: `frappe_backend`, `frappe_frontend`, etc.
- Container names are dynamic: `frappe_backend.1.xxxxx`
- Use `docker service ls` to verify services
- Use `docker ps -qf name=frappe_backend` to get container ID
4. **HR pages show "Failed to get employee data":**
- HR APIs need token auth, not session auth
- Check `app/api/hr/*/route.ts` uses `Authorization: token` header
5. **Code deployed but changes not visible:**
- Docker build caching issue - use `no_cache=true` when deploying
- See `/deploy-frappe` for details on when to use no_cache
- Verify with: `curl https://hq.bebang.ph/api/method/hrms.api.hello.hello`
- Response should include `build_version` field showing deployment timestamp
### Deployment Verification Endpoint
The hello API includes deployment info for verification:
```bash
curl -s https://hq.bebang.ph/api/method/hrms.api.hello.hello | python -m json.tool
```
Expected response:
```json
{
"message": {
"message": "Hello from Frappe HRMS!",
"timestamp": "2026-01-29 10:04:11.569399",
"build_version": "2026-01-29T12:16:00+08:00",
"deployment": "docker-swarm"
}
}
```
If `build_version` doesn't update after deployment, rebuild with `no_cache=true`.