Skip to main content

Bilko Add-on Arhitektura

"

Bilko Add-on Arhitektura

\n\n

MC #105282 | Feature-Flag Modularni Sistem | 2026-07-12

\n\n

Bilko add-on sistem omogućava fleksibilno uključivanje/isključivanje modula po organizaciji kroz kombinaciju tier-baziranog pristupa (PAUSALAC/BIZNIS/RACUNOVODJA/ENTERPRISE) i live Unleash feature flagova.

\n\n
\n\n

1. Arhitektura Pregled — Dva Sloja

\n\n

Bilko koristi dvoslojni gating sistem za kontrolu pristupa funkcionalnostima:

\n\n

Sloj A — Tier Baseline (in-memory, brzo)

\n
    \n
  • Lokacija: apps/api/src/main/kotlin/no/alai/bilko/features/FeatureAccess.kt
  • \n
  • Komponente: Feature enum (35 stavki) + FeatureMatrix (tier → feature mapping)
  • \n
  • Tierovi: BASIC / PRO / PREMIUM / ENTERPRISE (legacy nazivi u kodu, mapiraju se na PAUSALAC/BIZNIS/RACUNOVODJA/ENTERPRISE iz pricing-matrix.json)
  • \n
  • Svrha: Paket-nivo uključenost (šta je uključeno u osnovnu pretplatu)
  • \n
  • Brzina: In-memory decision table, bez mrežnih poziva
  • \n
\n\n

Sloj B — Unleash Add-on Override (live-toggle, per-org)

\n
    \n
  • Lokacija: apps/api/src/main/kotlin/no/alai/bilko/services/featureflags/
  • \n
  • Komponente: BilkoFlag enum (19 flagova) + UnleashFeatureFlagService
  • \n
  • Unleash server: bilko-unleash ACA (Azure Container Apps, rg-bilko-demo, purplebeach-f004d490)
  • \n
  • Svrha: Per-organizacija override iznad tier baseline-a — omogućava kupovinu add-ona bez mijenjanja cijelog tiera
  • \n
  • Targeting: FeatureFlagContext (userId, organizationId, planTier, country, userRole, orgType)
  • \n
\n\n

Lanac Odluke — checkFeature()

\n\n

Svaki zaštićeni endpoint poziva ApplicationCall.checkFeature(feature, principal) koje implementira sljedeći lanac:

\n\n
1. tier = loadPlanTier(organizationId)                      // DB lookup\n2. if FeatureMatrix.check(feature, tier).allowed → DOZVOLI  // Sloj A: uključeno u paket\n3. else:\n     addonFlag = FeatureFlagMapping.addonFlagFor(feature)  // SSOT mapiranje\n     if addonFlag != null:\n       orgType = loadOrgType(organizationId)               // INTERNAL/PILOT/COMMERCIAL\n       context = FeatureFlagContext(userId, orgId, tier, orgType, ...)\n       if unleash.isEnabled(addonFlag, context) → DOZVOLI  // Sloj B: kupljen add-on\n4. throw FeatureGatedException(403 FEATURE_GATED)          // Odbij\n
\n\n

Mermaid Dijagram

\n\n
flowchart LR\n    A[HTTP Request] --> B{checkFeature}\n    B --> C[loadPlanTier]\n    C --> D{FeatureMatrix
tier baseline?}\n D -->|allowed| Z[200 OK]\n D -->|not allowed| E[FeatureFlagMapping
SSOT lookup]\n E --> F{addonFlag
postoji?}\n F -->|null| X[403 FEATURE_GATED]\n F -->|BilkoFlag| G[loadOrgType]\n G --> H[Build FeatureFlagContext]\n H --> I{Unleash
isEnabled?}\n I -->|true| Z\n I -->|false| X\n
\n\n
\n\n

