ESWF — Store Manager & App Store Redesign¶
Implementation Notes · Architecture · Instructions · Next Steps¶
Date: 2026-02-24 Scope: App Store terminology, pricing model, dependency system, module filter, Store Manager DOP section, flexible email client
1. What Was Changed¶
1.1 Frontend Config — New Terminology¶
File: frontend/erp/src/config/types.ts
Added to SectionItem:
price?: 'community' | 'perpetual' | 'subscription'
appType?: 'core' | 'module' | 'addon' | 'integration'
requires?: string[] // app codes that must be installed first
forModules?: string[] // module tags for filter chips in App Store
European DOP standard terminology:
| Type | Description | Examples |
|---|---|---|
core |
Base system, cannot be uninstalled | Essentials |
module |
Major functional block | Fleet, Logistic, Accounting, CRM |
addon |
Feature extension on top of a module | FleetTrack, DriverApp, BankExchange |
integration |
Connector to external system | M.E.Doc, BAF Sync, eTTN |
Pricing model:
| Code | Badge colour | Description |
|---|---|---|
community |
gray | Included in free/open-source edition |
perpetual |
violet | One-time purchase, no recurring fee |
subscription |
orange | Monthly or annual recurring fee |
1.2 applications.ts — All Apps Updated¶
File: frontend/erp/src/config/applications.ts
All 17 apps now have appType, price, requires, forModules filled in.
Dependency tree:
Essentials (core, community — free self-hosted / subscription SaaS)
├── Fleet (module, community)
│ ├── FleetTrack (addon, subscription) requires: [Fleet]
│ ├── Driver App (addon, subscription) requires: [Fleet]
│ ├── eTTN (integration, subscription) requires: [Fleet]
│ └── Logistic (module, subscription) requires: [Essentials, Fleet]
│ └── BAF Sync (integration, perpetual) requires: [Logistic]
├── Accounting (module, subscription) requires: [Essentials]
│ └── Consolidator (module, subscription) requires: [Essentials, Accounting]
├── Bank Exchange (integration, perpetual) requires: [Essentials]
├── M.E.Doc (integration, subscription) requires: [Essentials]
├── CRM (module, subscription) requires: [Essentials]
├── Budgeting (module, subscription) requires: [Essentials]
├── HR Manager (module, subscription) requires: [Essentials]
├── E-Commerce (module, subscription) requires: [Essentials]
├── Client Portal (module, subscription) requires: [Essentials]
└── ESWF Chat (addon, subscription) requires: [Essentials]
1.3 ApplicationsPage.tsx — New Features¶
File: frontend/erp/src/components/Applications/ApplicationsPage.tsx
- New price badges: Community (gray) / Perpetual (violet) / Subscription (orange)
- New type badges: Core (red) / Module (blue) / Add-on (teal) / Integration (grape)
- Module filter chips: All | Essentials | Fleet | Logistic (multi-select, via
forModulesfield) - Dependency install guard: Install button disabled + tooltip if required modules are not installed
- Dependency uninstall guard: Uninstall button locked if other installed modules depend on this one
- Core protection:
coreapps always have Uninstall locked - Detail modal: shows dependency list with ✓/✗ indicators
1.4 i18n — New Keys¶
Both en.json and ua.json updated with:
- app.price_community, app.price_perpetual, app.price_subscription
- app.type_core, app.type_module, app.type_addon, app.type_integration
- Full storeManager.* namespace (50+ keys) in both languages
2. Backend Changes¶
2.1 Product Model — New Fields¶
File: backend/shop/models.py
Product.app_type # 'core' | 'module' | 'addon' | 'integration'
Product.requires # JSONField — list of product codes
Product.for_modules # JSONField — list of module tags
Product.price_monthly # DecimalField — monthly subscription price
Product.price_annual # DecimalField — annual subscription price
# pricing choices updated: community | perpetual | subscription | free (legacy) | paid (legacy)
Migration: backend/shop/migrations/0005_product_apptype_email.py
2.2 EmailSmtpSettings Model¶
EmailSmtpSettings:
name # Display name, e.g. "Google Workspace"
host # SMTP host
port # Default 587
username # SMTP username
password # App password / API key (plain text — use app passwords)
use_tls # STARTTLS (port 587)
use_ssl # SMTP over SSL (port 465)
from_email # Sender address
from_name # Sender display name
is_default # One server marked as default at a time (auto-enforced)
2.3 EmailLog Model¶
EmailLog:
smtp_settings # FK to EmailSmtpSettings
sent_by # FK to User
to_email # Recipient
subject # Subject
body # HTML body
status # 'sent' | 'failed'
error_message # SMTP error if failed
sent_at # auto timestamp
activation_code # optional FK to ActivationCode
2.4 Email Service¶
File: backend/shop/email_service.py
Key functions:
test_smtp_connection(settings_id) → {'ok': bool, 'error': str}
send_email(*, to_email, subject, body_html, settings_id=None, sent_by=None, activation_code=None) → EmailLog
send_activation_code(*, to_email, product_name, activation_code_str, lang='en', settings_id=None, sent_by=None) → EmailLog
Supports any SMTP server. Uses smtplib from stdlib — no external dependencies required.
2.5 New API Endpoints¶
Tenant-facing (existing + extended):
POST /api/v1/shop/activation-codes/generate/ — bulk generate codes
POST /api/v1/shop/activation-codes/{id}/send-email/ — send code by email
Staff-only manager API (/api/v1/shop/manager/):
GET /manager/licenses/ — all licenses across tenants
GET /manager/smtp-settings/ — list SMTP configs (password hidden)
POST /manager/smtp-settings/ — create SMTP config
PATCH/DELETE /manager/smtp-settings/{id}/ — update / delete
POST /manager/smtp-settings/{id}/test/ — test SMTP connection
POST /manager/smtp-settings/{id}/set-default/ — set as default server
POST /manager/email/send/ — send arbitrary email
GET /manager/email-log/ — email send history
2.6 Django Admin Additions¶
- ProductAdmin: new fields
app_type,requires,for_modules, prices in fieldsets - ActivationCodeAdmin: new action
send_codes_by_email— reads email from note field, sends via default SMTP - EmailSmtpSettingsAdmin: full CRUD +
test_connection_action - EmailLogAdmin: read-only log view
3. Frontend — Store Manager Section¶
New section: storeManagerSection in frontend/erp/src/config/storeManager.ts
Added to: frontend/erp/src/config/index.ts
Pages created in frontend/erp/src/components/StoreManager/:
| File | Description |
|---|---|
ProductsPage.tsx |
Product catalog with type/pricing filters |
LicensesPage.tsx |
All licenses across tenants with active/inactive filter |
ActivationCodesPage.tsx |
List, generate, send codes by email |
SmtpSettingsPage.tsx |
SMTP server management with presets and test connection |
EmailClientPage.tsx |
Compose email, insert activation codes, template support |
EmailLogPage.tsx |
Sent email history with status and body detail modal |
Routing: added componentPath detection in SectionPage.tsx before the generic ItemType router.
SMTP presets built in: Gmail, Gmail SSL, Office 365, Outlook, Mailgun, SendGrid, Meta.ua, UKR.net
Email templates built in: - Activation Code (EN + UA) - Subscription Renewal Reminder (EN + UA)
4. How to Apply¶
Step 1 — Run Django migration¶
Step 2 — Configure a SMTP server¶
Either via Django Admin /admin/shop/emailsmtpsettings/add/ or in DOP:
Store Manager → Email → SMTP Settings → Add Server
For Gmail:
1. Enable 2-step verification on the Google account
2. Create an App Password: Google Account → Security → App Passwords
3. Use smtp.gmail.com:587, TLS, enter the 16-character App Password (not your main password)
For Office 365 / Outlook:
- Host: smtp.office365.com, port 587, TLS
- Username: your full email, password: your account password (or App Password if MFA enabled)
For Custom / Corporate SMTP: - Ask your IT admin for: host, port, TLS/SSL setting, username, password
Step 3 — Test the connection¶
In DOP: SMTP Settings → select server → Edit → Test Connection Or in Django Admin: select settings → action "Test SMTP connection"
Step 4 — Generate activation codes¶
DOP: Store Manager → Activation Codes → Generate
Or Django Admin: Products → select product → action "Generate 10/50 activation codes"
Step 5 — Send codes to clients¶
Option A — Quick (from Activation Codes list): Click the mail icon on any unused code → enter client email → Send
Option B — Email Client (full compose):
Store Manager → Email → Email Client → select template, fill in, insert code from sidebar
Option C — Django Admin bulk send:
Activation Codes → select codes → action "Send selected codes by email"
(Requires the client email in the Note field of each code)
5. Known Limitations & Next Steps¶
Security¶
- SMTP passwords stored in plain text in the DB. Recommended fix: encrypt with
django-cryptographyor move to environment-based SMTP config. For now, use App Passwords (not main passwords). - The Manager API endpoints check
is_staffflag only. For multi-tenant SaaS with support managers, add a dedicatedis_support_managerpermission group.
Frontend¶
- Store Manager section visibility: currently always shown. Should be gated by
is_staffflag from auth store. Add role check in Sidebar/SectionDashboard. - Module filter in App Store currently only filters by explicit
forModulestags. Apps with emptyforModules(e.g. Essentials, Fleet) are always shown even when a module filter is active. Consider: "no filter = show all". - i18n storeManager namespace added to both
en.jsonandua.json, but Store Manager pages currently use hard-coded bilingual strings for speed. Future: migrate tot('storeManager.xxx')calls.
Backend¶
EmailComposeViewSetusesViewSetinstead ofModelViewSet— the/email/endpoint inmanager_routerneeds a quirk: thesendaction is accessed at/manager/email/send/. If Django Router complains about missing list action, add an empty list:- Pagination on
ActivationCode.generatereturns up to 500 codes. For very large batches, consider a background Celery task. - No email queue/retry mechanism yet. Failed emails are logged but not retried. Consider: Celery + Redis for async sending.
Pricing / Licensing model¶
- The
communitypricing means the product code exists in the backend but no actual license check is required. The activation flow forcommunityapps calls/activate/without an activation code — this works with the existing view. perpetuallicenses should never expire (expires_at = null). Subscription licenses should have anexpires_atand a background task to check/notify on expiry. Not yet implemented.- Subscription renewal flow (payment, invoice, re-issue of activation code) is not yet implemented — depends on payment gateway integration.
Email¶
- HTML email rendering in
EmailLogPageusesdangerouslySetInnerHTML— only safe because the body was written by a staff user. Do not allow client-submitted HTML here. - The "insert code" side panel in Email Client loads only the 20 most recent unused codes. Add product filter if catalog grows.
6. File Map¶
backend/shop/
├── models.py ← Product (updated), EmailSmtpSettings, EmailLog (new)
├── email_service.py ← NEW — SMTP send, test, templates
├── serializers.py ← Updated + EmailSmtpSettingsSerializer, EmailLogSerializer
├── views.py ← Updated + ManagerLicenseViewSet, EmailSmtpSettingsViewSet,
│ EmailComposeViewSet, EmailLogViewSet
├── urls.py ← Updated + /manager/ sub-router
├── admin.py ← Updated — send-by-email action, SMTP+Log admins
└── migrations/
└── 0005_product_apptype_email.py ← NEW
frontend/erp/src/
├── config/
│ ├── types.ts ← Updated — appType, requires, forModules, new price types
│ ├── applications.ts ← Updated — all apps with new fields
│ ├── storeManager.ts ← NEW — Store Manager section config
│ └── index.ts ← Updated — storeManagerSection added
├── api/
│ └── shop.ts ← Updated — new types + managerApi
├── components/
│ ├── Applications/
│ │ └── ApplicationsPage.tsx ← Rewritten — new badges, filter chips, dep logic
│ └── StoreManager/ ← NEW directory
│ ├── ProductsPage.tsx
│ ├── LicensesPage.tsx
│ ├── ActivationCodesPage.tsx
│ ├── SmtpSettingsPage.tsx
│ ├── EmailClientPage.tsx
│ └── EmailLogPage.tsx
├── pages/
│ └── SectionPage.tsx ← Updated — componentPath routing for StoreManager
└── i18n/locales/
├── en.json ← Updated — new app.price_*, app.type_*, storeManager.*
└── ua.json ← Updated — same