# Functional Requirements Specification

# Functional Requirements Specification (FRS): Bilko

> **Project:** Bilko — Balkan Accounting SaaS
> **Version:** 1.0
> **Date:** 2026-02-25
> **Author:** John (AI Director)
> **Status:** Final
> **Reviewers:** Alem Bašić (CEO)

## Document History

| Version | Date       | Author             | Changes                            |
| ------- | ---------- | ------------------ | ---------------------------------- |
| 0.1     | 2026-02-23 | John (AI Director) | Initial draft — Phase 1 Serbia MVP |
| 1.0     | 2026-02-25 | John (AI Director) | Finalized for v1.0 release         |

---

## 1. System Overview

**System Name:** Bilko — Balkan Accounting SaaS
**System Purpose:** Bilko is a cloud-based accounting platform for Balkan SMBs that handles e-invoicing (Serbian SEF), expense tracking, double-entry bookkeeping, bank reconciliation, VAT/PDV management, and financial reporting. It provides a simple, compliant, affordable alternative to legacy ERP systems, accessible from any browser.

**System Context Diagram:**

```mermaid
graph TB
    subgraph "Bilko Platform"
        UI[Web App\nNext.js 15 + React 19]
        API[Backend API\nExpress + TypeScript]
        DB[(PostgreSQL\nPrisma ORM)]
    end
    U1[SMB Owner] -->|Uses| UI
    U2[Accountant] -->|Uses| UI
    U3[Viewer / Employee] -->|Read-only| UI
    API -->|Reads/Writes| DB
    API -->|Submits e-invoices| SEF[SEF Platform\nefaktura.gov.rs]
    API -->|Sends invoice PDFs| EMAIL[Email Provider\nTransactional]
    API -->|Fetches rates| FX[Exchange Rate API\nECB / fixer.io]
    ADM[Admin\nOrg Owner] -->|Manages org + users| UI
```

---

## 2. Actors & Personas

| Actor ID | Actor Name         | Type   | Description                                                | Access Level                                             |
| -------- | ------------------ | ------ | ---------------------------------------------------------- | -------------------------------------------------------- |
| ACT-01   | Organization Owner | Human  | Business owner who created the Bilko account; full control | Owner role — all permissions                             |
| ACT-02   | Admin              | Human  | Designated admin (accountant or office manager)            | Admin role — all except billing                          |
| ACT-03   | Accountant         | Human  | External bookkeeper managing the organization's books      | Accountant role — financial data, reports; cannot delete |
| ACT-04   | Viewer             | Human  | Employee or partner with read-only access                  | Viewer role — read only                                  |
| ACT-05   | SEF Platform       | System | Serbian government e-invoice platform                      | External API                                             |
| ACT-06   | Email Provider     | System | Transactional email for invoice delivery                   | External API                                             |
| ACT-07   | Exchange Rate API  | System | Currency rate provider (ECB / fixer.io)                    | External API                                             |

### Persona Detail

**Persona: Marko Petrović — SMB Owner**

- **Role:** Owner of a 8-person IT consulting firm in Belgrade
- **Goal:** Create and submit SEF invoices fast; see real-time P&L; file PDV without an accountant
- **Pain Points:** SEF portal is slow; Pantheon is complex; doesn't understand bookkeeping jargon
- **Tech Savviness:** Medium — uses Google Suite, smartphones, but not accounting software
- **Frequency of Use:** Daily (invoices) + monthly (PDV report)

**Persona: Ana Nikolić — Accountant**

- **Role:** Independent bookkeeper managing 12 SMB clients
- **Goal:** Access all client organizations from one login; export VAT reports in required formats
- **Pain Points:** Each client uses different software; reconciliation takes too long each month
- **Tech Savviness:** High — experienced with accounting software, Excel, CSV workflows
- **Frequency of Use:** Daily across multiple client orgs

---

## 3. Functional Requirements

### 3.1 Module: Authentication & User Management

#### Module Overview

Handles user registration, login, session management, organization creation, and role-based access control (RBAC). Multi-tenant: one user can belong to multiple organizations.

---

**FR-001: User Registration**