2. Modul-Matrica — 8 Add-ona

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Add-on ModulBilkoFlagCijena (EUR/mj)StatusEnforcement
Plate / PayrollPAYROLL_MODULE
(bilko.addon.payroll)
€15CONFIRMEDPayrollRoutes.kt (4 grupe ruta)
Feature.PAYROLL_MODULE_ACCESS
MC #105288, commit fc3d2ab8
Računovodstvo SIMPLEACCOUNTING_SIMPLE
(bilko.addon.accounting-simple)
€5CONFIRMEDReportRoutes.kt REPORT_PDF_EXPORT + REPORT_EXCEL_EXPORT
MC #105321, commit (u letu)
Računovodstvo FULL (GL)ACCOUNTING_FULL_ADDON
(bilko.addon.accounting-full)
€25CONFIRMED9 GL_* ruta: AccountingRoutes, ApRoutes, GlBankRoutes, JournalEntriesRoutes, ReportingRoutes
MC #105288, commit fc3d2ab8
API pristupAPI_PUBLIC
(bilko.api.public)
€29PLACEHOLDERBilkoFlag postoji (T2), route enforcement TBD
Napredni izvještajiREPORTS_ADVANCED
(bilko.reports.advanced)
€9PLACEHOLDERBilkoFlag postoji, route TBD
Auto-sync banke (Tok)BANKING_FEED_TOK
(bilko.banking.feed-tok)
€12PLACEHOLDERBilkoFlag postoji, route TBD
Multi-org računovođeACCOUNTANT_MULTI_ORG
(bilko.accountant.multi-org)
€39PLACEHOLDERBilkoFlag postoji, route TBD
AI asistentAI_ASSISTANT
(bilko.ai.assistant)
€9PLACEHOLDERBilkoFlag postoji, route TBD
\n\n

Napomena: CONFIRMED status = route enforcement backing postoji (mogu se naplaćivati). PLACEHOLDER = prijedlog cijena, ne mogu se naplatiti dok route enforcement ne postoji.

\n\n

Pricing benchmark: Fiken payroll €10-13, Tripletex €5.80/user, mi €15 FLAT MVP.

\n\n
\n\n

3. Flag → Ruta Mapping

\n\n

GL (Računovodstvo FULL) — 9/9 Features

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Feature EnumRute (ukupno 30 call-sites)Fajl
GL_CHART_OF_ACCOUNTSGET /accounting/accounts
PUT /accounting/account-mapping
AccountingRoutes.kt
GL_JOURNAL_ENTRIESGET/POST /accounting/journal
GET/POST /accounting/entries*
GET/POST /journal-entries (alias)
AccountingRoutes.kt
JournalEntriesRoutes.kt
GL_TRIAL_BALANCEGET /accounting/trial-balanceAccountingRoutes.kt
GL_AP_LEDGERGET/POST /accounting/purchase-invoices*
POST /supplier-payments
GET /ura
ApRoutes.kt
GL_VAT_FORMGET /accounting/pdv-obrazac*
GET /pdv-s
GET /zp
ReportingRoutes.kt
GL_BALANCE_SHEETGET /accounting/salda-kontiReportingRoutes.kt
GL_ANALYTICSGET /accounting/account-card/{code}ReportingRoutes.kt
GL_OPENING_BALANCEPOST /accounting/opening-balanceApRoutes.kt
GL_BANK_IMPORTGET/POST /accounting/bank-import
GET/POST /bank-transactions*
GlBankRoutes.kt
\n\n

Payroll (Plate) — 4 Grupe Ruta

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Feature EnumRute (7 call-sites)Fajl
PAYROLL_MODULE_ACCESSGET/POST/PUT/DELETE /payroll/employees*
POST /payroll/calculate
GET/POST /payroll/payslips
PayrollRoutes.kt
\n\n

Reports (Izvještaji) — 19 Gated / 9 Exempt

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Feature EnumRute (gated)Fajl
REPORT_VIEWGET /reports/ (root)
/dashboard, /profit-loss, /balance-sheet, /cash-flow, /vat, /trial-balance, /general-ledger
ReportRoutes.kt
REPORT_PDF_EXPORTGET /reports/*/export/pdf (vat, profit-loss, balance-sheet, trial-balance, cash-flow)ReportRoutes.kt
REPORT_EXCEL_EXPORTGET /reports/*/export/xlsx
GET /reports/accountant-export
ReportRoutes.kt
\n\n

Izuzeci — NIKAD gated (zakonski registri):

\n
    \n
  • KPO (RS paušalni registar): GET /reports/kpo + /kpo/export/pdf + /kpo/export/xlsx
  • \n
  • KIR (HR PDV-obveznik registar): GET /reports/kir + /kir/export/pdf + /kir/export/xlsx
  • \n
  • KPR (HR PDV-obveznik registar): GET /reports/kpr + /kpr/export/pdf + /kpr/export/xlsx
  • \n
