# Backend Krav - Dansk Bogføringssystem

## Overblik

Dette dokument beskriver backend-kravene for det danske bogføringssystem. Frontend er implementeret i React/TypeScript med Ant Design og Zustand state management.

---

## 1. Regnskabsår (Fiscal Years)

### 1.1 Data Model

```typescript
interface FiscalYear {
  id: string;
  companyId: string;
  name: string;                    // "2025" eller "2024/2025"
  startDate: string;               // ISO date "YYYY-MM-DD"
  endDate: string;                 // ISO date "YYYY-MM-DD"
  status: 'open' | 'closed' | 'locked';
  openingBalancePosted: boolean;
  closingDate?: string;            // Når året blev lukket
  closedBy?: string;               // Bruger ID
  createdAt: string;
  updatedAt: string;
}
```

### 1.2 API Endpoints

| Endpoint | Metode | Beskrivelse |
|----------|--------|-------------|
| `/api/fiscal-years` | GET | Hent alle regnskabsår for aktiv virksomhed |
| `/api/fiscal-years` | POST | Opret nyt regnskabsår |
| `/api/fiscal-years/:id` | GET | Hent specifikt regnskabsår |
| `/api/fiscal-years/:id` | PATCH | Opdater regnskabsår |
| `/api/fiscal-years/:id/close` | POST | Luk regnskabsår (årsafslutning) |
| `/api/fiscal-years/:id/reopen` | POST | Genåbn lukket regnskabsår |
| `/api/fiscal-years/:id/lock` | POST | Lås regnskabsår permanent |

### 1.3 Forretningsregler

#### Oprettelse
- Nye regnskabsår må IKKE overlappe med eksisterende
- Regnskabsår der "rører" ved grænsen (slutter/starter samme dag) er TILLADT
- Understøt både kalenderår (jan-dec) og skæve regnskabsår (fx jul-jun)
- Valider at regnskabsåret er mellem 300-400 dage (advarsel, ikke fejl)

#### Årsafslutning (Year-End Closing)
**KRITISK**: Ved årsafslutning skal backend:

1. **Validere** at regnskabsåret kan lukkes:
   - Ikke allerede låst
   - Advar om åbne perioder (men tillad lukning)
   - Advar om ikke-afstemte transaktioner

2. **Oprette lukkeposter** (closing entries):
   - Luk alle indtægtskonti til resultatoverførselskonto
   - Luk alle udgiftskonti til resultatoverførselskonto
   - Gem som normale transaktioner med særlig markering (`isClosingEntry: true`)

3. **Generere åbningsbalancer** til næste regnskabsår:
   - Beregn slutsaldo for alle balancekonti (aktiver, passiver, egenkapital)
   - Nulstil resultatkonti (indtægter, udgifter, vareforbrug, personale, finansielle)
   - Opret åbningsposteringer i næste regnskabsår (`isOpeningBalance: true`)

4. **Opdatere status**:
   - Sæt regnskabsår status til 'closed' eller 'locked'
   - Luk alle tilhørende regnskabsperioder
   - Sæt `openingBalancePosted: true` på næste regnskabsår

#### Genåbning
- Kun 'closed' regnskabsår kan genåbnes (ikke 'locked')
- Ved genåbning skal åbningsbalancer i næste år opdateres automatisk
- Log hvem der genåbnede og hvornår

### 1.4 Dynamiske Åbningsbalancer

Frontend forventer at åbningsbalancer opdateres automatisk når der bogføres i et tidligere år (som Dinero):

```
Scenarie:
1. Regnskabsår 2024 er lukket, 2025 er åbent
2. Revisor finder fejl og bogfører korrektion i 2024
3. Backend skal automatisk genberegne åbningsbalancen for 2025
```

---

## 2. Regnskabsperioder (Accounting Periods)

### 2.1 Data Model

```typescript
interface AccountingPeriod {
  id: string;
  fiscalYearId: string;
  companyId: string;
  name: string;                    // "Januar 2025", "Q1 2025"
  periodNumber: number;            // 1-12 for månedlig
  startDate: string;
  endDate: string;
  status: 'future' | 'open' | 'closed' | 'locked';
  closedAt?: string;
  closedBy?: string;
  reopenedAt?: string;
  reopenedBy?: string;
  lockedAt?: string;
  lockedBy?: string;
  createdAt: string;
  updatedAt: string;
}

type PeriodFrequency = 'monthly' | 'quarterly' | 'half-yearly' | 'yearly';
```

### 2.2 API Endpoints

| Endpoint | Metode | Beskrivelse |
|----------|--------|-------------|
| `/api/periods` | GET | Hent perioder (filtrer på fiscalYearId) |
| `/api/periods/:id` | PATCH | Opdater periode |
| `/api/periods/:id/close` | POST | Luk periode |
| `/api/periods/:id/reopen` | POST | Genåbn periode |
| `/api/periods/:id/lock` | POST | Lås periode |

### 2.3 Forretningsregler

- Perioder genereres automatisk ved oprettelse af regnskabsår
- Understøt: månedlig (12), kvartalsvis (4), halvårlig (2), årlig (1)
- Perioder kan kun lukkes i rækkefølge (periode 2 kræver periode 1 lukket)
- Låste perioder kan IKKE genåbnes

---

## 3. Transaktioner med Periode-Reference

### 3.1 Udvidet Transaction Model

