Sprint 1-3 + Arch Roadmap

Sprint 1-3 + Arch Roadmap

Sprint program context: Post-CEO Bilko triage 2026-05-02
Phase: Bug-fix sprint following UAT Phase 1 findings (MC #10487)


Sprint 1 (MC #10495) — P0 Legal + Email + Dead UI

Status: OPEN (H priority)
Owner: TBD (awaiting dispatch)

Scope

Three high-severity bugs blocking go-live:

1. SEF Stub → Real e-Invoicing Integration

Current state: SefService.kt issues stub sefId (SEF-STUB-<id>) and marks invoices as "submitted" without ever contacting efaktura.gov.rs.

Code location: apps/api/src/main/kotlin/no/alai/bilko/services/SefService.kt:180-200

Round 2 comment in code:

// Round 2 replaces stub with real Ktor CIO HTTP call to efaktura.gov.rs
// Retry queue already persisted — just wire up actual SEF submission

Decision pending CEO:

Blocker: Without CEO decision, cannot proceed. Integration effort = 3-5 days (XML schema, auth flow, retry handling).


2. Invoice Email Delivery

Current state: Invoice wizard Step 6 shows email compose UI (To, Subject, Message, "Send me a copy"). InvoiceService.sendInvoice() changes invoice status to "sent" but does NOT dispatch email.

Impact: Customer never receives invoice. User believes invoice was sent. Creates chargeback disputes when client says "nisam dobio nista."

Code gap: No email service injection in DI container (DI.kt). No SMTP/Resend/SendGrid integration exists in Kotlin backend.

Evidence: apps/api/src/main/kotlin/no/alai/bilko/services/InvoiceService.ktsendInvoice() function has no email dispatch logic.

Acceptance criteria:


3. Dead UI Buttons — Receipt Scan + Attach

What: Two buttons on expense form (/expenses/new) have zero functionality:

Impact: Mobile expense entry — a marketed selling point — is non-functional. RS/BA tax law requires receipt documentation for deductible expenses; without attachment, Bilko cannot support compliance.

UX deception: "Skeniraj racun" button is 120px tall, full-width, purple, most dominant UI element on form. Tapping it does nothing. On iPhone Safari with VoiceOver, announces "Skeniraj racun, button" but zero feedback on activation.

Acceptance criteria:


Sprint 2 (MC #10496) — P1 Core Workflow

Status: OPEN (M priority)
Owner: TBD

Scope

Four P1 gaps affecting core accounting workflows:

1. Chart of Accounts Seeding on Org Creation

Gap: CountryService.seedChartOfAccounts() exists (CountryService.kt:220) but is NOT called from AuthService.register().

Impact: New organizations have empty chart of accounts. US-030 AC1 requires "all 10 account classes present on new Serbian org" — currently fails.

Fix: Add countryService.seedChartOfAccounts(organizationId, country) call after org creation in AuthService.register().


2. Multi-Org Switcher

Gap: Each user has single organizationId. No UI switcher, no API endpoint to switch active org.

Impact: Accountants managing multiple companies (common in SMB Balkans) must log out/in with different emails. US-004 unmet.

Acceptance criteria:


3. EUR Exchange Rate Fetch from Central Bank

Gap: InvoiceService.getExchangeRate() exists (InvoiceService.kt:972-990) but has TODO comment: "fetch from NBS API for RS, CBBiH for BA."

Current behavior: Returns hardcoded 1.0 or throws error if currency not BAM/RSD.

Impact: Foreign currency invoices (EUR, USD) cannot be created. US-070 partially unmet.

Acceptance criteria:


4. Bank CSV Parser — Balkan Format Presets

Gap: CSV import expects generic date,description,amount,reference format. Real banks: Raiffeisen BA (semicolon, DD.MM.YYYY), UniCredit RS (local thousand separators), Intesa RS (multi-line headers).

Impact: User exports from real bank, uploads CSV, gets 0 imported rows. Feature reads as broken.

Acceptance criteria:


Sprint 3 (MC #10497) — P2 Polish

Status: OPEN (L priority)
Owner: TBD

Scope

Three P2 UX polish items:

1. sr-Latn Translation Pass (Dialect Consistency)

Gap: apps/web/messages/sr-Latn.json mixes Bosnian and Serbian forms: "dospijeva" (BS) alongside "dospeva" (RS/SR), "mjesec" vs "mesec", "Postavke" vs "Podešavanja."

Decision: sr-Latn targets RS market (pure Serbian ekavica). Bosnian forms belong in bs.json.

Acceptance criteria:


2. Mobile UX Micro-Fixes

Gaps (from maria-santos report):

Acceptance criteria: Address top 3 (step labels, line item width, sidebar animation).


3. Dashboard Empty States + Error Message Clarity

Gap: Dashboard shows Recharts widgets with no data → renders blank/broken-looking on first login. No empty state illustrations/guidance.

Acceptance criteria:


Arch Roadmap (MC #10498) — Infrastructure + CI

Status: OPEN (M priority)
Owner: TBD

Scope

Four architectural/infra improvements:

1. Kotlin CI Pipeline

Gap: cloudbuild.yaml deploys web only. Kotlin API has no CI (manual deploy only).

Risk: Regression in apps/api/ invisible to CI. If developer pushes breaking change to main, web gets deployed, API does not. Two can silently diverge.

Acceptance criteria:


2. RUNBOOK.md Fix (Still References Vercel)

Gap: docs/operations/OPERATIONAL-RUNBOOK.md:23,59,66,120 references Vercel (vercel --prod, vercel rollback, vercel env add) as deployment platform.

Reality: Deployment is GCP Cloud Run + Cloud Build. Vercel not used.

Acceptance criteria:


3. .gcloudignore Optimization (MC #10504)

Gap: Cloud Build web deploy uploads 492MB (includes apps/api/build/, .gradle/, node_modules/ from all workspaces).

Impact: Upload timeout on slow connections (3+ minutes on Build step 1/24).

Acceptance criteria:


4. Cloud Build Trigger Registration (Auto-Deploy on Push)

Gap: Cloud Build triggers are manual (gcloud builds submit) per deploy. No auto-trigger on git push origin main.

Acceptance criteria:


Dependency Diagram

graph TB
    UAT[UAT Phase 1 #10487] --> Express[Express Deletion #10493]
    Express --> Sprint0[Sprint 0 #10494]
    Sprint0 --> WebDockerfix[Web Dockerfile #10505]
    WebDockerfix --> Sprint1[Sprint 1 #10495]
    Sprint1 --> Sprint2[Sprint 2 #10496]
    Sprint1 --> Sprint3[Sprint 3 #10497]
    Sprint0 --> Arch[Arch #10498]
    Sprint1 --> AngieRerun[Angie Re-run #10500]
    Sprint0 --> ProdCutover[PROD CUTOVER #10502]
    Arch --> gcloudignore[.gcloudignore #10504]
    
    style UAT fill:#e1f5ff
    style Express fill:#c8e6c9
    style Sprint0 fill:#c8e6c9
    style WebDockerfix fill:#c8e6c9
    style Sprint1 fill:#fff9c4
    style Sprint2 fill:#fff9c4
    style Sprint3 fill:#fff9c4
    style Arch fill:#fff9c4
    style ProdCutover fill:#ffccbc
    style AngieRerun fill:#f3e5f5
    style gcloudignore fill:#f3e5f5

Legend:


References

MCs:

Evidence:


Revision #2
Created 2026-05-02 12:31:01 UTC by John
Updated 2026-06-07 20:01:00 UTC by John