> ⚠️ **SUPERSEDED (2026-06-25).** Email/password registration is RETIRED. Auth is **Microsoft Entra External ID (CIAM) SSO only** — the email/password endpoints return HTTP 410 (`AuthRoutes.kt` ~242/254). New users are provisioned via Entra JIT on first SSO login, or via the FR-003 tokenized invite flow (`/accept-invite` → Entra SSO). The criteria below (password complexity, verification email) DO NOT apply. Retained for historical trace only.

| Attribute    | Value                 |
| ------------ | --------------------- |
| Module       | Authentication        |
| Priority     | Must Have             |
| Trace        | BR-014                |
| UI Reference | `/auth/register` page |

**Description:**
A new user registers with email and password. On successful registration, a verification email is sent and a default organization is created for the user with Owner role.

**Acceptance Criteria:**

- [ ] **Given** a valid, unregistered email and strong password (≥8 chars, 1 uppercase, 1 number), **when** user submits registration form, **then** account is created, verification email sent, user redirected to email verification page
- [ ] **Given** an already-registered email, **when** user submits, **then** error "An account with this email already exists" shown; no account created
- [ ] **Given** password does not meet complexity rules, **when** user submits, **then** inline validation error shown before submission

**Data Requirements:**

- Input: email (unique), password, full name, organization name
- Output: user record, organization record, Owner role assignment, verification token
- Validation: email format, password complexity, uniqueness check

**Business Rules:** RUL-08 (org scoping from first login)

---

**FR-002: User Login**

> ⚠️ **SUPERSEDED (2026-06-25).** Email/password login is RETIRED (HTTP 410). Login is **Microsoft Entra External ID (CIAM) SSO only** — `/login` triggers `signInWithMicrosoft()` → `*.ciamlogin.com`; backend issues the session from the Entra id_token (`AuthService.createSessionFromEntraIdToken`). The email/password criteria below DO NOT apply. Retained for historical trace only.

| Attribute    | Value              |
| ------------ | ------------------ |
| Module       | Authentication     |
| Priority     | Must Have          |
| Trace        | BR-014             |
| UI Reference | `/auth/login` page |

**Description:**
Authenticated users log in with email and password. Returns JWT access token (15-min TTL) + refresh token (30-day TTL).

**Acceptance Criteria:**

- [ ] **Given** valid credentials, **when** user submits login, **then** JWT issued, user redirected to dashboard
- [ ] **Given** invalid credentials, **when** user submits, **then** generic error "Invalid email or password" (no user enumeration)
- [ ] **Given** 5 consecutive failed attempts, **when** user tries again, **then** account locked for 15 minutes
- [ ] **Given** idle session > 30 minutes, **when** user attempts action, **then** redirected to login

---

**FR-003: Invite User to Organization**

| Attribute    | Value                 |
| ------------ | --------------------- |
| Module       | Authentication        |
| Priority     | Must Have             |
| Trace        | BR-007                |
| UI Reference | `/settings/team` page |

**Description:**
Organization Owner or Admin can invite users by email with a specific role (Admin, Accountant, Viewer). Invited user receives email with accept link; on acceptance, they are added to the organization with the assigned role.

**Acceptance Criteria:**

- [ ] **Given** an Owner invites user@email.com as Accountant, **when** invite is sent, **then** invitation email received within 2 minutes with unique accept link
- [ ] **Given** invited user accepts link within 48 hours, **when** they register or log in, **then** added to organization with Accountant role
- [ ] **Given** invite link expires (48h), **when** user clicks link, **then** error shown with option to request new invite

---

### 3.2 Module: Invoicing

#### Module Overview

Core invoicing functionality. Create, edit, preview, send, and track invoices. For Serbia: automatic SEF submission on send. Multi-currency. PDF generation with client/Bilko branding.

---

**FR-010: Create Invoice**

| Attribute    | Value                              |
| ------------ | ---------------------------------- |
| Module       | Invoicing                          |
| Priority     | Must Have                          |
| Trace        | BR-001, BR-002, BR-004, BR-008     |
| UI Reference | `/invoices/create` — 6-step wizard |

**Description:**
User creates a new invoice by selecting a client (Contact), adding line items with quantities and unit prices, selecting currency and PDV rate. System auto-calculates PDV and totals. Preview shows PDF representation before sending.

**Acceptance Criteria:**