\n\n

Pravno obrazloženje: PorezniKIR/KPR registri suobavezna zakonskaevidencija obavezaulaznih/izlaznih obveznikaračuna (čl.po 79 ZPDV HR, čl. 56 ZakonZakonu o porezuPDV-u na(HR). dohodakKPO građana RS)knjiga obavezna evidencija za paušalne obveznike po važećim poreznim propisima. INVOICE_SEF_SUBMIT presedan (\"All tiers — never gated, differentiator\"). MC #105321.

\n\n
\n\n

4. orgType Exemption Model — INTERNAL/PILOT

\n\n

Postojeće organizacije (demo/pilot) zadržavaju pristup svim funkcijama bez obzira na tier ili add-on flagove kroz orgType exemption:

\n\n

Unleash Targeting Strategija

\n\n

Svi add-on flagovi (PAYROLL_MODULE, ACCOUNTING_SIMPLE, ACCOUNTING_FULL_ADDON) imaju sljedeću Unleash strategiju na production env:

\n\n
{\n  \"name\": \"flexibleRollout\",\n  \"constraints\": [\n    {\n      \"contextName\": \"orgType\",\n      \"operator\": \"IN\",\n      \"values\": [\"INTERNAL\", \"PILOT\"]\n    }\n  ],\n  \"parameters\": {\n    \"rollout\": \"100\",\n    \"stickiness\": \"default\"\n  }\n}\n
\n\n

Mehanizam:

\n
    \n
  1. FeatureGate.loadOrgType(organizationId) čita organizations.org_type kolonu (Postgres ENUM: COMMERCIAL/INTERNAL/PILOT)
  2. \n
  3. Fail-closed default: nepoznata org → \"COMMERCIAL\"
  4. \n
  5. INTERNAL/PILOT orgove dobijaju true na svim add-on flagovima (100% rollout within constraint)
  6. \n
  7. COMMERCIAL orgove moraju kupiti add-on eksplicitno (flag evaluira false)
  8. \n
\n\n

Seed Pokrivenost (Migracije)

\n\n
    \n
  • V54: kreirao org_type enum, označio [email protected] org kao INTERNAL
  • \n
  • V91: označio 3 demo orga kao INTERNAL:\n
      \n
    • 00000000-0000-0014-a000-000000000001 (RS demo, Bilko Demo Firma, RSD)
    • \n
    • 00000000-0000-0029-c000-000000000001 (HR demo, ALAI Demo Org HR, EUR)
    • \n
    • 00000000-0000-0029-d000-000000000001 (BA demo, ALAI Demo Org BA, BAM)
    • \n
    \n
  • \n
  • Novi orgovi: default COMMERCIAL (enforcement aktiviran odmah)
  • \n
\n\n
\n\n

5. Unleash Admin Provisioning Runbook

\n\n

Ko Može Pristupiti

\n\n

SAMO: Alem Basic (CEO) + John (AI Director)

\n\n

Gdje je Token

\n\n
    \n
  • Bitwarden item: \"Bilko Unleash Admin — bilko-unleash ACA\"
  • \n
  • Item ID: e917fc41-fddf-49a0-8510-f6f5f8f7faf0
  • \n
  • Token tip: ADMIN (type=admin, project=*, environment=*)
  • \n
  • Pristup:\n
    bw get item e917fc41-fddf-49a0-8510-f6f5f8f7faf0 --session $(cat /tmp/bw-session)\n
    \n
  • \n
\n\n

Unleash Server Info

\n\n
    \n
  • URL: https://bilko-unleash.purplebeach-f004d490.swedencentral.azurecontainerapps.io
  • \n
  • Azure: ACA bilko-unleash, resource group rg-bilko-demo, managed environment purplebeach-f004d490
  • \n
  • Database: unleash baza na bilko-demo-pg shared Postgres instanci
  • \n
  • Image: unleashorg/unleash-server:6
  • \n
\n\n

Kako Dodati Novi Flag

\n\n
# 1. Dohvati admin token iz Bitwardena (NIKAD hardkodiraj)\nexport UNLEASH_ADMIN_TOKEN=$(bw get item e917fc41-fddf-49a0-8510-f6f5f8f7faf0 --session $(cat /tmp/bw-session) | jq -r '.fields[] | select(.name==\"token\") | .value')\n\n# 2. Kreiraj flag (project=default)\ncurl -X POST https://bilko-unleash.purplebeach-f004d490.swedencentral.azurecontainerapps.io/api/admin/projects/default/features \\\n  -H \"Authorization: $UNLEASH_ADMIN_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"name\": \"bilko.addon.novi-modul\",\n    \"type\": \"release\",\n    \"description\": \"Opis novog modula\"\n  }'\n\n# 3. Dodaj strategiju za production env sa orgType constraint\ncurl -X POST https://bilko-unleash.purplebeach-f004d490.swedencentral.azurecontainerapps.io/api/admin/projects/default/features/bilko.addon.novi-modul/environments/production/strategies \\\n  -H \"Authorization: $UNLEASH_ADMIN_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"name\": \"flexibleRollout\",\n    \"constraints\": [\n      {\n        \"contextName\": \"orgType\",\n        \"operator\": \"IN\",\n        \"values\": [\"INTERNAL\", \"PILOT\"]\n      }\n    ],\n    \"parameters\": {\n      \"rollout\": \"100\",\n      \"stickiness\": \"default\",\n      \"groupId\": \"bilko.addon.novi-modul\"\n    }\n  }'\n\n# 4. Aktiviraj flag na production env\ncurl -X POST https://bilko-unleash.purplebeach-f004d490.swedencentral.azurecontainerapps.io/api/admin/projects/default/features/bilko.addon.novi-modul/environments/production/on \\\n  -H \"Authorization: $UNLEASH_ADMIN_TOKEN\"\n\n# 5. Verifikuj\ncurl -X GET https://bilko-unleash.purplebeach-f004d490.swedencentral.azurecontainerapps.io/api/admin/projects/default/features/bilko.addon.novi-modul \\\n  -H \"Authorization: $UNLEASH_ADMIN_TOKEN\"\n
\n\n

Kako Rotirati Token (Security Incident Response)

\n\n
    \n
  1. Generiši novi token kroz Unleash Admin UI (/admin/api) ili API:\n
    curl -X POST .../api/admin/api-tokens -H \"Authorization: $OLD_TOKEN\" \\\n  -d '{\"username\":\"admin\",\"type\":\"admin\",\"environment\":\"*\",\"projects\":[\"*\"]}'\n
    \n
  2. \n
  3. Ažuriraj Bitwarden item e917fc41-fddf-49a0-8510-f6f5f8f7faf0 sa novim tokenom
  4. \n
  5. Testiraj novi token:\n
    curl -H \"Authorization: $NEW_TOKEN\" .../api/admin/projects\n
    \n
  6. \n
  7. Revoke stari token kroz Unleash UI ili:\n
    curl -X DELETE .../api/admin/api-tokens/$OLD_TOKEN_ID -H \"Authorization: $NEW_TOKEN\"\n
    \n
  8. \n
  9. Slack notifikacija:\n
    node ~/system/tools/slack.js send '#security' \"🔐 Unleash admin token rotiran (MC #XXXXX) — novi token u BW e917fc41\"\n
    \n
  10. \n
  11. MC task evidencija sa starim/novim token ID (NE vrijednost!)
  12. \n
  13. Sync rotaciju sa John session-state ako je aktivan
  14. \n
\n\n
\n\n

6. /me/capabilities API Ugovor

\n\n

Endpoint GET /me/capabilities (MC #105291 T5) vraća jedinstveni izvor istine za entitlement — konzumiraju ga i web i mobile klijenti.

\n\n

Request

\n\n
GET /me/capabilities\nAuthorization: Bearer <JWT>\n
\n\n

Response Shape

\n\n
{\n  \"tier\": \"BIZNIS\",                   // pricing-matrix tier (PAUSALAC/BIZNIS/RACUNOVODJA/ENTERPRISE)\n  \"modules\": {\n    \"payroll\": true,                  // BilkoFlag.PAYROLL_MODULE resolved (tier + Unleash)\n    \"accountingFull\": false,          // BilkoFlag.ACCOUNTING_FULL_ADDON\n    \"accountingSimple\": true,         // BilkoFlag.ACCOUNTING_SIMPLE\n    \"apiPublic\": false,               // BilkoFlag.API_PUBLIC\n    \"reportsAdvanced\": false,         // BilkoFlag.REPORTS_ADVANCED\n    \"bankingFeed\": true,              // BilkoFlag.BANKING_FEED_TOK\n    \"multiOrg\": false,                // BilkoFlag.ACCOUNTANT_MULTI_ORG\n    \"aiAssistant\": false              // BilkoFlag.AI_ASSISTANT\n  },\n  \"market\": {\n    \"country\": \"HR\",                  // jurisdiction (iz MarketRoutes.kt)\n    \"currency\": \"EUR\",\n    \"fiscalFeatures\": [\"eRacun\", \"FINA\"]\n  }\n}\n
\n\n

Konzumacija

\n\n
    \n
  • Mobile (T6, MC #105292): poziv na login + periodični refresh, cache lokalno, uslovno renderovanje tabova (payroll tab se NE prikazuje ako modules.payroll == false)
  • \n
  • Web (T6b, MC #105293): migracija sa ad-hoc tier čitanja na ovaj endpoint
  • \n
\n\n
\n\n

7. Reference — MC Taskovi i Evidencija

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
MC TaskOpisStatusEvidence
#105282Parent plan (Full-tier)COMPLETE~/system/evidence/105282-addon-plan/PLAN.md
#105286T8 — Unleash admin tokenCOMPLETE~/system/evidence/105286/, commit 14c87382
#105287T2 — SSOT flag mergeCOMPLETE~/system/evidence/105287/, commit 6f1f0a89, merged 54b42510
#105288T1 — GL + Payroll enforcementCOMPLETE~/system/evidence/105288/, commit fc3d2ab8
#105289T3 — SIMPLE paket definicijaCOMPLETE~/system/evidence/105289/simple-package-definition.md
#105290T4 — Addon flagovi + org_typeCOMPLETE~/system/evidence/105290/, commit 6fce5ec6
#105321T1b — ReportRoutes enforcementCOMPLETE~/system/evidence/105321/, branch feat/report-enforcement-105321
#105294T7 — Pricing + Stripe TEST objektiCOMPLETE~/system/evidence/105294/, commit d9c11f6f
#105291T5 — GET /me/capabilities endpointIN PROGRESSTBD
#105292T6 — Mobile capabilities konzumacijaPENDINGTBD
#105293T6b — Web capabilities migracijaPENDINGTBD
#105295T9 — Proveo validationPENDINGTBD
\n\n

Git Commit Trag

\n\n
    \n
  • 14c87382: docs(infra): Unleash admin + DEPLOY-MAP/feature-flags (T8)
  • \n
  • 6f1f0a89: feat(features): SSOT flag merge — Feature→BilkoFlag addon override (T2), merged → 54b42510
  • \n
  • 6fce5ec6: feat(features): Addon flagovi PAYROLL/ACCOUNTING_SIMPLE/FULL + org_type fail-closed (T4)
  • \n
  • fc3d2ab8: feat(features): GL(9) + Payroll(4) route enforcement retrofit (T1)
  • \n
  • d9c11f6f: feat(pricing): Add-on cijene + Stripe TEST price objekti (T7)
  • \n
\n\n

Lokacije (Repozitorijum)

\n\n
    \n
  • Plan: ~/system/evidence/105282-addon-plan/PLAN.md
  • \n
  • DEPLOY-MAP.md: ~/business/ALAI-Holding-AS/products/Bilko/DEPLOY-MAP.md
  • \n
  • Feature docs: ~/business/ALAI-Holding-AS/products/Bilko/docs/feature-flags.md
  • \n
  • Pricing SSOT: ~/business/ALAI-Holding-AS/products/Bilko/apps/web/lib/pricing-matrix.json
  • \n
  • Kod: apps/api/src/main/kotlin/no/alai/bilko/features/ + apps/api/src/main/kotlin/no/alai/bilko/services/featureflags/
  • \n
\n\n

Pravne Reference

\n\n

Napomena: Pravne reference namjerno bez brojeva članova — verifikacija kod poreskog savjetnika u toku (nalaz porez-check 2026-07-12).

\n\n
\n\n

Dokument kreiran: 2026-07-12 | MC #105297 (T10 Skillforge) | Održava: ALAI Holding AS

\n"