# Functional Requirements Specification

# Functional Requirements Specification (FRS): Bilko

> **Project:** Bilko — Balkan Accounting SaaS
> **Version:** 0.1
> **Date:** 2026-02-23
> **Author:** John (AI Director)
> **Status:** Draft
> **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. 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**

| 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**

| 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 | Draft | BR-014 |
| FR-002 | User Login | Authentication | Must Have | Draft | BR-014 |
| FR-003 | Invite User to Organization | Authentication | Must Have | Draft | BR-007 |
| FR-010 | Create Invoice | Invoicing | Must Have | Draft | BR-001, BR-002, BR-004, BR-008 |
| FR-011 | SEF E-Invoice Submission | Invoicing | Must Have | Draft | BR-001 |
| FR-012 | Track Invoice Status | Invoicing | Must Have | Draft | BR-001, BR-008 |
| FR-020 | Create Expense | Expenses | Must Have | Draft | BR-009 |
| FR-030 | Chart of Accounts | Bookkeeping | Must Have | Draft | BR-003, BR-010 |
| FR-031 | Double-Entry Transaction Recording | Bookkeeping | Must Have | Draft | BR-003 |
| FR-040 | Bank Statement CSV Import | Banking | Must Have | Draft | BR-005 |
| FR-050 | PDV Report Generation | VAT/PDV | Must Have | Draft | BR-002, BR-006 |
| FR-060 | P&L Statement | Reports | Must Have | Draft | BR-006 |
| FR-061 | Balance Sheet | Reports | Must Have | Draft | BR-006 |
| FR-070 | Exchange Rate Management | Multi-Currency | Must Have | Draft | 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ć | | |