Перейти до змісту

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 forModules field)
  • 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: core apps 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

cd backend
python manage.py migrate shop

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-cryptography or move to environment-based SMTP config. For now, use App Passwords (not main passwords).
  • The Manager API endpoints check is_staff flag only. For multi-tenant SaaS with support managers, add a dedicated is_support_manager permission group.

Frontend

  • Store Manager section visibility: currently always shown. Should be gated by is_staff flag from auth store. Add role check in Sidebar/SectionDashboard.
  • Module filter in App Store currently only filters by explicit forModules tags. Apps with empty forModules (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.json and ua.json, but Store Manager pages currently use hard-coded bilingual strings for speed. Future: migrate to t('storeManager.xxx') calls.

Backend

  • EmailComposeViewSet uses ViewSet instead of ModelViewSet — the /email/ endpoint in manager_router needs a quirk: the send action is accessed at /manager/email/send/. If Django Router complains about missing list action, add an empty list:
    def list(self, request):
        return Response({'detail': 'Use /send/ endpoint'})
    
  • Pagination on ActivationCode.generate returns 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 community pricing means the product code exists in the backend but no actual license check is required. The activation flow for community apps calls /activate/ without an activation code — this works with the existing view.
  • perpetual licenses should never expire (expires_at = null). Subscription licenses should have an expires_at and 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 EmailLogPage uses dangerouslySetInnerHTML — 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