```typescript
interface Transaction {
  id: string;
  companyId: string;
  fiscalYearId: string;           // PÅKRÆVET - hvilket regnskabsår
  periodId: string;               // PÅKRÆVET - hvilken periode
  journalEntryNumber: number;
  date: string;
  description: string;
  reference?: string;
  lines: TransactionLine[];
  isVoided: boolean;
  isReconciled: boolean;
  isClosingEntry?: boolean;       // NY - markering for lukkepost
  isOpeningBalance?: boolean;     // NY - markering for åbningsbalance
  createdAt: string;
  updatedAt: string;
  createdBy: string;
}
```

### 3.2 Forretningsregler for Bogføring

```typescript
interface PostingValidation {
  allowed: boolean;
  reason?: string;
  reasonDanish?: string;
}
```

Backend skal validere ved bogføring:
1. Find periode for transaktionsdato
2. Tjek om perioden er åben
3. Tjek om regnskabsåret er åbent
4. Returner fejl med dansk besked hvis ikke tilladt

---

## 4. Kontoplan (Chart of Accounts)

### 4.1 Data Model

```typescript
interface Account {
  id: string;
  companyId: string;
  accountNumber: string;          // "1000", "3900"
  name: string;
  type: AccountType;
  parentId?: string;
  description?: string;
  vatCodeId?: string;
  isActive: boolean;
  isSystemAccount: boolean;       // Kan ikke slettes
  balance: number;                // Beregnet felt
  createdAt: string;
  updatedAt: string;
}

type AccountType =
  | 'asset'        // Aktiver
  | 'liability'    // Passiver
  | 'equity'       // Egenkapital
  | 'revenue'      // Indtægter
  | 'cogs'         // Vareforbrug
  | 'expense'      // Udgifter
  | 'personnel'    // Personaleomkostninger
  | 'financial'    // Finansielle poster
  | 'extraordinary'; // Ekstraordinære poster
```

### 4.2 Systemkonti

Følgende konti skal oprettes automatisk:
- `3900` - Overført resultat (equity) - bruges til årsafslutning
- Åbningsbalancekonto for primo-posteringer

---

## 5. Moms (VAT)

### 5.1 Data Model

```typescript
interface VATCode {
  id: string;
  companyId: string;
  code: string;                   // "S25", "K25"
  name: string;
  rate: number;                   // 0.25 for 25%
  type: 'sales' | 'purchase' | 'eu_sales' | 'eu_purchase' | 'reverse_charge';
  accountId: string;              // Momskonto
  isActive: boolean;
}

interface VATPeriod {
  id: string;
  companyId: string;
  startDate: string;
  endDate: string;
  dueDate: string;                // Indberetningsfrist
  status: 'open' | 'submitted' | 'paid';
  salesVAT: number;               // Beregnet
  purchaseVAT: number;            // Beregnet
  netVAT: number;                 // salesVAT - purchaseVAT
  submittedAt?: string;
  paidAt?: string;
}
```

### 5.2 SKAT Integration (Fremtidig)

- Momsindberetning til SKAT via NemVirksomhed API
- Kvartalsvise eller månedlige momsperioder baseret på virksomhedsstørrelse

---

## 6. Virksomhed (Company)

### 6.1 Data Model

```typescript
interface Company {
  id: string;
  name: string;
  cvr: string;                    // Dansk CVR-nummer
  address?: string;
  postalCode?: string;
  city?: string;
  country: string;                // Default "DK"
  fiscalYearStart: number;        // Måned 1-12 (default 1 for januar)
  currency: string;               // Default "DKK"
  vatRegistered: boolean;
  vatPeriodFrequency: 'monthly' | 'quarterly' | 'half-yearly';
  createdAt: string;
  updatedAt: string;
}
```

---

## 7. Fejlhåndtering

Alle fejlbeskeder skal være tilgængelige på både engelsk og dansk:

```typescript
interface APIError {
  code: string;
  message: string;
  messageDanish: string;
  details?: Record<string, unknown>;
}
```

### Eksempler på fejlkoder

| Kode | Engelsk | Dansk |
|------|---------|-------|
| `PERIOD_LOCKED` | Period is locked | Perioden er låst |
| `FISCAL_YEAR_LOCKED` | Fiscal year is locked | Regnskabsåret er låst |
| `OVERLAP_EXISTS` | Overlaps with existing fiscal year | Overlapper med eksisterende regnskabsår |
| `UNBALANCED_ENTRY` | Debit and credit must be equal | Debet og kredit skal være ens |
| `NO_COMPANY_SELECTED` | No company selected | Ingen virksomhed valgt |

---

## 8. Prioriteret Implementeringsrækkefølge

### Fase 1: Grundlæggende (MVP)
1. Company CRUD
2. Account CRUD med standard kontoplan
3. Transaction CRUD med validering
4. FiscalYear CRUD

### Fase 2: Periode-management
1. AccountingPeriod generering og CRUD
2. Periode-validering ved bogføring
3. Periode lukning/åbning

### Fase 3: Årsafslutning
1. Closing entries generering og bogføring
2. Opening balance beregning og bogføring
3. Dynamisk åbningsbalance-opdatering

### Fase 4: Moms
1. VATCode CRUD
2. VATPeriod generering
3. Momsberegning og -rapportering

---

## 9. Tekniske Krav

### Authentication
- JWT-baseret authentication
- Multi-tenant support (bruger kan have adgang til flere virksomheder)

### Database
- PostgreSQL anbefales
- Soft delete på alle entiteter
- Audit trail (createdBy, updatedBy, deletedBy)

### API Format
- RESTful JSON API
- GraphQL som alternativ (valgfrit)
- Pagination på liste-endpoints
- Filtering og sorting support

### Valuta
- Alle beløb i øre/cents (integers) for at undgå floating point problemer
- Frontend konverterer til/fra DKK ved visning

---

*Sidst opdateret: 17. januar 2026*
