Bilko Add-on Arhitektura
"Bilko Add-on Arhitektura
\n\nMC #105282 | Feature-Flag Modularni Sistem | 2026-07-12
\n\nBilko 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\nBilko koristi dvoslojni gating sistem za kontrolu pristupa funkcionalnostima:
\n\nSloj A — Tier Baseline (in-memory, brzo)
\n- \n
- Lokacija:
apps/api/src/main/kotlin/no/alai/bilko/features/FeatureAccess.kt\n - Komponente:
Featureenum (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
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:
BilkoFlagenum (19 flagova) +UnleashFeatureFlagService\n - Unleash server:
bilko-unleashACA (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
Lanac Odluke — checkFeature()
\n\nSvaki zaštićeni endpoint poziva ApplicationCall.checkFeature(feature, principal) koje implementira sljedeći lanac:
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\nMermaid Dijagram
\n\nflowchart 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| Add-on Modul | BilkoFlag | Cijena (EUR/mj) | Status | Enforcement |
|---|---|---|---|---|
| Plate / Payroll | PAYROLL_MODULE(bilko.addon.payroll) | €15 | CONFIRMED | PayrollRoutes.kt (4 grupe ruta) Feature.PAYROLL_MODULE_ACCESS MC #105288, commit fc3d2ab8 |
| Računovodstvo SIMPLE | ACCOUNTING_SIMPLE(bilko.addon.accounting-simple) | €5 | CONFIRMED | ReportRoutes.kt REPORT_PDF_EXPORT + REPORT_EXCEL_EXPORT MC #105321, commit (u letu) |
| Računovodstvo FULL (GL) | ACCOUNTING_FULL_ADDON(bilko.addon.accounting-full) | €25 | CONFIRMED | 9 GL_* ruta: AccountingRoutes, ApRoutes, GlBankRoutes, JournalEntriesRoutes, ReportingRoutes MC #105288, commit fc3d2ab8 |
| API pristup | API_PUBLIC(bilko.api.public) | €29 | PLACEHOLDER | BilkoFlag postoji (T2), route enforcement TBD |
| Napredni izvještaji | REPORTS_ADVANCED(bilko.reports.advanced) | €9 | PLACEHOLDER | BilkoFlag postoji, route TBD |
| Auto-sync banke (Tok) | BANKING_FEED_TOK(bilko.banking.feed-tok) | €12 | PLACEHOLDER | BilkoFlag postoji, route TBD |
| Multi-org računovođe | ACCOUNTANT_MULTI_ORG(bilko.accountant.multi-org) | €39 | PLACEHOLDER | BilkoFlag postoji, route TBD |
| AI asistent | AI_ASSISTANT(bilko.ai.assistant) | €9 | PLACEHOLDER | BilkoFlag postoji, route TBD |
Napomena: CONFIRMED status = route enforcement backing postoji (mogu se naplaćivati). PLACEHOLDER = prijedlog cijena, ne mogu se naplatiti dok route enforcement ne postoji.
\n\nPricing benchmark: Fiken payroll €10-13, Tripletex €5.80/user, mi €15 FLAT MVP.
\n\n\n\n
3. Flag → Ruta Mapping
\n\nGL (Računovodstvo FULL) — 9/9 Features
\n\n| Feature Enum | Rute (ukupno 30 call-sites) | Fajl |
|---|---|---|
GL_CHART_OF_ACCOUNTS | GET /accounting/accountsPUT /accounting/account-mapping | AccountingRoutes.kt |
GL_JOURNAL_ENTRIES | GET/POST /accounting/journalGET/POST /accounting/entries*GET/POST /journal-entries (alias) | AccountingRoutes.kt JournalEntriesRoutes.kt |
GL_TRIAL_BALANCE | GET /accounting/trial-balance | AccountingRoutes.kt |
GL_AP_LEDGER | GET/POST /accounting/purchase-invoices*POST /supplier-paymentsGET /ura | ApRoutes.kt |
GL_VAT_FORM | GET /accounting/pdv-obrazac*GET /pdv-sGET /zp | ReportingRoutes.kt |
GL_BALANCE_SHEET | GET /accounting/salda-konti | ReportingRoutes.kt |
GL_ANALYTICS | GET /accounting/account-card/{code} | ReportingRoutes.kt |
GL_OPENING_BALANCE | POST /accounting/opening-balance | ApRoutes.kt |
GL_BANK_IMPORT | GET/POST /accounting/bank-importGET/POST /bank-transactions* | GlBankRoutes.kt |
Payroll (Plate) — 4 Grupe Ruta
\n\n| Feature Enum | Rute (7 call-sites) | Fajl |
|---|---|---|
PAYROLL_MODULE_ACCESS | GET/POST/PUT/DELETE /payroll/employees*POST /payroll/calculateGET/POST /payroll/payslips | PayrollRoutes.kt |
Reports (Izvještaji) — 19 Gated / 9 Exempt
\n\n| Feature Enum | Rute (gated) | Fajl |
|---|---|---|
REPORT_VIEW | GET /reports/ (root)/dashboard, /profit-loss, /balance-sheet, /cash-flow, /vat, /trial-balance, /general-ledger | ReportRoutes.kt |
REPORT_PDF_EXPORT | GET /reports/*/export/pdf (vat, profit-loss, balance-sheet, trial-balance, cash-flow) | ReportRoutes.kt |
REPORT_EXCEL_EXPORT | GET /reports/*/export/xlsxGET /reports/accountant-export | ReportRoutes.kt |
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
Pravno obrazloženje: Porezni registri su zakonska obaveza obveznika (čl. 79 ZPDV HR, čl. 56 Zakon o porezu na dohodak građana RS) — INVOICE_SEF_SUBMIT presedan (\"All tiers — never gated, differentiator\"). MC #105321.
\n\n\n\n
4. orgType Exemption Model — INTERNAL/PILOT
\n\nPostojeće organizacije (demo/pilot) zadržavaju pristup svim funkcijama bez obzira na tier ili add-on flagove kroz orgType exemption:
\n\nUnleash Targeting Strategija
\n\nSvi add-on flagovi (PAYROLL_MODULE, ACCOUNTING_SIMPLE, ACCOUNTING_FULL_ADDON) imaju sljedeću Unleash strategiju na production env:
{\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\nMehanizam:
\n- \n
FeatureGate.loadOrgType(organizationId)čitaorganizations.org_typekolonu (Postgres ENUM: COMMERCIAL/INTERNAL/PILOT)\n- Fail-closed default: nepoznata org →
\"COMMERCIAL\"\n - INTERNAL/PILOT orgove dobijaju
truena svim add-on flagovima (100% rollout within constraint)\n - COMMERCIAL orgove moraju kupiti add-on eksplicitno (flag evaluira
false)\n
Seed Pokrivenost (Migracije)
\n\n- \n
- V54: kreirao
org_typeenum, 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)\n00000000-0000-0029-c000-000000000001(HR demo, ALAI Demo Org HR, EUR)\n00000000-0000-0029-d000-000000000001(BA demo, ALAI Demo Org BA, BAM)\n
- Novi orgovi: default
COMMERCIAL(enforcement aktiviran odmah)\n
\n\n
5. Unleash Admin Provisioning Runbook
\n\nKo Može Pristupiti
\n\nSAMO: Alem Basic (CEO) + John (AI Director)
\n\nGdje 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
\n\nbw get item e917fc41-fddf-49a0-8510-f6f5f8f7faf0 --session $(cat /tmp/bw-session)\n
Unleash Server Info
\n\n- \n
- URL:
https://bilko-unleash.purplebeach-f004d490.swedencentral.azurecontainerapps.io\n - Azure: ACA
bilko-unleash, resource grouprg-bilko-demo, managed environmentpurplebeach-f004d490\n - Database:
unleashbaza nabilko-demo-pgshared Postgres instanci\n - Image:
unleashorg/unleash-server:6\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\nKako Rotirati Token (Security Incident Response)
\n\n- \n
- Generiši novi token kroz Unleash Admin UI (
/admin/api) ili API:\n
\n\ncurl -X POST .../api/admin/api-tokens -H \"Authorization: $OLD_TOKEN\" \\\n -d '{\"username\":\"admin\",\"type\":\"admin\",\"environment\":\"*\",\"projects\":[\"*\"]}'\n - Ažuriraj Bitwarden item
e917fc41-fddf-49a0-8510-f6f5f8f7faf0sa novim tokenom\n - Testiraj novi token:\n
\n\ncurl -H \"Authorization: $NEW_TOKEN\" .../api/admin/projects\n - Revoke stari token kroz Unleash UI ili:\n
\n\ncurl -X DELETE .../api/admin/api-tokens/$OLD_TOKEN_ID -H \"Authorization: $NEW_TOKEN\"\n - Slack notifikacija:\n
\n\nnode ~/system/tools/slack.js send '#security' \"🔐 Unleash admin token rotiran (MC #XXXXX) — novi token u BW e917fc41\"\n - MC task evidencija sa starim/novim token ID (NE vrijednost!)\n
- Sync rotaciju sa John session-state ako je aktivan\n
\n\n
6. /me/capabilities API Ugovor
\n\nEndpoint GET /me/capabilities (MC #105291 T5) vraća jedinstveni izvor istine za entitlement — konzumiraju ga i web i mobile klijenti.
Request
\n\nGET /me/capabilities\nAuthorization: Bearer <JWT>\n\n\nResponse 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\nKonzumacija
\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
7. Reference — MC Taskovi i Evidencija
\n\n| MC Task | Opis | Status | Evidence |
|---|---|---|---|
| #105282 | Parent plan (Full-tier) | COMPLETE | ~/system/evidence/105282-addon-plan/PLAN.md |
| #105286 | T8 — Unleash admin token | COMPLETE | ~/system/evidence/105286/, commit 14c87382 |
| #105287 | T2 — SSOT flag merge | COMPLETE | ~/system/evidence/105287/, commit 6f1f0a89, merged 54b42510 |
| #105288 | T1 — GL + Payroll enforcement | COMPLETE | ~/system/evidence/105288/, commit fc3d2ab8 |
| #105289 | T3 — SIMPLE paket definicija | COMPLETE | ~/system/evidence/105289/simple-package-definition.md |
| #105290 | T4 — Addon flagovi + org_type | COMPLETE | ~/system/evidence/105290/, commit 6fce5ec6 |
| #105321 | T1b — ReportRoutes enforcement | COMPLETE | ~/system/evidence/105321/, branch feat/report-enforcement-105321 |
| #105294 | T7 — Pricing + Stripe TEST objekti | COMPLETE | ~/system/evidence/105294/, commit d9c11f6f |
| #105291 | T5 — GET /me/capabilities endpoint | IN PROGRESS | TBD |
| #105292 | T6 — Mobile capabilities konzumacija | PENDING | TBD |
| #105293 | T6b — Web capabilities migracija | PENDING | TBD |
| #105295 | T9 — Proveo validation | PENDING | TBD |
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
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
Dokument kreiran: 2026-07-12 | MC #105297 (T10 Skillforge) | Održava: ALAI Holding AS
\n"