Merge pull request #502 from christianlouis/copilot/update-plan-descriptions

fix(ui): rename "Business" → "Power" and rewrite plan copy for per-user pricing
This commit is contained in:
Christian Krakau-Louis
2026-03-07 21:44:40 +01:00
committed by GitHub
5 changed files with 42 additions and 21 deletions
+18 -14
View File
@@ -1,11 +1,12 @@
"""
Subscription tier definitions and enforcement utilities for DocuElevate SaaS.
All plans are priced per user per month (or per year with ~20 % discount).
Four tiers (prices ex-VAT; German customers +19 % MwSt):
- free $0/mo — 50 lifetime docs, 150 lifetime OCR pages, 1 dest
- starter $2.99/mo — 50/mo, 300 OCR pp/mo, 2 dests, 1 mailbox
- professional $5.99/mo — 150/mo, 750 OCR pp/mo, 5 dests, 3 mailboxes
- business $7.99/mo — 300/mo, 1500 OCR pp/mo, 10 dests, unlimited mailboxes
- power $7.99/mo — 300/mo, 1500 OCR pp/mo, 10 dests, unlimited mailboxes
Limits use 0 to represent "unlimited".
All paid tiers include a 30-day free trial (trial_days field).
@@ -14,14 +15,14 @@ All paid tiers include a 30-day free trial (trial_days field).
Infrastructure: CX32 (app+Redis €7.59) + CX22 (worker €3.79) + BX21 (storage €7.22) ≈ $24/mo
At 100 users infra share ≈ $0.24/user/mo.
Starter : OCR $0.45 + AI $0.012 + infra $0.24 + Stripe $0.34 = $1.04 → 65 % gross margin
Starter : OCR $0.45 + AI $0.012 + infra $0.24 + Stripe $0.34 = $1.04 → 65 % gross margin
Professional: OCR $1.13 + AI $0.035 + infra $0.24 + Stripe $0.42 = $1.82 → 70 % gross margin
Business : OCR $2.25 + AI $0.069 + infra $0.24 + Stripe $0.48 = $3.04 → 62 % gross margin
Power : OCR $2.25 + AI $0.069 + infra $0.24 + Stripe $0.48 = $3.04 → 62 % gross margin
After ~30 % German corporate tax: Starter 45 %, Professional 49 %, Business 43 %.
After ~30 % German corporate tax: Starter 45 %, Professional 49 %, Power 43 %.
At average usage (~40 % of quota) margins improve to 55-65 % after tax.
⚠ If GPT-4o (not mini) is configured, Business AI cost at max rises to ~$1.92/user,
⚠ If GPT-4o (not mini) is configured, Power AI cost at max rises to ~$1.92/user,
reducing after-tax margin to ~33 %. Recommend GPT-4o mini as default in production.
"""
@@ -46,7 +47,7 @@ TIER_DEFAULTS: dict[str, dict[str, Any]] = {
"free": {
"id": "free",
"name": "Free",
"tagline": "Explore DocuElevate at no cost",
"tagline": "Try DocuElevate free — no credit card needed",
"price_monthly": 0,
"price_yearly": 0,
"trial_days": 0,
@@ -75,7 +76,8 @@ TIER_DEFAULTS: dict[str, dict[str, Any]] = {
"starter": {
"id": "starter",
"name": "Starter",
"tagline": "Perfect for individuals getting started",
# Use case: freelancer sending ~50 invoices, contracts, or scanned receipts a month
"tagline": "Perfect for freelancers and side-project owners",
"price_monthly": 2.99,
"price_yearly": 28.99, # ≈ 80 % of monthly × 12 — save ~19 % (≈ 2½ months free)
"trial_days": 30,
@@ -89,7 +91,7 @@ TIER_DEFAULTS: dict[str, dict[str, Any]] = {
"max_mailboxes": 1,
"api_access": True,
"features": [
"50 documents / month",
"50 documents / month — invoices, contracts, receipts",
"2 storage destinations",
"300 OCR pages / month",
"25 MB max file size",
@@ -104,7 +106,8 @@ TIER_DEFAULTS: dict[str, dict[str, Any]] = {
"professional": {
"id": "professional",
"name": "Professional",
"tagline": "For growing teams that need more power",
# Use case: consultant or knowledge worker handling ~150 docs/month across multiple platforms
"tagline": "For knowledge workers managing documents daily",
"price_monthly": 5.99,
"price_yearly": 57.99, # ≈ 80 % of monthly × 12 — save ~19 %
"trial_days": 30,
@@ -118,7 +121,7 @@ TIER_DEFAULTS: dict[str, dict[str, Any]] = {
"max_mailboxes": 3,
"api_access": True,
"features": [
"150 documents / month",
"150 documents / month — reports, contracts, invoices",
"5 storage destinations",
"750 OCR pages / month",
"100 MB max file size",
@@ -133,8 +136,9 @@ TIER_DEFAULTS: dict[str, dict[str, Any]] = {
},
"business": {
"id": "business",
"name": "Business",
"tagline": "High-volume processing for organisations",
"name": "Power",
# Use case: power user — real estate agent, bookkeeper, or researcher processing ~10 docs/day
"tagline": "For power users with high-volume document workflows",
"price_monthly": 7.99,
"price_yearly": 76.99, # ≈ 80 % of monthly × 12 — save ~20 %
"trial_days": 30,
@@ -148,7 +152,7 @@ TIER_DEFAULTS: dict[str, dict[str, Any]] = {
"max_mailboxes": 0, # unlimited mailboxes
"api_access": True,
"features": [
"300 documents / month",
"300 documents / month — ~10 documents per day",
"10 storage destinations",
"1,500 OCR pages / month",
"Unlimited file size",
@@ -156,7 +160,7 @@ TIER_DEFAULTS: dict[str, dict[str, Any]] = {
"Unlimited email ingestion mailboxes",
"All ingestion methods",
"Webhooks & full API access",
"Dedicated support",
"Priority support",
],
"cta": "Start free trial",
"badge": "Best Value",
+12 -1
View File
@@ -2,6 +2,8 @@
DocuElevate uses database-backed subscription plans that are fully configurable by admins via the **Plan Designer** at `/admin/plans`. Four default tiers are seeded automatically on first startup.
All plans are priced **per user, per month** (or per year with ~20 % discount). There are no team, business, or enterprise tiers — every plan is a single-user subscription.
## Default Plans
| Plan | Monthly | Yearly | Docs/Month | Lifetime Docs | OCR Pages/Mo | Max File | Mailboxes | Destinations |
@@ -9,12 +11,21 @@ DocuElevate uses database-backed subscription plans that are fully configurable
| **Free** | $0 | $0 | — | 50 total | 150 total | 5 MB | 0 | 1 |
| **Starter** | $2.99 | $28.99 | 50 | — | 300 | 25 MB | 1 | 2 |
| **Professional** | $5.99 | $57.99 | 150 | — | 750 | 100 MB | 3 | 5 |
| **Business** | $7.99 | $76.99 | 300 | — | 1,500 | Unlimited | Unlimited | 10 |
| **Power** | $7.99 | $76.99 | 300 | — | 1,500 | Unlimited | Unlimited | 10 |
> Prices ex-VAT. German customers add 19% MwSt.
All paid plans include a **30-day free trial**.
### Intended Use Cases
- **Free** — Try DocuElevate with no commitment. Good for one-off experiments or evaluating the service.
- **Starter** — Freelancers and side-project owners sending ~50 invoices, contracts, or scanned receipts a month.
- **Professional** — Knowledge workers (consultants, paralegals, accountants) handling ~150 multi-page documents a month across several cloud destinations.
- **Power** — Power users with heavy daily workloads: real estate agents, bookkeepers, or researchers processing ~10 documents a day (≈ 300/month) with no file-size restrictions.
> The **plan_id** in the database remains `"business"` for the Power tier to preserve backwards compatibility. The display name shown to users is "Power".
## How Plans Are Stored
Plans are stored in the `subscription_plans` database table. On application startup, `seed_default_plans()` is called automatically — if the table is empty, the four built-in defaults are inserted. If plans already exist, the seed is a no-op.
+2 -2
View File
@@ -14,7 +14,7 @@
Choose the plan that's right for you
</h1>
<p class="text-indigo-100 text-lg max-w-2xl mx-auto">
From free exploration to unlimited enterprise processing — scale as your document workflows grow.
One price per person, per month — from casual exploration to power-user workflows. No team plans, no per-seat tiers.
</p>
<!-- Annual / Monthly toggle (cosmetic — actual billing handled separately) -->
@@ -318,7 +318,7 @@
<td class="px-4 py-3 text-center text-xs text-gray-500">Community</td>
<td class="px-4 py-3 text-center text-xs text-gray-700">Email</td>
<td class="px-4 py-3 text-center text-xs text-gray-700 font-medium">Priority email</td>
<td class="px-4 py-3 text-center text-xs text-indigo-700 font-bold">Dedicated</td>
<td class="px-4 py-3 text-center text-xs text-indigo-700 font-bold">Priority</td>
</tr>
</tbody>
+1 -1
View File
@@ -42,7 +42,7 @@
{% if tier_id == 'free' %}fa-seedling text-gray-500
{% elif tier_id == 'starter' %}fa-rocket text-blue-600
{% elif tier_id == 'professional' %}fa-star text-indigo-600
{% else %}fa-building text-purple-600{% endif %}
{% else %}fa-bolt text-purple-600{% endif %}
text-2xl" aria-hidden="true"></i>
</div>
<p class="text-xs font-semibold text-gray-400 uppercase tracking-widest">Current Plan</p>
+9 -3
View File
@@ -107,7 +107,7 @@ def test_free_tier_has_no_mailboxes():
@pytest.mark.unit
def test_business_tier_has_highest_limits():
"""Business tier must have the highest limits of all paid tiers."""
"""Power tier (plan_id 'business') must have the highest limits of all paid tiers."""
t = TIERS["business"]
# lifetime: no hard cap (0 = unlimited)
assert t["lifetime_file_limit"] == 0
@@ -121,9 +121,15 @@ def test_business_tier_has_highest_limits():
assert t["max_file_size_mb"] == 0
@pytest.mark.unit
def test_business_tier_display_name_is_power():
"""The 'business' plan_id must display as 'Power'."""
assert TIERS["business"]["name"] == "Power"
@pytest.mark.unit
def test_mailbox_limits_increase_by_tier():
"""Mailbox limits must increase across tiers: free=0, starter=1, professional=3, business=0(inf)."""
"""Mailbox limits must increase across tiers: free=0, starter=1, professional=3, power/business=0(inf)."""
assert TIERS["free"]["max_mailboxes"] == 0
assert TIERS["starter"]["max_mailboxes"] == 1
assert TIERS["professional"]["max_mailboxes"] == 3
@@ -144,7 +150,7 @@ def test_free_tier_has_no_trial():
@pytest.mark.unit
def test_pricing_order():
"""Paid tier prices must increase in order: starter < professional < business."""
"""Paid tier prices must increase in order: starter < professional < power."""
assert TIERS["starter"]["price_monthly"] < TIERS["professional"]["price_monthly"]
assert TIERS["professional"]["price_monthly"] < TIERS["business"]["price_monthly"]