- [ ] **Given** an authenticated user, **when** they complete the 6-step invoice wizard with valid data, **then** invoice is created in Draft status with correct PDV calculation
- [ ] **Given** Serbian PDV rate of 20%, **when** line item total is 1000 RSD, **then** PDV = 200 RSD, total = 1200 RSD
- [ ] **Given** multi-currency invoice in EUR, **when** created, **then** exchange rate at transaction date is locked and stored
- [ ] **Given** invoice is in Draft, **when** user sends it, **then** status changes to Sent, PDF emailed to client, SEF submission initiated (Serbia)

**Data Requirements:**

- Input: client (Contact), invoice_date, due_date, currency, line_items (description, qty, unit_price, vat_rate), notes
- Output: Invoice record with auto-generated invoice_number, PDV amounts, total_amount in NUMERIC(19,4)
- Validation: due_date > invoice_date; at least one line item; contact required

**Business Rules:** RUL-01 (NUMERIC), RUL-02 (double-entry on payment), RUL-05 (sequential numbering), RUL-06 (PDV rate selection)

---

**FR-011: SEF E-Invoice Submission (Serbia)**

| Attribute    | Value                                                     |
| ------------ | --------------------------------------------------------- |
| Module       | Invoicing                                                 |
| Priority     | Must Have                                                 |
| Trace        | BR-001                                                    |
| UI Reference | Automatic on invoice send; status shown in invoice detail |

**Description:**
When a user sends an invoice for a Serbian B2B transaction, Bilko automatically submits the invoice to SEF (efaktura.gov.rs) in UBL 2.1 XML format. SEF status (sent, accepted, rejected) is tracked and displayed.

**Acceptance Criteria:**

- [ ] **Given** a Serbian B2B invoice is sent, **when** the send action is triggered, **then** UBL 2.1 XML is generated and submitted to SEF API within 30 seconds
- [ ] **Given** SEF accepts the invoice, **when** success response received, **then** invoice SEF status updated to "Accepted" and SEF invoice ID stored
- [ ] **Given** SEF rejects the invoice, **when** rejection response received, **then** user is notified with the rejection reason from SEF; invoice stays in Sent status pending correction
- [ ] **Given** SEF API is unavailable, **when** submission attempted, **then** invoice queued for retry; user notified; max 3 retries with exponential backoff

**Data Requirements:**

- Input: Invoice record + organization SEF credentials
- Output: SEF invoice ID, submission timestamp, SEF status
- Validation: Required SEF fields present (buyer tax ID, invoice type, issue date)

---

**FR-012: Track Invoice Status**

| Attribute | Value          |
| --------- | -------------- |
| Module    | Invoicing      |
| Priority  | Must Have      |
| Trace     | BR-001, BR-008 |

**Description:**
Invoices progress through status states: Draft → Sent → SEF Accepted → Paid / Overdue. Users can mark invoices as paid, add payment date and amount.

**Acceptance Criteria:**

- [ ] **Given** invoice is Sent, **when** user marks as paid with payment date and amount, **then** status changes to Paid and payment transaction auto-created
- [ ] **Given** invoice is Sent and due_date has passed, **when** system checks daily, **then** status automatically changes to Overdue
- [ ] **Given** a list of invoices, **when** user filters by status, **then** only matching invoices shown

---

### 3.3 Module: Expense Tracking

#### Module Overview

Record business expenses with categorization, multi-currency support, and receipt attachment. Feeds double-entry bookkeeping automatically.

---

**FR-020: Create Expense**

| Attribute    | Value              |
| ------------ | ------------------ |
| Module       | Expenses           |
| Priority     | Must Have          |
| Trace        | BR-009             |
| UI Reference | `/expenses/create` |

**Description:**
User records a business expense with vendor, amount, currency, category, payment method, and optional receipt photo. System auto-creates the double-entry transaction.

**Acceptance Criteria:**

- [ ] **Given** valid expense data, **when** user submits, **then** expense saved with debit to expense account + credit to payment account
- [ ] **Given** a foreign currency expense, **when** created, **then** exchange rate at expense date locked and stored; amount also shown in base currency
- [ ] **Given** receipt image uploaded (JPG/PNG/PDF, max 10MB), **when** expense saved, **then** receipt stored and accessible from expense record

---

### 3.4 Module: Double-Entry Bookkeeping

#### Module Overview

