Ai Saas

Install Ai Saas

bench get-app https://github.com/bboa3/ai_saas

Add the Frappe Gems badge to your README

Maintain Ai Saas? Paste this into your README:

[![Listed on Frappe Gems](https://frappegems.com/api/method/frappe_gems.seo.badge?app=bboa3%2Fai_saas)](https://frappegems.com/gems/apps/bboa3/ai_saas)

About Ai Saas

AI SaaS

Frappe app that manages SaaS marketing, customer contracts and billing for MozEconomia Cloud. Depends on erpnext and erpnext_mz.


Table of Contents

  1. Installation
  2. DocTypes
  3. Contract Custom Fields
  4. Contract Lifecycle
  5. Email Notifications
  6. Billing Monitor
  7. Scheduled Tasks
  8. File Structure
  9. Verification & Testing

Installation

# 1. Ensure the Python package is installed in the bench virtualenv
./env/bin/pip install -e apps/ai_saas

# 2. Add to the bench apps list
echo "ai_saas" >> sites/apps.txt

# 3. Install on the site
sudo -u erp-user bench --site erp.local install-app ai_saas

# 4. Migrate (creates tables, syncs custom fields, installs client scripts)
sudo -u erp-user bench --site erp.local migrate

# 5. Clear cache
sudo -u erp-user bench --site erp.local clear-cache

Required apps (must be installed first): erpnext, erpnext_mz

To re-apply setup after code changes without a full migrate:

bench --site erp.local execute ai_saas.install.after_migrate

DocTypes

MZ Overdue Review

The one place a human decides on an account, and its audit trail. Created by the daily billing scheduler for overdue invoices, from the Contract form (button Rever conta) for a manual suspend/reactivate, or by the customer from /reactivar?contract=&token= (api/reactivation.request) after a suspension or archive email. Setting review_status executes through tenant_lifecycle, which reports the new stage to the Opportunity.

Field Type Description
customer Link: Customer Customer with overdue balance
contract Link: Contract Associated contract
outstanding_amount Currency Total outstanding
overdue_since Date Invoice due date
origin Select Facturação (engine) / Manual (Contract button) / Pedido do Cliente (/reactivar)
review_status Select Pending Review / Suspend / Reactivate / Deactivate
assigned_to Link: User Commercial staff responsible
notes Text Internal notes

Autoname: MZ-OVERDUE-.YYYY.-.#####

Permissions: System Manager (full), Accounts Manager (read/write, no delete), Sales Manager (create/write)

Deduplication: the scheduler only creates a new record if no record with review_status = "Pending Review" already exists for the same contract.


Contract Custom Fields

Defined in install.py (_sync_custom_fields, applied on after_install / after_migrate) and mirrored in fixtures/custom_field.json. Visible in the "MozEconomia Cloud" tab of the Contract form.

Fieldname Type Description
mz_saas_tab Tab Break "MozEconomia Cloud" tab
mz_subscription_plan Link: Subscription Plan Plan to bill. Editable after submit until a Subscription is linked (the customer may correct it at activation)
mz_tenant Data Customer subdomain slug — the user types only the prefix (e.g. boa-construtora)
mz_tenant_url Data (read-only) Full access domain: .erp.mozeconomia.co.mz
contact_email / mz_contact_name / mz_contact_mobile read-only fetch_from Mirrors of the Customer (email_id, customer_primary_contact, mobile_no) — the primary Contact is the source; nothing writes these directly
mz_linked_subscription Link: Subscription (read-only) Subscription created at signature
mz_apps_to_install Table: MZ Tenant App Apps installed on the tenant (defaults from provisioning.DEFAULT_APPS)
mz_billing_start Date (read-only) The day billing actually began (later of start_date and the signature date); the post-contract notifications anchor here

Lead.mz_segment (Link: Segment Intelligence Map) carries the industry captured at signup.

There is no stored account phase: tenant_lifecycle.account_phase(contract) derives it — no provisioning row → "" (invisible to the engine), provisioning Archived → Closed, Suspended → Suspended, is_signed → Active, else Trial. live_trials() is the one query the engine, the usage probe and the signup ceiling share.

Opportunity.mz_stage_since (Datetime) and Opportunity.mz_signup (Link) are the two fields the remarketing campaigns read — see The Funnel.

Tenant URL UX

mz_tenant is rendered with a visual suffix .erp.mozeconomia.co.mz glued to the right of the input (Client Script "AI SaaS - Contract"); mz_tenant_url is computed as the user types and stores the domain only.

Property Setters

Field Property Value Reason
Contract.start_date reqd 1 It is the trial end and the billing anchor

The Funnel

The full design is in docs/sales-funnel.md (business rationale) and docs/sales-funnel-implementation.md (itemized, code-verified plan with a per-row review of what was built). In one paragraph:

/registo step 1 already creates the Lead and the Opportunity (stage Cloud - Form Started); finishing the form creates Customer (group Cloud - Trial), primary Contact, Billing Address and an unsigned, submitted Contract (stage Cloud - Account Created). Submission provisions the tenant site (trial begins, no Subscription). A daily probe reads usage from the trial site and raises hot/cold lead signals; countdown emails anchor on Contract.start_date. /activar signs the contract — through the document save path, so the hooks run — which creates the Subscription with billing starting on the later of start_date and the signature date. If nobody signs, the lifecycle engine suspends the site on start_date, and archives it (full backup, then drop-site) after the grace period. Overdue invoices suspend 33 days after their due date. Nothing counts days; everything reads dates.

The Opportunity is the funnel's ledger. Every lifecycle event reports its sales_stage through crm.report(), which stamps mz_stage_since: Form Started → Account Created → Trial Engaged / At Risk (probe) → Activated (signature, Converted) · Trial Expired / Suspended (engine or review) → Closed (archive, Lost). Campaigns are Notifications on Opportunity, Days After mz_stage_since: G1 unfinished signups, G2 expired trials, G3 closed accounts. The contact lives in the Lead until there is a Customer, then in the Customer's primary Contact — Opportunity and Contract only mirror it.

Two triggers, split along the signature (ai_saas.saas.contract_lifecycle)

Event Handler Does
Contract.on_submit on_contract_submitted Queues provisioning regardless of signature, creates the Subscription only if already signed
Contract.on_update_after_submit on_contract_signed Acts only on the is_signed 0→1 transition: Subscription, Opportunity → Activated/Converted, customer moved out of the trial group; guards the plan against changes once a Subscription exists
Contract.on_cancel on_contract_cancel Cancels the linked Subscription

Subscription: generate_invoice_at = "Beginning of the current subscription period", submit_invoice = 1, days_until_due = 7, generate_new_invoices_past_due_date = 0. When billing starts today, the first invoice is issued immediately (sub.process(today)) — ERPNext only generates on an exact date match.

Modules

Module Role
api/signup.py Guest endpoints behind /registo: start, update, check_subdomain, submit, status. Token-only access; one live signup per email; duplicates refused generically at submit
saas/activation.py + www/activar Token-gated activation page and transaction (get_activation_url is exposed to Jinja)
saas/provisioning.py Site creation (provision_tenant), retry of Failed records, delivery email from the MozEconomia Cloud - Entrega da Conta Email Template
saas/tenant_lifecycle.py suspend / reactivate / archive and the daily engine (process_lifecycle) — unarmed by default
saas/usage_signals.py + erpnext_mz.utils.tenant_usage Daily read-only probe of trial sites (the probe ships in erpnext_mz, the app tenants have) → MZ Tenant Usage Snapshot → hot/cold signals on the Opportunity
saas/billing_monitor.py Pre-billing reminder, D+1 overdue review row, commercial follow-up ToDo + call Event
saas/crm.py The funnel ledger: STAGES, report / report_for_contract / touch, Opportunity resolution, sales ToDos
api/reactivation.py + www/reactivar Token-gated customer reactivation request → MZ Overdue Review (origin Pedido do Cliente)
install.py Custom fields, Contract Template, Email Template, Calendly booking link (MZ SaaS Settings.booking_url), trial Customer Group, sales stages, legacy-form retirement

DocTypes

DocType Role
MZ SaaS Settings (Single) Trial length, dry-run switch, suspension/grace/reminder thresholds, ops alert recipients, default sales user, commercial customer group, self-service ceilings
MZ Signup One row per signup in progress (resume token, step timestamps, links to the documents created)
MZ Tenant Provisioning One record per contract for life: status incl. Suspended / Archived, suspended_on, backup_path, log
MZ Tenant Usage Snapshot One row per trial per day from the probe
MZ Overdue Review Dunning review queue; Suspend / Reactivate / Deactivate execute through tenant_lifecycle

Email Notifications

Fixtures in fixtures/notification.json — 32 records, all AI SaaS - *. None sends to a bare role.

Group Records Anchor Recipient
G1 unfinished-signup nurture Lead Nurture - Dia 0 (on New), Dia 3/10/20 (Days After mz_stage_since) Opportunity, sales_stage == "Cloud - Form Started" contact_email; every body carries the resume link via mz_signup
G2 expired trials Trial Expirado - Dia 1/7/21 (Days After mz_stage_since) Opportunity, Cloud - Trial Expired contact_email; activation link — signing is the reactivation
G3 closed accounts Conta Encerrada - Dia 3/30 (Days After mz_stage_since) Opportunity, Cloud - Closed, only while a backup_path exists contact_email; /reactivar link, retention from archive_retention_days
Trial countdown Trial - 7 dias, - 3 dias, - Último dia amanhã, - Hoje (Days Before start_date) Contract, unsigned contact_email; copy branches on the latest usage snapshot; activation + booking links
Invoice + dunning Fatura Emitida, SMS Fatura Emitida, Lembrete 1/2/3 (+1/+4/+7 from posting_date), Aviso de Suspensão (+1 from due_date), Aviso de Desativação (+30 from due_date), Recibo de Pagamento — every one conditioned on doc.is_return == 0 Sales Invoice / Payment Entry contact_email (+ billing contacts in CC)
Credit note Nota de Crédito (on Submit, doc.is_return == 1 and not doc.is_pos), Nota de Crédito (MZ) attached Sales Invoice contact_email (+ billing contacts in CC)
Internal Escalação Comercial (+7) Sales Invoice role Sales Manager
Post-contract Pós-Contrato Dia 3/5/33 (Days After mz_billing_start) Contract, signed contact_email
Booking Marcação Confirmada (on New) Appointment customer_email

Every customer-facing email names only actions and dates the engine will actually take: suspension is 33 days after the due date, never "nas próximas horas".

Two delivery paths. Notifications always go through the Email Queue, which only the scheduler's frappe.email.queue.flush sends — a site with the scheduler disabled shows them Not Sent with no error. frappe.sendmail(..., delayed=False) mails (deli

Related Other apps for Frappe & ERPNext

  • Erpnext — Free and Open Source Enterprise Resource Planning (ERP)
  • Helpdesk — Modern, Streamlined, Free and Open Source Customer Service Software
  • Print Designer — Visual print designer for Frappe / ERPNext
  • Ctr — CTR模型代码和学习笔记总结
  • Whitelabel — Whitelabel ERPNext
  • Fossunited — fossunited.org
  • Helm — Helm Chart Repository for Frappe/ERPNext
  • Frappe Attachments S3 — A frappe app to upload file attachments in doctypes to s3.