Solanasis Custom ERP/CRM Platform — Build Plan
Date: 2026-03-11 Status: Active Planning Document Related:
open-source-erp-research.md— Original ERP platform researcherp-crm-plugins-research.md— Plugin/licensing analysis, two-track strategytechnical-reference-frappe-framework.md— Frappe hook system, lifecycle events, override mechanismstechnical-reference-frappe-crm.md— Frappe CRM feature inventory, replication guidematchkeyz-to-frappe-translation-guide.md— Pattern-by-pattern implementation specs with code
CRITICAL STRATEGIC DECISION
We are building our own platform. While we start from ERPNext/Frappe as the foundation, we are intentionally diverging. This means:
- We do NOT need to maintain easy upstream upgradability. The cost of staying compatible with upstream ERPNext releases is not worth the constraint it places on our product. We will cherry-pick security patches and critical fixes, but we are not tracking upstream versions.
- We are building custom Frappe apps that replace major ERPNext modules (starting with CRM, eventually expanding to other modules as needed).
- The Matchkeyz meta-driven architecture patterns are our secret weapon — we will incorporate patterns that ERPNext/Frappe CRM simply don’t have.
- All custom code is proprietary — built on the GPL-3.0 Frappe Framework (safe for SaaS), never incorporating AGPL code into the resale product.
This is not a fork-and-tweak. This is a new product built on a proven foundation.
Current State (as of 2026-03-11)
| Component | Status |
|---|---|
| ERPNext v16.9.0 | Running at db.zasage.us (localhost:8080) |
| Frappe CRM 1.60.1 | Installed (internal use / reference only) |
| Frappe Insights 2.2.13 | Installed (internal use / reference only) |
| Frappe WhatsApp 1.0.12 | Installed (MIT — safe for resale) |
| Custom Docker image | solanasis/erpnext:v16-custom built and running |
| Cloudflare tunnel | Active via claude-bot tunnel → db.zasage.us |
| GitHub forks | dzinreach/erpnext, dzinreach/frappe |
Architecture Overview
┌─────────────────────────────────────────────────────────────┐
│ SOLANASIS PLATFORM │
│ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ CUSTOM FRAPPE APPS (Proprietary — GPL SaaS-safe) │ │
│ │ │ │
│ │ solanasis_core — Meta-schema engine, RecSaver, │ │
│ │ field-level audit, universal │ │
│ │ soft-delete, polymorphic entities│ │
│ │ │ │
│ │ solanasis_crm — Pipeline, deals, leads, scoring,│ │
│ │ unified timeline, multi-table │ │
│ │ forms, email sequences │ │
│ │ │ │
│ │ solanasis_whitelabel — Deep UI reskin, per-tenant │ │
│ │ theming, dark mode, custom nav │ │
│ │ │ │
│ │ solanasis_comms — WhatsApp, Twilio, cold email │ │
│ │ tool integration, email tracking │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ FRAPPE FRAMEWORK (GPL-3.0) + ERPNEXT (GPL-3.0) │ │
│ │ DocType system, REST API, auth, WebSocket, │ │
│ │ Accounting, Inventory, HR, Manufacturing │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ INFRASTRUCTURE │ │
│ │ MariaDB 10.6 | Redis | Docker | Cloudflare Tunnel │ │
│ └───────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
Why We’re Diverging From Upstream
| Concern | Our Position |
|---|---|
| ”What about upstream security patches?” | We monitor upstream releases and cherry-pick security fixes into our fork. This is a manual process but only needed for critical CVEs — not every release. |
| ”What about new ERPNext features?” | We evaluate each release. If a feature is useful, we port it. But we’re building features they don’t have, so upstream becomes less relevant over time. |
| ”What if we fall too far behind?” | That’s the point. Our product will be so different that “behind” doesn’t apply. We share the same foundation (Frappe Framework) but the application layer is ours. |
| ”Isn’t this more work?” | Yes. But the alternative is being constrained to ERPNext’s UX decisions, AGPL licensing traps, and a CRM that Frappe themselves have abandoned for new development. The custom build IS the product. |
What Matchkeyz Teaches Us (Patterns to Incorporate)
After deep analysis of the Matchkeyz codebase (/mnt/c/_matchkeyz_code/qa/), these are the architectural patterns that ERPNext/Frappe lack and that we will build into solanasis_core:
Pattern 1: Meta-Driven Table Registry
Matchkeyz has it: Every entity is registered in a tables table with parent relationships, JOIN semantics (join_on_col, parent_table_join_col, join_sql), form references, and table type classification. The framework auto-generates SQL JOINs for any view without developer intervention.
Frappe lacks it: DocTypes define fields and relationships, but JOIN logic is hardcoded per report/query. There’s no universal “give me a form spanning tables X, Y, Z with auto-generated joins.”
What we’ll build: A Solanasis Table Registry DocType that enriches Frappe’s DocType system with explicit join semantics, parent-child hierarchy metadata, and auto-SQL generation capabilities. This sits alongside Frappe’s DocType system, not replacing it — extending it.
Pattern 2: Multi-Table Form Composition
Matchkeyz has it: A single form (views table) can span multiple database tables via view_tables. Fields from different tables appear in the same form. Sections can embed sub-forms via for_view_id. Per-form field overrides (editable, required, name, placeholder) let the same field appear differently in different contexts.
Frappe lacks it: Forms map 1:1 to DocTypes. If you want data from multiple DocTypes in one view, you build a custom page or use Dashboard links. No per-form field overrides.
What we’ll build: A Solanasis View system that composes forms from multiple DocTypes with per-view field overrides. This is the foundation for building CRM deal views that show contact info, activity history, email threads, and deal data in a single unified form — without building separate custom pages for each combination.
Pattern 3: Field-Level Modification History
Matchkeyz has it: mods + mod_vals tables track every individual field change with old/new values, linked to the user, form, auto-action, and activity. Virtual “Record-Created” and “Record-Updated” sub-tables appear as first-class child tables.
Frappe has partially: Document versioning exists but tracks whole-document JSON diffs, not individual field changes. No easy way to query “who changed field X on record Y and when?”
What we’ll build: A Solanasis Audit Trail system that tracks field-level changes in a queryable format. For the CRM, this means: “Show me every time this deal’s value changed, who changed it, and from what to what.” This is essential for compliance, accountability, and the activity timeline.
Pattern 4: Polymorphic Entities with Virtual Table IDs
Matchkeyz has it: Single-table inheritance where contacts serves as People, Organizations, and Groups via contact_type_id. Negative table IDs (-101, -110) create virtual views with their own forms and child tables.
Frappe lacks it: Separate DocTypes for Lead, Customer, Supplier, etc. No single-table-inheritance pattern. Converting a Lead to a Customer creates a new record and links them, rather than changing a type flag.
What we’ll build: A unified Solanasis Contact DocType that can be a Person, Organization, or Group — with type-specific views and child tables. Conversion between types (Lead → Customer) is a field change, not a record migration. This dramatically simplifies the CRM data model and eliminates the fragmentation between Lead, Customer, Supplier, and Contact DocTypes that plagues ERPNext.
Pattern 5: Data-Driven Field Definitions with Runtime Schema Changes
Matchkeyz has it: Fields are data records. Adding a field is a database operation that both creates metadata AND alters the physical table via stored procedures. No code deployment needed.
Frappe has partially: Custom Fields exist and can be added at runtime via UI. But they’re second-class citizens — they don’t participate in the same way as core fields, and the customization options are limited.
What we’ll build: Enhanced custom field capabilities that match Matchkeyz’s sophistication — calculated fields (SQL expressions), per-form visibility/editability overrides, field-level encryption, edit-once restrictions, and admin-only fields. All configurable via the Solanasis admin UI.
Pattern 6: Universal Soft Delete with Lifecycle States
Matchkeyz has it: rec_type_id with states: active, archived, disabled, deleted, hidden, system-disabled, invited, suggested, denied. Every query filters by is_exclude flag.
Frappe has partially: docstatus (Draft=0, Submitted=1, Cancelled=2) for amendable documents. No universal soft-delete — deletion is usually hard delete with an optional “Trash” for some types.
What we’ll build: A universal rec_status field on all Solanasis DocTypes with configurable lifecycle states. Records are never hard-deleted. This is critical for audit compliance and data recovery.
Pattern 7: Auto-Actions and Scheduled Tasks as Data
Matchkeyz has it: Auto-actions (triggers), templates, and scheduled tasks share the views table. They have trigger criteria (filters), field mappings, versioning, and execution history — all configurable without code.
Frappe has partially: Server Scripts and Scheduled Jobs exist but require Python code. Notification rules are configurable but limited in what they can do.
What we’ll build: A Solanasis Automation engine where trigger rules, field mappings, and scheduled tasks are fully data-driven. A non-developer admin can set up “When a Deal moves to Won stage, create a Customer record and send a WhatsApp message” without writing code.
Pattern 8: Section-Based Form Layout with Grid Positioning
Matchkeyz has it: Sections with row, col for grid positioning, parent_section_id for nesting, for_view_id for embedding sub-forms.
Frappe has limited: Section Break, Column Break, Tab Break — linear layout only. No grid positioning, no section nesting, no sub-form embedding.
What we’ll build: Enhanced form layouts using Frappe UI’s Vue components with CSS Grid support, nested sections, and embeddable sub-views. This makes forms feel modern and information-dense without being overwhelming.
Custom App Specifications
App 1: solanasis_core
Purpose: Foundation layer providing the meta-schema engine and infrastructure patterns from Matchkeyz. All other Solanasis apps depend on this.
DocTypes to create:
| DocType | Purpose | Key Fields |
|---|---|---|
| Solanasis Table Registry | Meta-registry extending DocType | doctype_link, parent_registry, join_column, join_sql, table_type |
| Solanasis Field Override | Per-form field customization | source_field, target_view, is_editable, is_required, label_override, placeholder |
| Solanasis Audit Entry | Modification history header | doctype, docname, action_type, user, form_used, memo |
| Solanasis Audit Value | Field-level change detail | audit_entry, field, old_value, new_value, old_id, new_id |
| Solanasis Lifecycle State | Configurable record states | state_name, is_active, is_excluded, color, icon |
| Solanasis Automation Rule | Data-driven trigger rules | trigger_doctype, trigger_event, condition_filters, actions |
| Solanasis Automation Action | Action definitions | action_type, target_doctype, field_mapping, template |
Server-side infrastructure:
SolanasisModelbase class (Python) extending Frappe’sDocumentwith:- Automatic audit trail generation on save
- Universal soft-delete (override
delete()) - Lifecycle state management
- Field-level change detection
RecSaverAPI endpoint for multi-DocType saves from composite forms- Auto-JOIN SQL generator from Table Registry metadata
App 2: solanasis_crm
Purpose: Modern CRM replacing both ERPNext’s built-in CRM and serving as the alternative to AGPL Frappe CRM. Built entirely on solanasis_core + Frappe Framework.
DocTypes to create:
| DocType | Purpose | Key Fields |
|---|---|---|
| Solanasis Contact | Unified Person/Org/Group (polymorphic) | contact_type, name, email, phone, company, title, lifecycle_state, lead_score |
| Solanasis Deal | Sales pipeline deal | contact, deal_stage, value, probability, expected_close, assigned_to, source |
| Solanasis Deal Stage | Configurable pipeline stages | stage_name, order, default_probability, color, is_won, is_lost |
| Solanasis Activity | Unified timeline entry | contact, deal, activity_type, subject, detail, timestamp, user |
| Solanasis Activity Type | Activity categories | type_name, icon, color (email, call, note, meeting, task, whatsapp) |
| Solanasis Email Sequence | Multi-step drip campaigns | name, steps, trigger_criteria, status |
| Solanasis Email Step | Individual sequence step | sequence, delay_days, subject, body_template, condition |
| Solanasis Lead Score Rule | Scoring engine rules | field, operator, value, points, category |
| Solanasis CRM Settings | Pipeline config, defaults | default_stages, scoring_weights, auto_assignment_rules |
Custom Vue.js pages:
| Route | Purpose | Key UI Elements |
|---|---|---|
/app/solanasis-deals | Pipeline Kanban board | Drag-and-drop columns by stage, deal cards with value/contact/days-in-stage, quick filters |
/app/solanasis-deals/{id} | Deal detail | Unified activity timeline (left), deal fields (right), contact info card, quick actions bar |
/app/solanasis-contacts | Contact list with smart filters | Combined People/Orgs view, inline search, lead score badges, last activity date |
/app/solanasis-contacts/{id} | Contact detail | Multi-tab: Overview, Deals, Activities, Emails, Documents, Relationships |
/app/solanasis-dashboard | Sales analytics | Pipeline value by stage, conversion funnel, activity metrics, revenue forecast (eCharts) |
/app/solanasis-sequences | Email sequence builder | Visual step editor, delay configuration, template preview, performance metrics |
Integrations:
- Email: Built on Frappe’s email infrastructure (GPL) with added open/click tracking (tracking pixel + link wrapping)
- WhatsApp: Via
frappe_whatsapp(MIT) — safe for resale - Telephony: Direct Twilio Python SDK (MIT) — click-to-call, call logging
- Cold email: API webhook integration with external tools (Instantly.ai, Smartlead)
App 3: solanasis_whitelabel
Purpose: Complete UI transformation. Solanasis brand palette, per-tenant theming, dark mode.
Implementation:
- CSS variable overrides: Navy #020532, Parchment FEF9F1, Copper C47A3D
- Custom login page (Jinja2 template override)
- Redesigned sidebar navigation
- Command palette enhancement (Cmd+K)
- Per-tenant
Solanasis ThemeDocType (colors, logo, fonts per client) - Dark mode toggle
- Hide all ERPNext/Frappe branding
App 4: solanasis_comms
Purpose: Communication layer — WhatsApp, telephony, email campaigns, cold outreach integration.
Key components:
- WhatsApp messaging via
frappe_whatsapp(MIT) or direct Meta Cloud API - Twilio click-to-call and call logging via Python SDK (MIT)
- Email sequence execution engine (sends from Frappe’s email, tracks opens/clicks)
- Cold email tool webhook receiver (syncs leads/responses from external tools)
- Unified communication log feeding into
Solanasis Activity
Build Phases
Phase 1: solanasis_core Foundation
What: Build the core infrastructure layer — audit trail, soft delete, lifecycle states, base model class.
Deliverables:
- Frappe custom app scaffold:
bench new-app solanasis_core -
SolanasisModelbase class (extendsfrappe.model.document.Document)- Auto audit trail via
on_update/after_inserthooks - Field-level change detection via
get_doc_before_save()+ field comparison - Soft-delete interception via
on_trashoverride get_field_history(fieldname)helper for per-field history queries- See
matchkeyz-to-frappe-translation-guide.mdPattern 2 for full code
- Auto audit trail via
-
Solanasis Audit EntryDocType (maps to Matchkeyzmodstable)- Fields: ref_doctype, ref_docname, action_type, user, timestamp, form_used, memo, automation_rule
- Child table:
values→ Solanasis Audit Value
-
Solanasis Audit ValueDocType (maps to Matchkeyzmod_valstable)- Fields: field_name, field_label, old_value, new_value, old_ref_name, new_ref_name
-
Solanasis Lifecycle StateDocType + fixture data- Default states: Active, Archived, Deleted, Disabled, Draft
- Fields: state_name, is_active, is_excluded, color, icon
- Universal audit via
doc_events["*"]wildcard hookon_update+after_insert→ create Solanasis Audit Entryon_trash→ intercept and soft-delete (set lifecycle_state=Deleted)
- hooks.py with: doc_events, after_install, after_migrate, fixtures
- Unit tests for audit trail generation (insert + update + field comparison)
- Unit tests for soft-delete behavior (verify record not hard-deleted)
Key technical decisions:
- Use
"*"doc_events hook for universal coverage (all DocTypes get audit) - Store audit values as strings (Long Text) for uniformity — Matchkeyz does the same
- Audit entries use child table (not separate join) for atomic creation
- Lifecycle state is a Link field, not Select — allows runtime state addition
Dependencies: None (pure Frappe Framework)
Phase 2: solanasis_crm — Data Model
What: Create all CRM DocTypes, relationships, and server-side logic.
Deliverables:
-
Solanasis ContactDocType (polymorphic — maps to Matchkeyz Pattern 7)contact_typeSelect: Person/Organization/Group- Type-specific sections with
depends_onvisibility - Common fields: email, phone, lifecycle_state, lead_score, assigned_to, source
- Extends SolanasisModel for automatic audit trail
override_doctype_classfor Frappe’s Contact → sync to Solanasis Contact
-
Solanasis Deal+Solanasis Deal StageDocTypes- Deal: contact (Link), deal_stage (Link), value (Currency), probability, expected_close
- Stage: stage_name, order, default_probability, color, is_won, is_lost
on_updatehook: auto-create ERPNext Customer when deal stage is_won=True
-
Solanasis ActivityDocType (write-time aggregation — improvement over Frappe CRM)- Fields: contact, deal, activity_type (Link), subject, detail, timestamp, user
- Created by hooks on: email sent, call logged, note added, field changed, WhatsApp sent
- This replaces Frappe CRM’s runtime aggregation from 6 tables
-
Solanasis Activity TypeDocType (fixture)- Types: Email, Call, Note, Meeting, Task, WhatsApp, Field Change, Conversion
-
Solanasis Lead Score RuleDocType + scoring engine -
Solanasis Email Sequence+Solanasis Email StepDocTypes -
Solanasis Lost ReasonDocType (maps to Frappe CRM’s CRM Lost Reason) -
Solanasis CRM SettingsDocType - API endpoints (
solanasis_crm/api/):deals.py: move_deal, get_pipeline_data, mark_won, mark_lostcontacts.py: convert_type, merge_contacts, get_contact_timelinedata.py: get_data (generic list/kanban/group_by — maps to Frappe CRM’sapi/doc.py)dashboard.py: pipeline_value, conversion_funnel, activity_metrics
- Default fixtures: pipeline stages, activity types, lead score rules
- Migration script: import ERPNext Leads → Solanasis Contacts, Opportunities → Deals
Dependencies: solanasis_core
Phase 3: solanasis_crm — Vue.js UI
What: Build the modern frontend pages using Frappe UI (Vue 3 + Tailwind, both MIT).
Tech stack: Vue 3 + Vite + frappe-ui + Vue Router + Pinia + Tailwind CSS (all MIT)
Component architecture (informed by Frappe CRM’s 100+ component inventory):
solanasis_crm/frontend/src/
├── App.vue
├── router.js
├── stores/ # Pinia state management
│ ├── deals.js
│ ├── contacts.js
│ └── user.js
├── layouts/
│ ├── AppLayout.vue # Desktop: sidebar + content
│ ├── MobileLayout.vue # Mobile: hamburger + content
│ └── SidePanelLayout.vue # Detail: timeline + metadata
├── pages/
│ ├── Deals.vue # Pipeline Kanban + list toggle
│ ├── DealDetail.vue # Side panel: timeline left, fields right
│ ├── Contacts.vue # Contacts list with smart filters
│ ├── ContactDetail.vue # Multi-tab: Overview, Deals, Activities
│ ├── Dashboard.vue # eCharts: pipeline, funnel, metrics
│ └── EmailSequences.vue # Sequence builder
├── components/
│ ├── KanbanBoard.vue # Drag-and-drop pipeline columns
│ ├── KanbanCard.vue # Deal card (value, contact, days)
│ ├── ActivityTimeline.vue # Unified activity feed
│ ├── ActivityItem.vue # Single activity (email/call/note/etc)
│ ├── ViewControls.vue # List/Kanban/GroupBy toggle + filters
│ ├── QuickFilterField.vue # Inline filter inputs
│ ├── ColumnSettings.vue # Column visibility/order config
│ ├── CompositeForm.vue # Multi-DocType form (Pattern 5)
│ └── modals/
│ ├── DealModal.vue # Quick deal creation
│ ├── ContactModal.vue # Quick contact creation
│ ├── LostReasonModal.vue # Why deal was lost
│ └── ConvertModal.vue # Contact type conversion
└── composables/
├── useDeals.js # Deal data fetching + mutations
├── useContacts.js # Contact data + search
└── useActivities.js # Activity timeline data
Deliverables:
- Vue 3 + Vite + frappe-ui project scaffold
- Route registration via
website_route_rulesin hooks.py - Pipeline Kanban page with drag-and-drop (uses
get_pipeline_dataAPI) - Deal detail page with unified activity timeline (left) + deal fields (right)
- Contact list page with smart filters, inline search, lead score badges
- Contact detail page with multi-tab layout (Overview, Deals, Activities, Docs)
- Sales dashboard with eCharts (pipeline value, conversion funnel, activity metrics)
- ViewControls component (list/kanban/group_by toggle with saved preferences)
- Modal-based CRUD (create records without losing context)
- Mobile-responsive layouts for all pages
- Real-time updates via
frappe.realtime.on()(deal moved → Kanban updates)
Dependencies: Phase 2 (DocTypes must exist for the UI to query)
Phase 4: solanasis_whitelabel
What: Complete UI reskin.
Deliverables:
- Solanasis brand CSS variables and theme
- Custom login page
- Redesigned sidebar navigation
-
Solanasis ThemeDocType for per-tenant branding - Dark mode implementation
- All ERPNext/Frappe branding removed
- Command palette (Cmd+K) enhancement
Dependencies: Can start in parallel with Phase 2/3
Phase 5: solanasis_comms
What: Communication integrations.
Deliverables:
- WhatsApp send/receive via frappe_whatsapp
- Twilio click-to-call integration
- Email open/click tracking (tracking pixel + link wrapping)
- Email sequence execution engine
- Cold email tool webhook integration
- All communications logged as
Solanasis Activityentries
Dependencies: Phase 2 (needs Activity DocType)
Phase 6: solanasis_core — Advanced (Table Registry, Multi-Table Forms, Automations)
What: The advanced meta-schema patterns from Matchkeyz.
Deliverables:
-
Solanasis Table RegistryDocType with JOIN semantics -
Solanasis Field OverrideDocType for per-form customization - Auto-JOIN SQL generator
-
RecSavermulti-DocType save endpoint -
Solanasis Automation Rule+Solanasis Automation ActionDocTypes - Automation execution engine (data-driven triggers, no code needed)
- Composite form rendering (multi-DocType forms from View definition)
Dependencies: Phase 1 (core foundation), real usage data from Phases 2-5 to inform design
Matchkeyz Patterns → Solanasis Mapping
| Matchkeyz Pattern | Matchkeyz Implementation | Solanasis Implementation |
|---|---|---|
| Tables-as-data registry | tables table with 67 fields | Solanasis Table Registry DocType extending Frappe’s DocType |
| Fields-as-data | fields table + p_field_edit proc | Enhanced Custom Fields with calc expressions, per-form overrides |
| Views-as-data (forms) | views + view_tables + view_fields | Solanasis View DocType with multi-DocType composition |
| Modification history | mods + mod_vals tables | Solanasis Audit Entry + Solanasis Audit Value DocTypes |
| Polymorphic contacts | contacts with negative table_ids | Solanasis Contact with contact_type field + type-specific child tables |
| RecSaver (generic save) | Single API for multi-table saves | Custom Frappe API endpoint handling composite form saves |
| Soft delete | rec_type_id with is_exclude | lifecycle_state field with Solanasis Lifecycle State configuration |
| Auto-actions | Actions in views table | Solanasis Automation Rule + Solanasis Automation Action DocTypes |
| Section grid layout | sections with row/col/nesting | Vue components with CSS Grid + nested section support |
| Filter/Sort system | filters + filter_items + sort | Build on Frappe’s existing filter system + custom enhancements |
What We’re NOT Building (Yet)
These are ERPNext modules we’ll use as-is for now:
- Accounting — ERPNext’s accounting module is solid. No need to rebuild.
- Inventory/Stock — Same. Works well.
- HR/Payroll — Same. Use as-is.
- Manufacturing — Same. Use as-is.
- Projects — Evaluate later. May need CRM integration enhancements.
The custom build focuses on CRM, UI, communications, and the meta-schema engine — where ERPNext is weakest and where our Matchkeyz patterns give us a genuine advantage.
Docker Image Strategy (Going Forward)
Base: frappe/frappe:version-16 # Frappe Framework (GPL)
└── + ERPNext version-16 # ERP backbone (GPL)
└── + frappe_whatsapp version-16 # WhatsApp (MIT)
└── + solanasis_core # Our foundation (Proprietary)
└── + solanasis_crm # Our CRM (Proprietary)
└── + solanasis_whitelabel # Our UI (Proprietary)
└── + solanasis_comms # Our comms layer (Proprietary)
Tag: solanasis/platform:v1.x.x
Internal dev instance (current db.zasage.us) also includes:
- Frappe CRM (AGPL — reference only, never ships to clients)
- Frappe Insights (AGPL — reference only, never ships to clients)
License Compliance Checklist
- All custom apps (
solanasis_*) built on GPL Frappe Framework — SaaS deployment = no distribution = no source disclosure - No AGPL code in client-facing image (Frappe CRM, Helpdesk, Insights, Raven, Telephony excluded)
-
frappe_whatsapp(MIT) — safe to include - GPL license notice maintained in codebase
- Product never called “ERPNext” or “Frappe” in marketing/branding
- JavaScript distribution risk: custom Vue components served to browsers are built on MIT-licensed Frappe UI — keep proprietary business logic in server-side Python, not client-side JS where possible
- CORE_PATCHES.md tracks any direct modifications to upstream Frappe/ERPNext
- Legal review before first client deployment
File Locations
/home/zasage/_solanasis/
├── frappe_docker/ # Docker deployment
│ ├── compose.solanasis.yaml # Production compose
│ ├── .env # Secrets (gitignored)
│ ├── apps.json # App manifest for image builds
│ └── images/ # Dockerfiles
├── erpnext/ # Fork: dzinreach/erpnext
├── frappe/ # Fork: dzinreach/frappe
├── solanasis_core/ # (TO CREATE) Custom core app
├── solanasis_crm/ # (TO CREATE) Custom CRM app
├── solanasis_whitelabel/ # (TO CREATE) Custom UI app
├── solanasis_comms/ # (TO CREATE) Custom comms app
├── solanasis-docs/ # Documentation
│ ├── solanasis-custom-erp-crm-build-plan.md # THIS FILE — master plan
│ ├── erp-crm-plugins-research.md # Licensing & plugin analysis
│ ├── technical-reference-frappe-framework.md # Frappe hooks, lifecycle, overrides
│ ├── technical-reference-frappe-crm.md # CRM feature replication guide
│ ├── matchkeyz-to-frappe-translation-guide.md # Pattern implementation specs
│ └── open-source-erp-research.md # Original platform research
├── CORE_PATCHES.md # Upstream modification tracker
└── backups/ # Local backups
Implementation Quick Reference
How Solanasis Apps Hook Into Frappe
All custom behavior flows through hooks.py in each app. Here’s the hook architecture:
solanasis_core/hooks.py
├── doc_events["*"]["on_update"] → Universal audit trail
├── doc_events["*"]["after_insert"] → Audit trail for new records
├── doc_events["*"]["on_trash"] → Soft-delete interception
├── override_doctype_class → Override Contact, Customer behavior
├── after_install → Seed lifecycle states, table registry
├── after_migrate → Schema updates
└── fixtures → Lifecycle states, activity types
solanasis_crm/hooks.py
├── doc_events["Solanasis Deal"]["on_update"] → Customer creation on Won
├── website_route_rules → /crm/* → Vue SPA
├── scheduler_events.hourly → Email sequences, lead scoring
├── scheduler_events.daily → Stale deal alerts
├── app_include_css → CRM styles
└── fixtures → Deal stages, lead sources
solanasis_whitelabel/hooks.py
├── app_include_css → Theme CSS variables
├── app_include_js → Theme toggle, nav override
├── base_template → Custom login page
├── jinja.methods → Template helpers
└── extend_bootinfo → Per-tenant theme config
solanasis_comms/hooks.py
├── doc_events["WhatsApp Message"] → Activity creation on send/receive
├── scheduler_events.cron["*/5"] → Check incoming WhatsApp
├── scheduler_events.hourly → Process email sequences
└── doc_events["Communication"] → Email tracking pixel injection
Document Lifecycle Hook Order (for reference)
On INSERT: before_insert → before_validate → validate → before_save → [DB INSERT] → after_insert → on_update → on_change
On UPDATE: before_validate → validate → before_save → [DB UPDATE] → on_update → on_change
On DELETE: on_trash → [DB DELETE] → after_delete
See technical-reference-frappe-framework.md for complete flow diagrams with every sub-step.