The accounting engine. Every financial event generates balanced debit + credit Transaction entries. Supports Chart of Accounts per Balkan GAAP (Serbian Kontni Okvir 2021).

---

**FR-030: Chart of Accounts**

| Attribute    | Value                |
| ------------ | -------------------- |
| Module       | Bookkeeping          |
| Priority     | Must Have            |
| Trace        | BR-003, BR-010       |
| UI Reference | `/settings/accounts` |

**Description:**
Every organization is seeded with the Serbian Kontni Okvir (Chart of Accounts) per Pravilnik 2021 on creation. Accountants can add custom sub-accounts. Pre-populated accounts cover all 10 account classes (0-9).

**Acceptance Criteria:**

- [ ] **Given** a new Serbian organization is created, **when** setup completes, **then** all standard Kontni Okvir accounts are pre-populated (Classes 0-9)
- [ ] **Given** an accountant adds a custom sub-account under 411, **when** saved, **then** account 411xxx appears in Chart of Accounts and is available in transaction entry
- [ ] **Given** an account has transactions, **when** user attempts to delete it, **then** deletion blocked with explanation

---

**FR-031: Double-Entry Transaction Recording**

| Attribute    | Value                                              |
| ------------ | -------------------------------------------------- |
| Module       | Bookkeeping                                        |
| Priority     | Must Have                                          |
| Trace        | BR-003                                             |
| UI Reference | Auto-generated; viewable in `/bookkeeping/journal` |

**Description:**
Every financial event (invoice paid, expense recorded, bank reconciliation) automatically generates a balanced Transaction record with debitAccountId, creditAccountId, and amount in NUMERIC(19,4).

**Acceptance Criteria:**

- [ ] **Given** an invoice of 1200 RSD (1000 + 200 PDV) is marked paid, **when** payment recorded, **then** Transaction created: Debit 1200 (bank account), Credit 1000 (revenue), Credit 200 (PDV payable)
- [ ] **Given** any transaction is created, **when** saved, **then** sum of all debits = sum of all credits (ACID enforcement)
- [ ] **Given** a transaction is created, **when** user attempts to delete it, **then** soft-delete only — LoggedAction records the deletion; original data preserved

**Business Rules:** RUL-01, RUL-02, RUL-03, RUL-04

---

### 3.5 Module: Bank Reconciliation

#### Module Overview

Import bank statements via CSV upload. Auto-match transactions. Manual reconciliation for unmatched items.

---

**FR-040: Bank Statement CSV Import**

| Attribute    | Value           |
| ------------ | --------------- |
| Module       | Banking         |
| Priority     | Must Have       |
| Trace        | BR-005          |
| UI Reference | `/banking` page |

**Description:**
Users upload bank statement CSV files (Serbian bank format). System parses transactions and attempts to auto-match with existing invoices/expenses by amount and date proximity.

**Acceptance Criteria:**

- [ ] **Given** a valid CSV in supported Serbian bank format, **when** uploaded, **then** all transactions parsed and displayed for review
- [ ] **Given** parsed transaction matches an open invoice by amount ± 5%, **when** suggested, **then** match highlighted for user confirmation
- [ ] **Given** unmatched transaction, **when** user manually matches or categorizes, **then** Transaction and double-entry entry created

---

### 3.6 Module: VAT / PDV Management

#### Module Overview

Auto-calculate, track, and generate PDV reports for monthly filing with Poreska Uprava (Serbia).

---

**FR-050: PDV Report Generation**

| Attribute    | Value          |
| ------------ | -------------- |
| Module       | VAT/PDV        |
| Priority     | Must Have      |
| Trace        | BR-002, BR-006 |
| UI Reference | `/reports/vat` |

**Description:**
System aggregates all VAT-applicable transactions for a period and generates the PDV report in the format required by Poreska Uprava. Export in PDF and XML/JSON formats.

**Acceptance Criteria:**

- [ ] **Given** a reporting period (month), **when** user generates PDV report, **then** all sales PDV and input PDV correctly summed; net PDV payable/refundable calculated
- [ ] **Given** PDV report generated, **when** user exports, **then** PDF and XML export available; XML format compatible with ePorezi portal
- [ ] **Given** zero PDV period, **when** report generated, **then** zero report generated correctly (still required by law)

---

### 3.7 Module: Financial Reports

