Claude-skill-registry local-frappe
Local Frappe development environment for testing APIs before production deployment. Use when developing or testing Python/API changes locally.
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/local-frappe" ~/.claude/skills/majiayu000-claude-skill-registry-local-frappe && rm -rf "$T"
skills/data/local-frappe/SKILL.md/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
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)
# 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 |
|---|---|
| Start all containers (first run: 10-15 min) |
| Stop all containers |
| Restart Frappe to pick up code changes |
| Copy API files and clear cache (quick reload) |
| Open bash shell in container |
| Run bench command (e.g., ) |
| Follow Frappe logs |
| Test hello API endpoint |
| Run database migrations |
| Show container status |
| 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
# 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
# hrms/api/__init__.py from hrms.api.my_new_api import ( hello, get_data, )
3. Sync and test
dev.bat sync curl http://localhost:8000/api/method/hrms.api.my_new_api.hello
Container Configuration
| Component | Value |
|---|---|
| Frappe Image | |
| Bench Location | |
| API Mount | (read-only from ) |
| Site Name | |
| MariaDB Port | 3307 (external), 3306 (internal) |
| Web Port | 8000 |
| Socket.IO Port | 9000 |
Volumes
| Volume | Purpose |
|---|---|
| Frappe bench directory (apps, sites, etc.) |
| Database data |
Note: Volumes persist data across container restarts. To completely reset, use
docker compose down -v.
Troubleshooting
Container won't start
# Check logs dev.bat logs # Or directly docker logs frappe-dev
API changes not visible
# Force sync and clear cache dev.bat sync # If still not working, restart Frappe dev.bat restart
Database connection errors
# Check MariaDB is healthy docker ps # Should show mariadb-dev as (healthy)
Need fresh start
# 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
# APIs with @frappe.whitelist(allow_guest=True) curl http://localhost:8000/api/method/hrms.api.hello.hello
With authentication
# 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
# 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:
-
Commit changes:
git add hrms/api/ git commit -m "feat: Add employee clearance API" -
Push to production branch:
git push origin production -
Monitor GitHub Actions: https://github.com/Bebang-Enterprise-Inc/hrms/actions
-
Verify production:
curl https://hq.bebang.ph/api/method/hrms.api.hello.hello
See
/deploy-frappe skill for full production deployment options.
DO NOT DO
- NEVER edit files directly in the container - Changes are lost on restart
- NEVER use
- Corrupts the Python environmentdocker commit - NEVER push untested code to production - Always test locally first
- NEVER skip updating
- API won't be whitelisted__init__.py - NEVER assume production deploy will pick up code changes - Use
(see below)no_cache=true
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 |
|---|---|---|
| ✅ OK | ❌ NEVER |
| ✅ 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:
- GitHub Actions rebuilds the entire Docker image
- New CSS/JS hashes are baked into image
- Assets volume should be deleted before deploy
- 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)
-
Create your page file:
hrms/www/bei-login.html # The HTML template hrms/www/bei-login.py # Optional Python context handler -
If redirecting from an existing route, add to
:hrms/hooks.pywebsite_redirects = [ {"source": "/login", "target": "/bei-login", "redirect_http_status": 302}, ] -
Sync to container:
dev.bat sync -
Test locally:
# 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
when deploying:no_cache=true
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
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)
# 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:
# 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
cd F:\Dropbox\Projects\bei-tasks npm run dev # Frontend runs at http://localhost:3000
5. Test the Connection
# 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
# 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
- Authentication Required: Local APIs need authentication (no
)allow_guest=True - Session Cookie OR Token Auth: Use either method for local testing
- Different Site: Local uses
, production usesdev.localhosthq.bebang.ph - No Test Data: Local DB is empty - create test data as needed
Related Skills
- Production deployment (ONLY after local testing)/deploy-frappe
- Bulk data operations/frappe-sql-bulk
- DocType development/frappe-doctype
Production Testing Infrastructure (Added 2026-01-23)
CRITICAL: Database Architecture Discovery
The Frappe system at
uses a LOCAL Docker database via Docker Swarm (migrated 2026-01-29).hq.bebang.ph
| Component | Service Name | Purpose |
|---|---|---|
| Backend | | Frappe/ERPNext/HRMS (Gunicorn) |
| Database | | MariaDB (LOCAL) |
| Frontend | | Nginx proxy |
| WebSocket | | Socket.IO |
| Queue | , | Background workers |
| 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 | 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
# 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) | | | 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
# 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
-
"No employee found" but employee exists:
- Check you're querying the Docker database, not RDS
- Use
not direct RDS connectionbench --site hq.bebang.ph mariadb
-
API returns 401:
- Verify Vercel env vars match Doppler
- Redeploy after env var changes
-
Container not found:
- Production now uses Docker Swarm (migrated 2026-01-29)
- Service names:
,frappe_backend
, etc.frappe_frontend - Container names are dynamic:
frappe_backend.1.xxxxx - Use
to verify servicesdocker service ls - Use
to get container IDdocker ps -qf name=frappe_backend
-
HR pages show "Failed to get employee data":
- HR APIs need token auth, not session auth
- Check
usesapp/api/hr/*/route.ts
headerAuthorization: token
-
Code deployed but changes not visible:
- Docker build caching issue - use
when deployingno_cache=true - See
for details on when to use no_cache/deploy-frappe - Verify with:
curl https://hq.bebang.ph/api/method/hrms.api.hello.hello - Response should include
field showing deployment timestampbuild_version
- Docker build caching issue - use
Deployment Verification Endpoint
The hello API includes deployment info for verification:
curl -s https://hq.bebang.ph/api/method/hrms.api.hello.hello | python -m json.tool
Expected response:
{ "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.