Business Logic Overview
Proxima Invoice handles three types of client deals, each with distinct billing logic. Understanding deal types, client classification, and currency handling is essential to working with the system.
Deal Types
Every client application (contract) has a deal_type that determines how hours are billed.
| Deal Type | Name | Billing Model |
|---|---|---|
| SUP | Support | Fixed monthly amount + overtime beyond limit |
| HR | Hourly | All hours billed at hourly rate |
| FP | Fixed Price | Flat amount regardless of hours worked |
SUP (Support)
The most common deal type. The client pays a fixed monthly base_amount that covers hours up to monthly_limit. Hours beyond the limit are billed as overtime at the contract's hourly_rate.
if total_hours <= monthly_limit:
total = base_amount
else:
overtime_hours = total_hours - monthly_limit
overtime_amount = overtime_hours * hourly_rate * rate_multiplier
total = base_amount + overtime_amount
HR (Hourly)
All hours are billable. Each hour is multiplied by the hourly_rate and the applicable rate multiplier (for critical incidents, off-hours work, etc.).
total = sum(hours_per_tier * hourly_rate * tier_multiplier)
FP (Fixed Price)
A flat amount is charged regardless of how many hours were logged. Rate multipliers do not apply.
total = base_amount // always
Client Types
Clients are classified as local (Uzbekistan) or international based on their financial details.
| Criterion | Local | International |
|---|---|---|
| Currency | UZS | USD, EUR, or other |
| SWIFT/BIC | Empty or ≤2 chars | Present (>2 chars) |
| Invoice template | Russian ("Акт выполненных работ") | English ("Invoice") |
| Delivery | Didox.uz | Email with PDF |
| Base amount source | deal_amount | invoice_amount (if set), else deal_amount |
Detection logic:
isInternational = (swift_bic?.length > 2) || (currency !== 'UZS')
See International Invoices for full details.
Currency
The system supports three currencies:
- UZS — Uzbekistani som (local clients)
- USD — US dollar (international clients)
- EUR — Euro (international clients)
Currency is set at the application level in ClickHouse and carried through to the invoice. All financial math uses shopspring/decimal (never float64) to avoid rounding errors.
Key Business Rules
- Minimum billable time: Every worklog is rounded up to a minimum of 30 minutes (1800 seconds)
- Invoice numbering: Per-client atomic counter stored in
client_invoice_config.last_invoice_number. Incremented viaSELECT ... FOR UPDATEwithin the generation transaction to prevent concurrent collisions. The numbering seed must be set before the first invoice can be generated for a client. - Jira/Tempo data is pre-fill only: Users can edit every field before finalizing
- PDF generated only on finalize: Not during pre-fill or draft editing
- Rate multipliers: Phase 1 ships with all multipliers = 1.0 (except P1-P3 off-hours = 1.5)
- ProximaOps company: TIN 309630523, registered in Uzbekistan
- Display toggles: 4 per-invoice boolean flags control PDF/HTML rendering — task list visibility, time column, pricing column, overtime row. Defaults are inherited from
client_invoice_configand can be overridden per-invoice while in draft. - Legal text: Per-invoice legal text replaces hardcoded template paragraphs. Defaults from
client_invoice_config.default_legal_text, editable per-invoice with "Reset to default". - SUP task linking: Line items show
linked_issue_key(SUP-* tasks) from ClickHousejira.jira_issueslinked via Jira "duplicates" relation. - Company name enrichment: Invoice list and detail views show company names from ClickHouse
cdm.jira_customers, with graceful degradation if ClickHouse is unavailable.