#### Module Overview

P&L Statement, Balance Sheet, Cash Flow Statement — generated from double-entry transaction data. PDF and Excel export.

---

**FR-060: Profit & Loss Statement**

| Attribute    | Value          |
| ------------ | -------------- |
| Module       | Reports        |
| Priority     | Must Have      |
| Trace        | BR-006         |
| UI Reference | `/reports` hub |

**Acceptance Criteria:**

- [ ] **Given** a date range, **when** P&L generated, **then** all revenue and expense accounts summarized; net profit/loss calculated and matches double-entry totals
- [ ] **Given** multi-currency org, **when** P&L generated, **then** all amounts shown in base currency using locked exchange rates

---

**FR-061: Balance Sheet**

| Attribute | Value     |
| --------- | --------- |
| Module    | Reports   |
| Priority  | Must Have |
| Trace     | BR-006    |

**Acceptance Criteria:**

- [ ] **Given** any date, **when** Balance Sheet generated, **then** Assets = Liabilities + Equity (double-entry balance enforced)
- [ ] **Given** Balance Sheet is unbalanced (should be impossible), **when** detected, **then** alert raised immediately for investigation

---

### 3.8 Module: Multi-Currency

#### Module Overview

Support BAM, RSD, EUR, USD. Exchange rates fetched daily from ECB / fixer.io. Rates locked at transaction date per IFRS requirements.

---

**FR-070: Exchange Rate Management**

| Attribute | Value          |
| --------- | -------------- |
| Module    | Multi-Currency |
| Priority  | Must Have      |
| Trace     | BR-004         |

**Acceptance Criteria:**

- [ ] **Given** a transaction in non-base currency, **when** created, **then** exchange rate at transaction date is fetched, stored, and locked — cannot be edited later
- [ ] **Given** ECB rate API unavailable, **when** transaction attempted, **then** system uses cached rate (max 24h old) or prompts user for manual entry
- [ ] **Given** organization base currency is RSD, **when** EUR invoice created, **then** both EUR amount and RSD equivalent stored; reports show RSD

---

## 4. Use Case Diagrams

### 4.1 Core Workflows

```mermaid
graph LR
    Owner((Owner)) --> UC1[FR-001: Register]
    Owner --> UC2[FR-010: Create Invoice]
    Owner --> UC3[FR-011: Submit to SEF]
    Owner --> UC4[FR-050: Generate PDV Report]
    Owner --> UC5[FR-060: View P&L]
    Accountant((Accountant)) --> UC2
    Accountant --> UC6[FR-030: Manage Chart of Accounts]
    Accountant --> UC7[FR-031: Record Manual Transaction]
    Accountant --> UC4
    Accountant --> UC8[FR-040: Import Bank CSV]
    Viewer((Viewer)) --> UC9[View Reports — Read Only]
    SEF((SEF API)) --> UC3
```

---

## 5. System Behavior Specifications

### 5.1 Error Handling

- All user-facing errors display human-readable Serbian/English message (no stack traces)
- All errors logged with correlation ID, timestamp, user ID (if authenticated), and action
- Validation errors shown inline, adjacent to the invalid field
- SEF API errors: show SEF's rejection reason in user-readable format + link to fix

### 5.2 Data Persistence

- All financial data persisted within 500ms of user action
- Optimistic UI updates rolled back if server confirmation fails
- All mutations (create/update/delete) audited in LoggedAction with user ID and timestamp

### 5.3 Session & State

- Session timeout: 30 minutes of inactivity (JWT expiry)
- Refresh token: 30-day rolling TTL
- Concurrent sessions: allowed (mobile + desktop)
- Browser refresh: state restored from server (no stale data)

### 5.4 Notifications

- Email notifications: invoice sent, invoice paid, SEF acceptance/rejection, account invitation
- In-app notifications: overdue invoices, PDV filing reminder (14th of month), bank import complete
- All marketing emails include unsubscribe link (GDPR)

### 5.5 Accessibility

