Skip to content

English {#english}

prego-saas-app Implementation Plan (Frappe Custom App · SaaS Layer)

Purpose: Specify Frappe app structure, file tree, features, Doctype schema, API, and ops policy for prego-saas-app in a form usable at implementation.
No code generation — plan, design, paths, requirements only.

  • Target: Pregoi/prego-saas-app. Business logic only Frappe custom app.
  • Assumption: Frappe/ERPNext core unchanged; all extension via hooks, overrides, API, Doctypes inside prego_saas_app.
  • Related: prego-docker handles image build, deploy, repository_dispatch; prego-saas-app has no Docker/compose.

1. Two-repo strategy

prego-saas-app: hooks, api, overrides, doctype, public, scheduler (no Docker). prego-docker: Dockerfile, apps.json, compose, GitHub Actions. Benefits: separate versioning (app semver vs image), CI only on app change (repository_dispatch), no Docker tokens in app repo, rollback by commit, dev vs DevOps split, SKU variants via apps.json.


2–onwards. Structure, phases (summary)

  • §2 Directory: prego_saas_app/ (hooks, api, overrides, doctype/, config, utils, flags, payroll); setup.py, MANIFEST.in, pyproject.toml; .github/workflows/notify-image-rebuild.yml. Doctypes: prego_tenant, prego_feature_flag, prego_plan_feature, prego_tenant_flag, prego_usage_metric, prego_usage_event, prego_usage_aggregation. No Docker files.
  • §3+ Phase 1: minimal validation (test features + workflow). Later phases: provisioning API, billing, Stripe webhook, usage metrics, feature flags, overrides, security. Full phase table and Doctype/API details in Korean section.

Full tables (§1–§2, Phase lists, Doctype specs) and exact wording are in the Korean section below.


한국어 {#korean}

기획서: prego-saas-app 코드 구현 (Frappe Custom App · SaaS 확장 레이어)

목적: prego-saas-app 레포(/Users/marco/prego-saas-app)에 생성할 Frappe 앱 구조·파일 트리·기능 명세·Doctype 스키마·API·운영 정책을 구현 단계에서 그대로 따라 쓸 수 있는 형태로 정리한다.
코드 생성 없음 — 기획·설계·파일 경로·요구사항만 기술.

  • 대상 폴더: /Users/marco/prego-saas-app (GitHub: Pregoi/prego-saas-app). 비즈니스 로직 전용 Frappe Custom App.
  • 전제: Frappe/ERPNext v15 코어 무수정; 모든 확장은 prego_saas_app 내부에서 hooks·override·API·Doctype으로만 구현.
  • 연관: prego-docker-implementation-plan.md — 이미지 빌드·배포·repository_dispatch는 prego-docker 담당. prego-saas-app에는 Docker/compose 없음.

1. 2-Repo 전략과 분리 이유

구분prego-saas-appprego-docker
역할비즈니스 로직 전용 (Frappe App)이미지 빌드·배포 전용
포함hooks, api, overrides, doctype, public, schedulerDockerfile, apps.json, compose, GitHub Actions build/promote
제외Docker·compose·빌드 스크립트앱 소스 코드
항목분리 시 장점
버전 관리앱은 semver, 이미지는 runtime 버전으로 독립 관리
CI 속도앱 변경 시에만 이미지 재빌드(repository_dispatch)
보안Docker Hub 토큰을 앱 레포에 두지 않음
롤백특정 app commit 기반 이미지 복구(commit pin) 가능
팀 협업개발팀 vs DevOps 분리
SKU 확장apps.json 조합만 바꿔 Core/Premium/Enterprise 이미지 생성 가능

전체 흐름: prego-saas-app push → repository_dispatch → prego-docker build → Docker Hub push → Staging deploy → 승격 → Production.


2. prego-saas-app 최종 디렉터리 구조 (목표)

구현 단계별로 아래 구조를 채운다. Phase가 올라갈수록 파일·모듈이 추가된다.

prego-saas-app/
├── prego_saas_app/
│ ├── __init__.py
│ ├── hooks.py
│ ├── api.py
│ ├── modules.txt
│ ├── config/
│ │ └── desktop.py
│ ├── overrides/ # ERPNext/HRMS 클래스 오버라이드
│ │ └── employee.py
│ ├── api/ # API 모듈 분리
│ │ ├── provisioning.py
│ │ ├── billing.py
│ │ └── stripe_webhook.py
│ ├── utils/
│ │ └── security.py
│ ├── flags.py # Feature Flag 런타임 조회
│ ├── payroll.py # Salary Slip validate 등
│ └── doctype/ # Custom Doctypes
│ ├── prego_tenant/
│ ├── prego_feature_flag/
│ ├── prego_plan_feature/
│ ├── prego_tenant_flag/
│ ├── prego_usage_metric/
│ ├── prego_usage_event/
│ └── prego_usage_aggregation/
├── setup.py
├── MANIFEST.in
├── pyproject.toml
└── .github/
└── workflows/
└── notify-image-rebuild.yml
  • Docker 관련 파일·compose 없음.
  • notify-image-rebuild.yml: prego-docker 레포의 docs/prego-saas-app-notify-workflow.yml을 복사해 두는 형태. (이미 prego-docker에 템플릿 있음.)

3. Phase 1: 최소 검증용 (테스트용 기능 2종 + 워크플로)

목표: 서버 빌드/배포·custom app 로드·Hook 동작 확인.

3.1 포함 기능

기능설명검증 방법
Health Check APIGET /api/method/prego_saas_app.api.health_check{"status":"ok","app":"prego_saas_app","frappe_version":"15.x.x"}브라우저/curl 호출 시 JSON 응답 확인.
Sales Order HookSales Order after_insert 시 로그 기록 [PREGO TEST] Sales Order Created: <name>ERPNext에서 SO 생성 후 docker logs <app_container> 확인.
(선택) Scheduler5분마다 [PREGO TEST] Cron executed 로그hooks에 cron 등록, api에 cron_test() 추가.

3.2 Phase 1 파일 트리

prego-saas-app/
├── prego_saas_app/
│ ├── __init__.py
│ ├── hooks.py # doc_events Sales Order after_insert; (선택) scheduler_events
│ ├── api.py # health_check(); log_sales_order(doc, method); (선택) cron_test()
│ └── modules.txt # "Prego SaaS App"
├── setup.py
└── .github/workflows/
└── notify-image-rebuild.yml

3.3 테스트 체크리스트

항목기대 결과
API 호출JSON 응답 정상
Sales Order 생성로그 출력
Scheduler (선택)5분마다 로그
이미지 재빌드prego-saas-app push 시 prego-docker 자동 트리거

Production 주의: 테스트용 API(health_check)는 staging 전용 브랜치에만 두거나, permission 제한 또는 배포 후 제거 검토.


4. Phase 2: 최소 운영형 Frappe App 템플릿

목표: 앱 메타·데스크탑·모듈·Payroll 확장 예시까지 포함한 “완전한 최소 템플릿”.

4.1 추가 파일

경로역할
prego_saas_app/config/desktop.pyWorkspace/앱 아이콘 등
MANIFEST.in, pyproject.toml패키징·의존성
hooks.py 확장app_name, app_title, app_publisher, app_description, app_email, app_license; doc_events (Salary Slip validate); scheduler_events (6시간마다 periodic_task)
api.py 확장periodic_task()
payroll.py (신규)Salary Slip validate 시 net pay 0 이하 차단, custom_saas_fee 예시(1%)

4.2 hooks.py 요구사항 (Phase 2)

  • doc_events: Salary Slipvalidateprego_saas_app.payroll.custom_validate
  • scheduler_events: cron0 */6 * * *prego_saas_app.api.periodic_task
  • ERPNext 코드 수정 없이 hooks로만 override.

4.3 payroll.py 요구사항

  • custom_validate(doc, method): doc.base_net_pay <= 0 이면 throw; (예시) doc.base_net_pay * 0.01doc.custom_saas_fee 설정.
  • Salary Slip에 custom 필드 custom_saas_fee 필요 시 Custom Field로 추가(구현 시).

5. Phase 3: SaaS 멀티테넌트 설정 구조

목표: 하나의 이미지·여러 site, site별 DB·정책 분리.

5.1 사이트 구조 개념

sites/
├── tenant1.domain.com/
│ └── site_config.json
├── tenant2.domain.com/
│ └── site_config.json
└── common_site_config.json
  • common_site_config.json: db_host, redis_cache, redis_queue, redis_socketio 등 인프라 공통.
  • site_config.json (테넌트별): db_name, db_password, prego_plan, max_users 등 SaaS 정책.

5.2 Plan 기반 제한 예시 (Employee)

  • hooks: override_doctype_class"Employee": "prego_saas_app.overrides.employee.CustomEmployee"
  • overrides/employee.py: CustomEmployee(Employee)에서 validate() 오버라이드; frappe.conf.get("prego_plan")basic일 때 frappe.db.count("Employee") > 5면 throw.
  • Plan/limit 확장은 이후 Feature Flag·Doctype과 연동.

6. Phase 4: Tenant 프로비저닝 API

목표: Provisioner Site에서만 “신규 테넌트 생성” API 호출 1회로 site 생성 + DB + 앱 설치 + 관리자 생성. 비동기(long queue) 권장.

6.1 권장 구조

  • Provisioner 전용 site 1개: 예) provision.prego.internal. 이 site에서만 테넌트 생성 API 허용.
  • 파일 위치: prego_saas_app/api/provisioning.py, prego_saas_app/utils/security.py.

6.2 API 요구사항

  • create_tenant(site_name, admin_email, plan)
    • POST, whitelist.
    • _assert_provisioner(): 현재 site가 provisioner site인지, session user가 System Manager인지 검사.
    • 입력 검증(site_name에 ., admin_email에 @).
    • frappe.enqueue(..., queue="long")_create_tenant_job 호출.
    • 반환: { "queued": true, "job_id": ..., "site_name": ... }.

6.3 _create_tenant_job 요구사항

  • bench new-site, install-app erpnext, hrms, prego_saas_app; set-config prego_plan, prego_admin_email.
  • (선택) Prego Tenant Doctype에 레코드 insert(tenant_site, admin_email, plan, status).
  • 운영: admin_password는 API 응답에 넣지 말고, 초대 메일/일회성 링크로 전달.

6.4 Prego Tenant Doctype (Phase 4에서 사용할 필드)

  • tenant_site, admin_email, plan, status (Trial/Active/Past Due/Suspended/Canceled) 등.
  • 전체 스키마는 §10에 통합 정의.

7. Feature Flag 중앙 제어 (Phase 5)

목표: 플래그 우선순위 — (1) 테넌트 override (DB) (2) 플랜 기본값 (DB) (3) 코드 기본값.

7.1 Doctype 3종

Doctype용도
Prego Feature Flagflag_key(unique), description, default_enabled, category, enterprise_only
Prego Plan Featureplan, flag_key(Link), enabled, limit_value. Composite unique (plan, flag_key).
Prego Tenant Flagtenant_site, flag_key, enabled (테넌트별 override)

7.2 런타임 조회 (prego_saas_app/flags.py)

  • is_enabled(flag_key): (1) Prego Tenant Flag 조회 (2) 없으면 Prego Plan Feature(현재 site의 prego_plan) (3) 없으면 Prego Feature Flag.default_enabled.
  • 캐시: Redis 등 short TTL(예: 60초)로 중복 DB 접근 감소.
  • 사용 예: if not is_enabled("payroll_advanced_rules"): frappe.throw(...).

7.3 권한

  • Provisioner site에서만 Feature Flag / Plan Feature / Tenant Flag 편집 허용.
  • 각 테넌트 site에서는 읽기만 또는 접근 차단.

8. Stripe 연동 (Phase 6)

목표: 테넌트별 Subscription·plan/status 반영, Webhook 수신.

8.1 Prego Tenant 확장 필드 (Stripe)

  • stripe_customer_id, stripe_subscription_id, current_period_end, limits_json, flags_json (이미 §10 스키마에 포함).

8.2 Webhook (prego_saas_app/api/stripe_webhook.py)

  • webhook(): POST, allow_guest; Provisioner site에서만 수신.
  • Stripe 서명 검증 필수(운영).
  • 이벤트: customer.subscription.created/updated → _handle_subscription; customer.subscription.deleted → _handle_subscription_deleted.
  • subscription object에서 customer_id → Prego Tenant 조회 후 plan, status, stripe_subscription_id 업데이트; tenant site에 bench --site <site> set-config prego_plan <plan> 반영.

8.3 Checkout Session 생성 (prego_saas_app/api/billing.py)

  • create_checkout_session(plan, tenant_name): Stripe Customer 없으면 생성 후 Tenant에 stripe_customer_id 저장; Session.create(mode=subscription, line_items price by plan); success_url/cancel_url 반환.
  • price_id ↔ plan 매핑은 Doctype 또는 설정으로 관리 권장.

9. Doctype 스키마 상세 (구현 시 반영)

9.1 Prego Tenant

Field LabelFieldnameTypeOptions/NotesRequiredIndex
Tenant Sitetenant_siteDataunique (e.g. tenant1.domain.com)
DomaindomainDataFQDN
PlanplanSelectbasic / pro / enterprise
StatusstatusSelectTrial / Active / Past Due / Suspended / Canceled
Admin Emailadmin_emailData
Stripe Customer IDstripe_customer_idDataunique
Stripe Subscription IDstripe_subscription_idData
Current Period Endcurrent_period_endDatetime
Limits JSONlimits_jsonCodeJSON
Flags JSONflags_jsonCodeJSON
DisableddisabledCheck

Index: tenant_site (unique), stripe_customer_id, status, plan.

9.2 Prego Feature Flag

FieldTypeNotes
Flag KeyDataunique (e.g. payroll_advanced_rules)
DescriptionSmall Text
Default EnabledCheckglobal default
CategorySelectpayroll / hr / billing / system
Enterprise OnlyCheck

Index: flag_key (unique).

9.3 Prego Plan Feature

FieldTypeNotes
PlanSelectbasic / pro / enterprise
Flag KeyLinkPrego Feature Flag
EnabledCheck
Limit ValueIntoptional

Composite unique: (plan, flag_key).

9.4 Prego Tenant Flag

FieldTypeNotes
Tenant SiteData
Flag KeyData
EnabledCheck

테넌트별 플래그 override.


10. Tenant 생성 시 도메인·TLS·Nginx (Phase 4 확장)

  • DNS 검증: create_tenant 전/중에 domain이 서버 IP를 가리키는지 확인(socket 등).
  • Certbot: provisioning job 내부에서 certbot --nginx -d <domain> (또는 wildcard 시 DNS challenge).
  • Nginx: tenant별 upstream/ server_name 설정 파일 생성 후 nginx -s reload.
  • 상세는 Runbook 또는 인프라 레포에서 “Tenant 프로비저닝” 절차로 문서화.

11. Enterprise 이미지 분리 (prego-docker 연동)

  • 옵션 A: Enterprise 전용 Frappe App 별도 레포(prego-enterprise-app).
    • prego-docker에서 apps-core.json (erpnext + hrms + prego-saas-app), apps-enterprise.json (+ prego-enterprise-app) 분리 빌드.
    • 태그: core-, enterprise-.
  • 옵션 B: 한 앱 내 enterprise 모듈 + flag 잠금 — 코드가 모든 고객 이미지에 포함되므로 보안/계약 측면에서 A가 유리.
  • 기획 단계에서는 옵션 A 권장; 구현 시 prego-docker 쪽 matrix 빌드(§9.2 prego-docker-implementation-plan)와 연동.

12. Provisioner Site 전용 관리자 UI (Phase 7)

목표: 테넌트·플랜·기능 플래그·배포·결제·사용량을 한 흐름으로 운영.

12.1 Workspace

  • 이름: “Prego Control Plane”. Provisioner site 전용.
  • 메뉴 구조: Tenant (Registry, Create Wizard, Domain & TLS, Health) / Plans & Features (Feature Flags, Plan Features, Tenant Overrides) / Billing (Stripe Customers, Invoices, Usage) / Deployments (Image Catalog, Promote, Rollback) / Operations (Jobs Queue, Audit, Alerts).

12.2 핵심 화면

화면내용
Tenant RegistryList: tenant_site, region, plan, status, domain_verified, tls_status, last_deploy_digest, last_seen_at. 액션: View, Suspend/Resume, Change Plan, Rebuild/Deploy, Regenerate Invite, Open Tenant.
Create Tenant WizardStep: 도메인/리전/플랜/관리자 이메일 → DNS 검증 → Job enqueue → 진행률/완료·초대 링크.
Tenant Detail탭: Overview, Domain & TLS, Billing, Usage, Deployments.
Feature Flag ConsoleFlag별 기본값, Plan별 enabled/limit, Tenant override 검색/적용.
Billing ConsoleTenant ↔ Stripe customer/subscription 매핑, 결제 실패 시 Grace → Suspend 정책.
Deployment ConsoleSKU별 image digest 목록, Staging→Production Promote(승인), Blue/Green 스위치/롤백 호출.

12.3 권한

  • Role: Prego Provisioner Admin.
  • Prego Tenant, Prego Feature Flag, Prego Plan Feature, Prego Tenant Flag 등: provisioner role만 CRUD.
  • 서버/API 레벨: provisioner site가 아니면 provisioning/billing webhook·API 거부.

13. Stripe + Usage 기반 과금 (Phase 8)

목표: 월 구독 + 초과 사용량(metered) Stripe 제출.

13.1 Stripe 측

  • Subscription에 metered price line item 추가.
  • Usage Record API로 주기적 제출.

13.2 Doctype

Doctype용도
Prego Usage Metricmetric_key(unique), unit, aggregation(sum/max/last), stripe_price_id, enabled
Prego Usage Event(선택) tenant_site, metric_key, quantity, event_ts, idempotency_key, source, raw_json. 트래픽 많으면 집계만 유지.
Prego Usage Aggregationtenant_site, period_start(YYYY-MM-01), metric_key, quantity_sum, quantity_max, last_updated_at, stripe_reported_quantity, stripe_last_reported_at, status(Open/Reported/Error). Unique: (tenant_site, period_start, metric_key).

13.3 파이프라인

  1. 앱에서 사용 발생 시 record_usage(metric_key, qty, idempotency_key) 호출 → aggregation만 원자 갱신.
  2. Scheduler(5~15분)에서 submit_usage_to_stripe(): delta = quantity_sum - stripe_reported_quantity; delta > 0면 Usage Record 생성 후 stripe_reported_quantity 갱신.
  3. Idempotency: idempotency_key로 중복 무시.

13.4 결제 실패 정책

  • past_due → status Past Due; grace period 후 read-only/제한; 지속 미납 → Suspended.
  • Webhook으로 정상화 시 Active 복구.

14. Multi-region SaaS (Phase 9, 확장)

목표: 테넌트를 리전별 배치, 데이터 주권·지연 최소화.

14.1 아키텍처

  • Control Plane (전역): Provisioner site, Tenant Registry, Billing/Usage, Deployment Orchestrator.
  • Regional Data Plane: 리전별 app + mariadb + redis + storage; 테넌트 site 데이터는 해당 리전만.

14.2 라우팅

  • 옵션 1 (권장): 리전별 서브도메인 — tenant.sg.prego.com, tenant.eu.prego.com.
  • 옵션 2: 동일 도메인 + Geo/DNS 라우팅(고급).
  • Prego Tenant.region 필드로 고정; 리전 이동은 마이그레이션 Runbook으로 별도.

14.3 배포

  • 이미지 빌드는 전역 1회(digest 고정).
  • Promote는 리전별(SG-staging→SG-prod, EU-staging→EU-prod); 리전별 승인 단계 분리 가능.

15. 구현 순서 요약 (Phase)

Phase내용
1최소 검증: health_check API, Sales Order hook, (선택) scheduler, notify-image-rebuild.yml
2최소 운영 템플릿: desktop, MANIFEST.in, pyproject.toml, payroll custom_validate, periodic_task
3SaaS 멀티테넌트: common_site_config/site_config 개념, Employee override(plan별 limit)
4Tenant 프로비저닝: create_tenant API, _create_tenant_job, Prego Tenant Doctype, (선택) DNS/Certbot/Nginx
5Feature Flag: Prego Feature Flag, Prego Plan Feature, Prego Tenant Flag, flags.py is_enabled
6Stripe: Prego Tenant Stripe 필드, webhook, create_checkout_session
7Provisioner UI: Workspace, Tenant Registry, Create Wizard, Feature Console, Billing Console, Deployment Console
8Usage 과금: Prego Usage Metric/Event/Aggregation, record_usage, submit_usage_to_stripe, idempotency
9Multi-region: Control Plane vs Regional Data Plane, region 필드, 리전별 배포 정책

16. 초기 스캐폴딩 (bench 명령)

  • Frappe v15 bench 환경에서: bench new-app prego_saas_app (App Title: Prego SaaS App, Publisher: Pregoi, License: MIT 등).
  • 생성 후 해당 앱 디렉터리를 prego-saas-app Git 레포로 둔 뒤, origin을 Pregoi/prego-saas-app으로 설정.
  • 이후 구현은 위 Phase 순서로 파일 추가·수정.

17. 연관 문서

문서연계
prego-docker-implementation-plan.md이미지 빌드·repository_dispatch·commit pin·compose.
provision-tenant-workflow-design.md프로비저닝 워크플로·입출력·콜백.
docker-hub-stack-ansible-deploy-plan.mdAnsible에서 앱 이미지 소스.
naming-conventions.md테넌트·site·도메인 네이밍.

다음 단계: 이 기획서를 바탕으로 /Users/marco/prego-saas-app(Pregoi/prego-saas-app)에 Phase 1부터 순서대로 코드·Doctype·워크플로를 구현한다.

Help