- WCAG 2.1 Level AA compliance
- Keyboard navigation for all interactive elements
- Screen reader compatibility (Radix UI / shadcn ARIA labels)
- Color contrast ratio ≥ 4.5:1 (Bilko mint green #00E5A0 on dark backgrounds verified)

---

## 6. Requirements Summary Table

| ID     | Feature Name                       | Module         | Priority  | Status                                                             | Trace                          |
| ------ | ---------------------------------- | -------------- | --------- | ------------------------------------------------------------------ | ------------------------------ |
| FR-001 | User Registration                  | Authentication | Must Have | SUPERSEDED — Entra SSO/JIT + FR-003 invite (email/pw retired, 410) | BR-014                         |
| FR-002 | User Login                         | Authentication | Must Have | SUPERSEDED — Entra CIAM SSO only (email/pw retired, 410)           | BR-014                         |
| FR-003 | Invite User to Organization        | Authentication | Must Have | Not Started                                                        | BR-007                         |
| FR-010 | Create Invoice                     | Invoicing      | Must Have | Not Started                                                        | BR-001, BR-002, BR-004, BR-008 |
| FR-011 | SEF E-Invoice Submission           | Invoicing      | Must Have | Not Started                                                        | BR-001                         |
| FR-012 | Track Invoice Status               | Invoicing      | Must Have | Not Started                                                        | BR-001, BR-008                 |
| FR-020 | Create Expense                     | Expenses       | Must Have | Not Started                                                        | BR-009                         |
| FR-030 | Chart of Accounts                  | Bookkeeping    | Must Have | Not Started                                                        | BR-003, BR-010                 |
| FR-031 | Double-Entry Transaction Recording | Bookkeeping    | Must Have | Not Started                                                        | BR-003                         |
| FR-040 | Bank Statement CSV Import          | Banking        | Must Have | Not Started                                                        | BR-005                         |
| FR-050 | PDV Report Generation              | VAT/PDV        | Must Have | Not Started                                                        | BR-002, BR-006                 |
| FR-060 | P&L Statement                      | Reports        | Must Have | Not Started                                                        | BR-006                         |
| FR-061 | Balance Sheet                      | Reports        | Must Have | Not Started                                                        | BR-006                         |
| FR-070 | Exchange Rate Management           | Multi-Currency | Must Have | Not Started                                                        | BR-004                         |

**Requirements Count:**

- Must Have: 14
- Should Have: 0 in this document (Croatian eRačun in Phase 2 FRS)
- Could Have: 0
- Won't Have (Phase 1): Payroll, AI bookkeeping, live bank feeds

---

## 7. Traceability to Business Requirements

| FR ID  | Feature Name            | Business Requirement (BR ID)   | Business Objective (BO ID) |
| ------ | ----------------------- | ------------------------------ | -------------------------- |
| FR-001 | User Registration       | BR-014                         | BO-01                      |
| FR-002 | User Login              | BR-014                         | BO-01                      |
| FR-003 | Invite User             | BR-007                         | BO-02                      |
| FR-010 | Create Invoice          | BR-001, BR-002, BR-004, BR-008 | BO-01, BO-02               |
| FR-011 | SEF Submission          | BR-001                         | BO-01                      |
| FR-012 | Invoice Status Tracking | BR-001, BR-008                 | BO-02                      |
| FR-020 | Create Expense          | BR-009                         | BO-02                      |
| FR-030 | Chart of Accounts       | BR-003, BR-010                 | BO-01                      |
| FR-031 | Double-Entry Recording  | BR-003                         | BO-01                      |
| FR-040 | Bank CSV Import         | BR-005                         | BO-02                      |
| FR-050 | PDV Report              | BR-002, BR-006                 | BO-01, BO-03               |
| FR-060 | P&L Statement           | BR-006                         | BO-03                      |
| FR-061 | Balance Sheet           | BR-006                         | BO-01, BO-03               |
| FR-070 | Exchange Rates          | BR-004                         | BO-01                      |

> Full traceability matrix: [`RTM.md`](RTM.md)

---

## Approval

| Role               | Name               | Date       | Signature |
| ------------------ | ------------------ | ---------- | --------- |
| Author             | John (AI Director) | 2026-02-23 |           |
| Reviewer           |                    |            |           |
| Business Analyst   | John               | 2026-02-23 |           |
| Tech Lead          | John               | 2026-02-23 |           |
| Product Owner      | John               | 2026-02-23 |           |
| AI Director (John) | John               | 2026-02-23 |           |
| CEO (Alem)         | Alem Bašić         |            |           |