# Invoice Master - Fortnox Integration Technical Specification

**版本**: v1.0  
**日期**: 2026-02-01  
**作者**: Claude Code  
**状态**: 设计阶段

---

## 目录

1. [概述](#概述)
2. [集成模式说明](#集成模式说明)
3. [系统架构](#系统架构)
4. [Fortnox API分析](#fortnox-api分析)
5. [数据映射设计](#数据映射设计)
6. [核心功能模块](#核心功能模块)
7. [用户流程设计](#用户流程设计)
8. [UI设计规范](#ui设计规范)
9. [API设计](#api设计)
10. [数据库设计](#数据库设计)
11. [安全设计](#安全设计)
12. [错误处理](#错误处理)
13. [开发计划](#开发计划)
14. [测试策略](#测试策略)
15. [部署方案](#部署方案)
16. [附录](#附录)

---

## 概述

### 1.1 项目背景

Invoice Master是一个基于YOLO26 + PaddleOCR的发票字段自动提取系统，当前准确率达到94.8%。本方案设计将Invoice Master作为Fortnox会计软件的插件/扩展，实现无缝的发票数据导入功能。

### 1.2 目标

- 为Fortnox用户提供智能发票识别功能
- 实现一键将发票数据导入Fortnox
- 自动匹配供应商和会计科目
- 减少90%的手动录入工作

### 1.3 范围

**包含功能:**
- Fortnox OAuth2认证集成
- 发票PDF上传和OCR识别
- 供应商自动匹配/创建
- 会计凭证(Voucher)自动生成
- 发票图像存档

**不包含功能 (Phase 2):**
- 多文档类型支持 (收据、对账单)
- 自动付款流程
- 审批工作流

### 1.4 术语定义

| 术语 | 英文 | 说明 |
|------|------|------|
| 供应商 | Supplier | Leverantör i Fortnox |
| 会计凭证 | Voucher | Verifikation i Fortnox |
| 发票 | Invoice | Faktura |
| 科目 | Account | Konto i kontoplanen |
| OCR参考号 | OCR Number | 瑞典特有的付款参考号 |

---

## 集成模式说明

### 2.1 Fortnox Extension UI模式

Fortnox的集成主要有**两种模式**，Invoice Master采用**模式1: 外部独立应用**。

#### 模式1: 外部独立应用 (External App) - 推荐

**架构示意图:**

```
用户流程:
┌─────────────────┐     ┌─────────────────────┐     ┌─────────────────┐
│   Fortnox       │────▶│  Invoice Master     │────▶│   Fortnox       │
│   (点击集成)     │     │  (独立Web应用)       │     │   (数据已导入)   │
└─────────────────┘     └─────────────────────┘     └─────────────────┘
        │                          │                        │
        │                          │                        │
        ▼                          ▼                        ▼
   在Fortnox中              用户在你的网站上            用户回到Fortnox
   看到"Invoice Master"      完成发票上传和识别          查看已导入的凭证
   点击打开新窗口
```

**特点:**
- ✅ **有自己的完整UI**（独立网站）
- ✅ 通过OAuth2连接Fortnox
- ✅ 用户在Fortnox点击后跳转到你的网站
- ✅ 数据通过API双向同步
- ✅ 更灵活的功能和用户体验

**Fortnox中的展示:**
- 在Fortnox Integrations页面列出
- 用户点击后打开新标签页到你的网站
- 显示连接状态和基本设置

#### 模式2: 嵌入式集成 (Embedded) - 有限支持

**Fortnox目前支持:**
- 菜单链接 (Menu Links) - 在Fortnox菜单中添加链接
- 快捷操作 (Quick Actions) - 有限的上下文操作
- 文件导入 (File Import) - 通过Inbox API

**Fortnox不提供:**
- ❌ iframe嵌入第三方UI
- ❌ 自定义页面/标签
- ❌ 深度UI定制

### 2.2 推荐方案: 混合模式

**架构设计:**

```
┌─────────────────────────────────────────────────────────────────┐
│                    Invoice Master for Fortnox                    │
│                                                                  │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │              独立Web应用 (你的域名)                        │  │
│  │  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐   │  │
│  │  │   发票上传    │  │   识别结果   │  │   历史记录   │   │  │
│  │  │    页面      │  │   确认页面   │  │    页面     │   │  │
│  │  └──────────────┘  └──────────────┘  └──────────────┘   │  │
│  │                                                              │
│  │  功能: OCR识别、供应商匹配、预览确认、一键导入Fortnox         │  │
│  └──────────────────────────────────────────────────────────┘  │
│                              │                                   │
│                              │ HTTPS API                        │
│                              ▼                                   │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │              Fortnox Integration Service                   │  │
│  │                   (Backend API)                            │  │
│  └──────────────────────────────────────────────────────────┘  │
│                              │                                   │
└──────────────────────────────┼───────────────────────────────────┘
                               │
                               │ OAuth2 + REST API
                               │
                    ┌──────────▼──────────┐
                    │      Fortnox        │
                    │   (数据存储/展示)    │
                    └─────────────────────┘
```

### 2.3 与纯API方案对比

| 特性 | 独立UI方案 (推荐) | 纯API方案 |
|------|------------------|-----------|
| **用户体验** | ⭐⭐⭐⭐⭐ 完整的可视化界面 | ⭐⭐ 需要用户自己调用API |
| **开发复杂度** | ⭐⭐⭐ 需要前端+后端 | ⭐⭐ 只需要后端API |
| **功能灵活性** | ⭐⭐⭐⭐⭐ 可以做OCR预览、编辑 | ⭐⭐ 直接导入，无法预览 |
| **用户门槛** | ⭐⭐⭐⭐⭐ 低，非技术用户可用 | ⭐ 高，需要开发者 |
| **Fortnox审核** | ⭐⭐⭐⭐ 标准流程 | ⭐⭐⭐⭐ 更简单 |

### 2.4 用户完整流程

```
1. 发现阶段
   用户在Fortnox Integrations页面找到"Invoice Master"
   
2. 授权阶段
   用户点击"连接" → OAuth2授权 → 跳转到Invoice Master
   
3. 使用阶段 (在Invoice Master网站)
   上传PDF → OCR识别 → 确认/编辑 → 导入到Fortnox
   
4. 查看阶段 (回到Fortnox)
   用户在Fortnox中查看已导入的凭证和发票
```

---

## 系统架构

### 3.1 整体架构图

**注意: 这是技术架构图，对应第2章描述的"独立Web应用"模式**

```
┌─────────────────────────────────────────────────────────────────┐
│                        Fortnox Platform                          │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐          │
│  │   Fortnox    │  │   Fortnox    │  │   Fortnox    │          │
│  │     UI       │  │     API      │  │   Database   │          │
│  └──────┬───────┘  └──────┬───────┘  └──────────────┘          │
└─────────┼─────────────────┼─────────────────────────────────────┘
          │                 │
          │ OAuth2          │ HTTPS
          │                 │
┌─────────▼─────────────────▼─────────────────────────────────────┐
│                    Invoice Master Integration                    │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │              Fortnox Integration Service                  │  │
│  │  ┌────────────┐  ┌────────────┐  ┌────────────┐         │  │
│  │  │   Auth     │  │  Invoice   │  │  Supplier  │         │  │
│  │  │   Module   │  │  Handler   │  │  Matcher   │         │  │
│  │  └────────────┘  └────────────┘  └────────────┘         │  │
│  │  ┌────────────┐  ┌────────────┐  ┌────────────┐         │  │
│  │  │  Voucher   │  │   File     │  │  Webhook   │         │  │
│  │  │  Creator   │  │  Storage   │  │  Handler   │         │  │
│  │  └────────────┘  └────────────┘  └────────────┘         │  │
│  └──────────────────────────────────────────────────────────┘  │
│                                                                  │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │              Invoice Master Core Services                 │  │
│  │  ┌────────────┐  ┌────────────┐  ┌────────────┐         │  │
│  │  │    OCR     │  │    YOLO    │  │  Field     │         │  │
│  │  │   Engine   │  │  Detector  │  │Normalizer  │         │  │
│  │  └────────────┘  └────────────┘  └────────────┘         │  │
│  └──────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────┘
          │
          │ PostgreSQL / Azure Blob
          │
┌─────────▼─────────────────────────────────────────────────────┐
│                      Data Storage                              │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐        │
│  │   Invoice    │  │   Fortnox    │  │    File      │        │
│  │    Data      │  │    Tokens    │  │   Storage    │        │
│  └──────────────┘  └──────────────┘  └──────────────┘        │
└─────────────────────────────────────────────────────────────────┘
```

### 3.2 组件说明

| 组件 | 技术栈 | 职责 |
|------|--------|------|
| **Integration Service** | FastAPI + Python | Fortnox API交互、业务逻辑 |
| **Auth Module** | OAuth2 + JWT | Fortnox认证、Token管理 |
| **Invoice Handler** | - | 发票处理流程协调 |
| **Supplier Matcher** | Fuzzy Matching | 供应商匹配算法 |
| **Voucher Creator** | - | 生成Fortnox会计凭证 |
| **File Storage** | Azure Blob / S3 | 发票PDF存储 |
| **Webhook Handler** | - | 接收Fortnox事件 |

### 3.3 技术栈

| 层级 | 技术 | 说明 |
|------|------|------|
| **Backend** | FastAPI + Python 3.11 | API服务 |
| **Database** | PostgreSQL 15 | 关系数据 |
| **Cache** | Redis | Token缓存、限流 |
| **Storage** | Azure Blob Storage | 文件存储 |
| **Message Queue** | Redis Queue | 异步任务 |
| **Monitoring** | Prometheus + Grafana | 监控告警 |

---

## Fortnox API分析

### 3.1 认证机制

Fortnox使用OAuth2授权码流程:

```
┌─────────┐                                    ┌─────────────┐
│  User   │──(1) Authorization Request───────▶│   Fortnox   │
│         │◀──(2) Authorization Code─────────│   OAuth2    │
│         │                                    │   Server    │
│         │──(3) Token Request────────────────▶│             │
│         │◀──(4) Access + Refresh Token──────│             │
└─────────┘                                    └─────────────┘
```

**关键端点:**
```
Authorization URL: https://apps.fortnox.se/oauth-v1/auth
Token URL:         https://apps.fortnox.se/oauth-v1/token
API Base URL:      https://api.fortnox.se/3
```

**Scopes Required:**
```
supplier      - 供应商管理
invoice       - 发票管理 (如需要)
voucher       - 会计凭证
account       - 会计科目
companyinformation - 公司信息
```

### 3.2 核心API端点

#### 3.2.1 供应商管理

```http
# 获取供应商列表
GET /3/suppliers
Response: {
  "Suppliers": [
    {
      "@url": "https://api.fortnox.se/3/suppliers/123",
      "Name": "ABC Company",
      "SupplierNumber": "123",
      "OrganisationNumber": "556677-8899"
    }
  ]
}

# 创建供应商
POST /3/suppliers
Body: {
  "Supplier": {
    "Name": "New Supplier",
    "OrganisationNumber": "112233-4455"
  }
}
```

#### 3.2.2 会计凭证 (Voucher)

```http
# 创建会计凭证
POST /3/vouchers
Body: {
  "Voucher": {
    "VoucherSeries": "A",           // 凭证系列
    "TransactionDate": "2024-01-15", // 交易日期
    "VoucherRows": [
      {
        "Account": 2440,            // 应付账款科目
        "Debit": 1250.00,
        "Credit": 0,
        "Description": "Invoice F2024-001"
      },
      {
        "Account": 5460,            // 费用科目
        "Debit": 0,
        "Credit": 1000.00,
        "Description": "Office supplies"
      },
      {
        "Account": 2610,            // 增值税科目
        "Debit": 0,
        "Credit": 250.00,
        "Description": "VAT 25%"
      }
    ]
  }
}
```

#### 3.2.3 文件上传

```http
# 上传附件到Fortnox
POST /3/inbox
Content-Type: multipart/form-data
Body: {
  "file": [PDF file],
  "name": "Invoice_F2024_001.pdf"
}
```

### 3.3 API限制

| 限制类型 | 值 | 说明 |
|---------|-----|------|
| 速率限制 | 300请求/分钟 | 超出返回429 |
| 并发连接 | 10 | 同时连接数 |
| Token有效期 | 3600秒 | 需使用Refresh Token |
| 文件大小 | 10MB | 单个文件限制 |

---

## 数据映射设计

### 4.1 发票字段映射

**Invoice Master提取字段 → Fortnox字段**

| Invoice Master | Fortnox | 类型 | 必填 | 转换逻辑 |
|---------------|---------|------|------|----------|
| `invoice_number` | `ExternalInvoiceNumber` | string | 是 | 直接映射 |
| `invoice_date` | `TransactionDate` | date | 是 | ISO 8601格式 |
| `due_date` | `DueDate` | date | 否 | 计算或提取 |
| `supplier_name` | `SupplierName` | string | 是 | 匹配或创建 |
| `supplier_org_number` | `SupplierOrganisationNumber` | string | 否 | 用于匹配 |
| `amount_total` | `TotalAmount` | decimal | 是 | 直接映射 |
| `amount_vat` | `VatAmount` | decimal | 否 | 计算得出 |
| `ocr_number` | `OCRNumber` | string | 否 | 瑞典特有 |
| `bankgiro` | `BankgiroNumber` | string | 否 | 付款信息 |
| `plusgiro` | `PlusgiroNumber` | string | 否 | 付款信息 |
| `currency` | `Currency` | string | 是 | 默认SEK |

### 4.2 会计科目映射

**默认科目映射表 (Kontoplan BAS2024)**

| 费用类型 | 科目代码 | 科目名称 | 说明 |
|---------|---------|---------|------|
| 应付账款 | 2440 | Leverantörsskulder | 默认贷方 |
| 办公用品 | 5460 | Kontorsmaterial | 常见费用 |
| 咨询服务 | 6210 | Konsultarvoden | 外部服务 |
| 运输费 | 5710 | Frakter | 物流费用 |
| 增值税进项 | 2610 | Ingående moms | 25% VAT |
| 增值税进项12% | 2620 | Ingående moms 12% | 食品等 |
| 增值税进项6% | 2630 | Ingående moms 6% | 交通等 |

**科目选择逻辑:**
```python
def select_account(invoice_data: dict) -> int:
    """根据发票内容选择会计科目"""
    
    # 1. 检查是否有历史映射
    if invoice_data['supplier_org_number']:
        historical = get_historical_account(invoice_data['supplier_org_number'])
        if historical:
            return historical
    
    # 2. 关键词匹配
    description = invoice_data.get('description', '').lower()
    if any(word in description for word in ['kontor', 'papper', 'penna']):
        return 5460  # 办公用品
    elif any(word in description for word in ['konsult', 'tjänst']):
        return 6210  # 咨询服务
    elif any(word in description for word in ['frakt', 'transport']):
        return 5710  # 运输费
    
    # 3. 默认科目
    return 6100  # 其他外部费用
```

### 4.3 供应商匹配算法

**匹配优先级:**

```python
class SupplierMatcher:
    def match_supplier(self, extracted_data: dict) -> MatchResult:
        """
        供应商匹配算法
        返回: (supplier_number, confidence_score, action)
        """
        
        # 1. 组织号精确匹配 (权重: 100%)
        if extracted_data.get('supplier_org_number'):
            exact_match = self.find_by_org_number(
                extracted_data['supplier_org_number']
            )
            if exact_match:
                return MatchResult(
                    supplier_number=exact_match.number,
                    confidence=1.0,
                    action='USE_EXISTING'
                )
        
        # 2. 名称模糊匹配 (权重: 80%)
        name_matches = self.fuzzy_match_name(
            extracted_data['supplier_name'],
            threshold=0.85
        )
        if name_matches and name_matches[0].score > 0.9:
            return MatchResult(
                supplier_number=name_matches[0].number,
                confidence=name_matches[0].score,
                action='USE_EXISTING'
            )
        
        # 3. 建议创建新供应商 (权重: <80%)
        return MatchResult(
            supplier_number=None,
            confidence=0.0,
            action='CREATE_NEW',
            suggested_name=extracted_data['supplier_name']
        )
```

---

## 核心功能模块

### 5.1 认证模块 (Auth Module)

**职责:**
- Fortnox OAuth2流程管理
- Token存储和刷新
- 多租户隔离

**核心类:**

```python
class FortnoxAuthManager:
    """Fortnox认证管理器"""
    
    def __init__(self, client_id: str, client_secret: str):
        self.client_id = client_id
        self.client_secret = client_secret
        self.token_store = TokenStore()
    
    def get_authorization_url(self, state: str) -> str:
        """生成Fortnox授权URL"""
        params = {
            'client_id': self.client_id,
            'redirect_uri': settings.FORTNOX_REDIRECT_URI,
            'scope': 'supplier voucher account companyinformation',
            'state': state,
            'response_type': 'code'
        }
        return f"{FORTNOX_AUTH_URL}?{urlencode(params)}"
    
    async def exchange_code_for_token(self, code: str) -> FortnoxToken:
        """用授权码换取Token"""
        response = await httpx.post(
            FORTNOX_TOKEN_URL,
            auth=(self.client_id, self.client_secret),
            data={
                'grant_type': 'authorization_code',
                'code': code,
                'redirect_uri': settings.FORTNOX_REDIRECT_URI
            }
        )
        token_data = response.json()
        
        return FortnoxToken(
            access_token=token_data['access_token'],
            refresh_token=token_data['refresh_token'],
            expires_at=datetime.utcnow() + timedelta(seconds=token_data['expires_in']),
            scope=token_data['scope']
        )
    
    async def get_valid_access_token(self, tenant_id: str) -> str:
        """获取有效的访问Token（自动刷新）"""
        token = await self.token_store.get(tenant_id)
        
        if token.is_expired():
            token = await self.refresh_token(token.refresh_token)
            await self.token_store.save(tenant_id, token)
        
        return token.access_token
```

### 5.2 发票处理模块 (Invoice Handler)

**处理流程:**

```python
class InvoiceProcessingService:
    """发票处理服务"""
    
    async def process_invoice(
        self,
        tenant_id: str,
        pdf_file: UploadFile,
        settings: ProcessingSettings
    ) -> ProcessingResult:
        """
        处理发票的主流程
        """
        
        # 1. 保存PDF文件
        file_path = await self.file_storage.save(pdf_file)
        
        # 2. OCR提取
        extraction_result = await self.ocr_service.extract(file_path)
        
        # 3. 验证提取结果
        if not self.validate_extraction(extraction_result):
            return ProcessingResult(
                status='FAILED',
                error='Extraction validation failed'
            )
        
        # 4. 供应商匹配
        supplier_match = await self.supplier_matcher.match(
            tenant_id,
            extraction_result
        )
        
        # 5. 创建或获取供应商
        if supplier_match.action == 'CREATE_NEW':
            supplier_number = await self.create_supplier(
                tenant_id,
                extraction_result
            )
        else:
            supplier_number = supplier_match.supplier_number
        
        # 6. 生成会计凭证
        voucher = await self.voucher_creator.create(
            tenant_id,
            extraction_result,
            supplier_number,
            settings
        )
        
        # 7. 上传附件
        if settings.attach_pdf:
            await self.attach_invoice_pdf(tenant_id, voucher.id, file_path)
        
        return ProcessingResult(
            status='SUCCESS',
            extraction=extraction_result,
            supplier_number=supplier_number,
            voucher_id=voucher.id,
            confidence=supplier_match.confidence
        )
```

### 5.3 供应商匹配模块 (Supplier Matcher)

```python
class FortnoxSupplierMatcher:
    """Fortnox供应商匹配器"""
    
    def __init__(self, fortnox_client: FortnoxClient):
        self.client = fortnox_client
        self.cache = SupplierCache()
    
    async def match(
        self,
        tenant_id: str,
        extraction: ExtractionResult
    ) -> SupplierMatchResult:
        """匹配供应商"""
        
        # 获取所有供应商（带缓存）
        suppliers = await self.cache.get_suppliers(tenant_id)
        
        # 1. 组织号精确匹配
        if extraction.supplier_org_number:
            match = self._match_by_org_number(
                suppliers,
                extraction.supplier_org_number
            )
            if match:
                return SupplierMatchResult(
                    supplier_number=match['SupplierNumber'],
                    confidence=1.0,
                    action='USE_EXISTING'
                )
        
        # 2. 名称模糊匹配
        name_match = self._fuzzy_match_name(
            suppliers,
            extraction.supplier_name
        )
        
        if name_match and name_match['score'] > 0.9:
            return SupplierMatchResult(
                supplier_number=name_match['supplier']['SupplierNumber'],
                confidence=name_match['score'],
                action='USE_EXISTING'
            )
        elif name_match and name_match['score'] > 0.7:
            return SupplierMatchResult(
                supplier_number=name_match['supplier']['SupplierNumber'],
                confidence=name_match['score'],
                action='SUGGEST_MATCH',
                suggested_name=extraction.supplier_name
            )
        
        # 3. 建议创建新供应商
        return SupplierMatchResult(
            supplier_number=None,
            confidence=0.0,
            action='CREATE_NEW',
            suggested_name=extraction.supplier_name,
            suggested_org_number=extraction.supplier_org_number
        )
    
    async def create_supplier(
        self,
        tenant_id: str,
        extraction: ExtractionResult
    ) -> str:
        """在Fortnox中创建新供应商"""
        
        supplier_data = {
            'Supplier': {
                'Name': extraction.supplier_name,
                'OrganisationNumber': extraction.supplier_org_number,
                'Address1': extraction.supplier_address,
                'Phone': extraction.supplier_phone,
                'Email': extraction.supplier_email,
                'BankgiroNumber': extraction.bankgiro,
                'PlusgiroNumber': extraction.plusgiro
            }
        }
        
        response = await self.client.post(
            tenant_id,
            '/3/suppliers',
            json=supplier_data
        )
        
        # 刷新缓存
        await self.cache.invalidate(tenant_id)
        
        return response['Supplier']['SupplierNumber']
```

### 5.4 凭证生成模块 (Voucher Creator)

```python
class FortnoxVoucherCreator:
    """Fortnox会计凭证生成器"""
    
    async def create_voucher(
        self,
        tenant_id: str,
        extraction: ExtractionResult,
        supplier_number: str,
        settings: VoucherSettings
    ) -> VoucherResult:
        """创建会计凭证"""
        
        # 确定会计科目
        account = await self.select_account(extraction)
        
        # 计算VAT
        vat_amount = self.calculate_vat(
            extraction.amount_total,
            extraction.vat_rate or 25
        )
        amount_excl_vat = extraction.amount_total - vat_amount
        
        # 构建凭证行
        voucher_rows = [
            # 借方: 费用科目
            {
                'Account': account,
                'Debit': amount_excl_vat,
                'Credit': 0,
                'Description': f"{extraction.supplier_name} - {extraction.invoice_number}",
                'Project': settings.project_code
            },
            # 借方: 增值税
            {
                'Account': self.get_vat_account(extraction.vat_rate),
                'Debit': vat_amount,
                'Credit': 0,
                'Description': f"Moms {extraction.vat_rate}%"
            },
            # 贷方: 应付账款
            {
                'Account': 2440,  # Leverantörsskulder
                'Debit': 0,
                'Credit': extraction.amount_total,
                'Description': f"Faktura {extraction.invoice_number}",
                'SupplierNumber': supplier_number,
                'OCRNumber': extraction.ocr_number
            }
        ]
        
        voucher_data = {
            'Voucher': {
                'VoucherSeries': settings.voucher_series or 'A',
                'TransactionDate': extraction.invoice_date.isoformat(),
                'VoucherText': f"Inköp {extraction.supplier_name}",
                'VoucherRows': voucher_rows
            }
        }
        
        response = await self.client.post(
            tenant_id,
            '/3/vouchers',
            json=voucher_data
        )
        
        return VoucherResult(
            voucher_id=response['Voucher']['VoucherNumber'],
            series=response['Voucher']['VoucherSeries'],
            url=response['Voucher']['@url']
        )
```

---

## 用户流程设计

### 6.1 集成入口点 (在Fortnox中)

**Fortnox Integrations页面展示:**

```
┌─────────────────────────────────────────────────────────────┐
│ Invoice Master - Smart Invoice OCR                          │
│                                                             │
│ 📄 自动识别发票信息                                          │
│ 🤖 AI驱动的OCR技术                                          │
│ ⚡ 一键导入到Fortnox                                         │
│                                                             │
│ [连接/打开]                                                  │
└─────────────────────────────────────────────────────────────┘
```

**Fortnox内配置页面:**

```
┌─────────────────────────────────────────────────────────────┐
│ Invoice Master 设置                                         │
│                                                             │
│ 状态: ✅ 已连接                                             │
│ 公司: My Company AB                                         │
│                                                             │
│ 默认设置:                                                   │
│ - 凭证系列: [A ▼]                                           │
│ - 自动导入: [✓]                                             │
│ - 附件上传: [✓]                                             │
│                                                             │
│ [保存设置]  [断开连接]                                       │
└─────────────────────────────────────────────────────────────┘
```

### 6.2 首次设置流程

```
用户点击"连接Fortnox"
        │
        ▼
┌───────────────────┐
│ 跳转到Fortnox授权页 │
│ (OAuth2流程)       │
└─────────┬─────────┘
          │
          ▼
┌───────────────────┐
│ 用户登录Fortnox    │
│ 并授权访问         │
└─────────┬─────────┘
          │
          ▼
┌───────────────────┐
│ 返回到Invoice      │
│ Master回调页面     │
└─────────┬─────────┘
          │
          ▼
┌───────────────────┐
│ 获取公司信息       │
│ 验证连接成功       │
└─────────┬─────────┘
          │
          ▼
┌───────────────────┐
│ 配置默认设置       │
│ - 凭证系列         │
│ - 默认科目         │
│ - 文件存储选项     │
└─────────┬─────────┘
          │
          ▼
┌───────────────────┐
│ 完成！显示        │
│ 上传发票界面       │
└───────────────────┘
```

### 6.2 发票处理流程

```
用户上传PDF发票
        │
        ▼
┌───────────────────┐
│ 显示处理进度      │
│ - OCR提取中...    │
└─────────┬─────────┘
          │
          ▼
┌───────────────────┐
│ 显示提取结果      │
│ 供用户确认/编辑   │
│                   │
│ ┌───────────────┐ │
│ │ 供应商: XXX   │ │
│ │ 金额: 1,250   │ │
│ │ 日期: 2024... │ │
│ │ [编辑] [确认] │ │
│ └───────────────┘ │
└─────────┬─────────┘
          │
    ┌─────┴─────┐
    │           │
    ▼           ▼
┌────────┐  ┌────────┐
│ 编辑   │  │ 确认   │
│ 数据   │  │ 导入   │
└───┬────┘  └───┬────┘
    │           │
    └─────┬─────┘
          │
          ▼
┌───────────────────┐
│ 供应商匹配        │
│ - 查找现有        │
│ - 或创建新        │
└─────────┬─────────┘
          │
          ▼
┌───────────────────┐
│ 生成会计凭证      │
│ 上传到Fortnox     │
└─────────┬─────────┘
          │
          ▼
┌───────────────────┐
│ 显示成功消息      │
│ 提供Fortnox链接   │
│ 查看凭证          │
└───────────────────┘
```

### 6.4 独立Web应用UI设计

**重要说明: 以下UI是Invoice Master独立Web应用的界面，用户在Fortnox点击"打开"后跳转到此界面。**

#### 主界面

#### 主界面

```
┌─────────────────────────────────────────────────────────────┐
│ Invoice Master for Fortnox                    [⚙️设置] [?] │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  📤 上传发票                                                 │
│  ┌───────────────────────────────────────────────────────┐ │
│  │                                                       │ │
│  │    拖放PDF文件到这里                                   │ │
│  │    或点击选择文件                                      │ │
│  │                                                       │ │
│  └───────────────────────────────────────────────────────┘ │
│                                                              │
│  📋 最近处理的发票                                           │
│  ┌───────────────────────────────────────────────────────┐ │
│  │ 文件名          │ 供应商      │ 金额    │ 状态      │ │
│  ├───────────────────────────────────────────────────────┤ │
│  │ INV001.pdf     │ ABC Company │ 1,250   │ ✅ 已导入 │ │
│  │ INV002.pdf     │ XYZ AB      │ 3,450   │ ✅ 已导入 │ │
│  │ INV003.pdf     │ (未匹配)    │ 890     │ ⚠️ 待确认 │ │
│  └───────────────────────────────────────────────────────┘ │
│                                                              │
└─────────────────────────────────────────────────────────────┘
```

#### 结果确认界面

**说明: 此界面在Invoice Master独立Web应用中显示，用于用户确认OCR识别结果。

```
┌─────────────────────────────────────────────────────────────┐
│ 确认发票信息                                      [✕] [✓] │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  📄 Invoice_F2024_001.pdf                                   │
│                                                              │
│  ┌───────────────────────────────────────────────────────┐ │
│  │ 供应商信息                                            │ │
│  │ ┌─────────────────────────────────────────────────┐   │ │
│  │ │ 名称: ABC Company                               │   │ │
│  │ │ 组织号: 556677-8899                             │   │ │
│  │ │ 状态: ✅ 已匹配现有供应商                        │   │ │
│  │ └─────────────────────────────────────────────────┘   │ │
│  └───────────────────────────────────────────────────────┘ │
│                                                              │
│  ┌───────────────────────────────────────────────────────┐ │
│  │ 发票信息                                              │ │
│  │ ┌─────────────────────────────────────────────────┐   │ │
│  │ │ 发票号: F2024-001                               │   │ │
│  │ │ 日期: 2024-01-15                                │   │ │
│  │ │ 到期日: 2024-02-15                              │   │ │
│  │ │ 金额: 1,250.00 SEK                              │   │ │
│  │ │ OCR: 7350012345678                              │   │ │
│  │ └─────────────────────────────────────────────────┘   │ │
│  └───────────────────────────────────────────────────────┘ │
│                                                              │
│  ┌───────────────────────────────────────────────────────┐ │
│  │ 会计科目                                              │ │
│  │ 借方: 5460 - Kontorsmaterial        1,000.00        │ │
│  │ 借方: 2610 - Ingående moms            250.00        │ │
│  │ 贷方: 2440 - Leverantörsskulder     1,250.00        │ │
│  └───────────────────────────────────────────────────────┘ │
│                                                              │
│  [编辑信息]  [重新识别]  [取消]  [确认并导入到Fortnox]      │
│                                                              │
└─────────────────────────────────────────────────────────────┘
```

---

## UI设计规范

### 7.1 设计原则

**独立Web应用设计原则:**

1. **品牌一致性**: 保持Invoice Master品牌，同时尊重Fortnox用户习惯
2. **简洁高效**: 发票处理是高频操作，界面必须简洁快速
3. **清晰反馈**: OCR识别结果必须清晰展示，便于用户确认
4. **无缝集成**: 虽然是独立应用，但要让用户感觉与Fortnox是一体的

### 7.2 响应式设计

**断点定义:**

| 断点 | 宽度 | 布局 |
|------|------|------|
| Mobile | < 768px | 单列，堆叠布局 |
| Tablet | 768px - 1024px | 双列布局 |
| Desktop | > 1024px | 三列布局 |

### 7.3 组件规范

#### 文件上传区域

```
┌─────────────────────────────────────────────────────────────┐
│ 📤 上传发票                                                  │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  ┌───────────────────────────────────────────────────────┐  │
│  │                                                       │  │
│  │              📄 拖放PDF文件到这里                      │  │
│  │                                                       │  │
│  │                   或点击选择                           │  │
│  │                                                       │  │
│  │         支持格式: PDF, JPG, PNG (最大10MB)            │  │
│  │                                                       │  │
│  └───────────────────────────────────────────────────────┘  │
│                                                              │
└─────────────────────────────────────────────────────────────┘
```

**交互状态:**
- 默认: 灰色边框，虚线
- 悬停: 蓝色边框，背景变浅蓝
- 拖入: 蓝色边框，背景变深蓝
- 上传中: 显示进度条

#### 发票卡片

```
┌─────────────────────────────────────────────────────────────┐
│ 📄 Invoice_F2024_001.pdf                        [✓] [✏️] [🗑️]│
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  供应商: ABC Company (556677-8899)                          │
│  金额: 1,250.00 SEK                                         │
│  日期: 2024-01-15                                           │
│                                                              │
│  状态: ✅ 已导入到Fortnox                                    │
│  凭证: A-1234  [在Fortnox中查看]                             │
│                                                              │
└─────────────────────────────────────────────────────────────┘
```

### 7.4 颜色规范

**主色调:**

| 用途 | 颜色 | Hex |
|------|------|-----|
| 主色 | 蓝色 | #2563EB |
| 成功 | 绿色 | #10B981 |
| 警告 | 黄色 | #F59E0B |
| 错误 | 红色 | #EF4444 |
| 背景 | 浅灰 | #F9FAFB |
| 文字 | 深灰 | #1F2937 |

**Fortnox品牌协调:**
- 使用Fortnox的蓝色作为次要色 (#0057FF)
- 在"导入到Fortnox"按钮中使用Fortnox品牌色

### 7.5 字体规范

| 元素 | 字体 | 大小 | 字重 |
|------|------|------|------|
| 标题 | Inter | 24px | 600 |
| 副标题 | Inter | 18px | 500 |
| 正文 | Inter | 14px | 400 |
| 小字 | Inter | 12px | 400 |
| 数字 | Inter | 16px | 600 (等宽) |

---

## API设计

### 8.1 REST API端点

#### 认证相关

```http
# 获取Fortnox授权URL
GET /api/v1/fortnox/auth/url
Response: {
    "authorization_url": "https://apps.fortnox.se/oauth-v1/auth?...",
    "state": "random_state_string"
}

# OAuth回调处理
GET /api/v1/fortnox/auth/callback?code=xxx&state=xxx
Response: {
    "status": "success",
    "company_name": "My Company AB",
    "connected_at": "2024-01-15T10:30:00Z"
}

# 断开连接
DELETE /api/v1/fortnox/auth
Response: {
    "status": "disconnected"
}
```

#### 发票处理

```http
# 上传并处理发票
POST /api/v1/fortnox/invoices
Content-Type: multipart/form-data
Body: {
    "file": [PDF file],
    "auto_import": false,  // 是否自动导入，false则返回预览
    "settings": {
        "voucher_series": "A",
        "attach_pdf": true
    }
}

Response (预览模式): {
    "id": "uuid",
    "status": "preview",
    "extraction": {
        "supplier_name": "ABC Company",
        "supplier_org_number": "556677-8899",
        "invoice_number": "F2024-001",
        "invoice_date": "2024-01-15",
        "amount_total": 1250.00,
        "ocr_number": "7350012345678"
    },
    "supplier_match": {
        "action": "USE_EXISTING",
        "supplier_number": "123",
        "confidence": 1.0
    },
    "voucher_preview": {
        "rows": [...]
    }
}

Response (自动导入模式): {
    "id": "uuid",
    "status": "imported",
    "voucher": {
        "voucher_number": "1234",
        "series": "A",
        "url": "https://api.fortnox.se/3/vouchers/A/1234"
    },
    "fortnox_url": "https://apps.fortnox.se/..."
}
```

#### 供应商管理

```http
# 获取Fortnox供应商列表
GET /api/v1/fortnox/suppliers
Response: {
    "suppliers": [
        {
            "supplier_number": "123",
            "name": "ABC Company",
            "organisation_number": "556677-8899"
        }
    ]
}

# 创建供应商
POST /api/v1/fortnox/suppliers
Body: {
    "name": "New Supplier",
    "organisation_number": "112233-4455",
    "address": "..."
}
```

### 8.2 Webhook接收

```http
# Fortnox Webhook接收端点
POST /webhooks/fortnox
Headers: {
    "X-Fortnox-Event": "voucher.created"
}
Body: {
    "event": "voucher.created",
    "data": {
        "voucher_number": "1234",
        "series": "A"
    }
}
```

---

## 数据库设计

### 9.1 实体关系图

```
┌─────────────────┐       ┌──────────────────┐       ┌─────────────────┐
│  fortnox_tenants │       │  fortnox_invoices │       │  supplier_cache │
├─────────────────┤       ├──────────────────┤       ├─────────────────┤
│ id (PK)         │◄──────┤ id (PK)          │       │ id (PK)         │
│ organization_id │       │ tenant_id (FK)   │       │ tenant_id (FK)  │
│ access_token    │       │ file_path        │       │ supplier_number │
│ refresh_token   │       │ extraction_data  │       │ name            │
│ expires_at      │       │ voucher_id       │       │ org_number      │
│ company_name    │       │ status           │       │ cached_at       │
│ created_at      │       │ created_at       │       └─────────────────┘
└─────────────────┘       └──────────────────┘
          │
          │         ┌──────────────────┐
          │         │ processing_queue │
          │         ├──────────────────┤
          └────────►│ id (PK)          │
                    │ invoice_id (FK)  │
                    │ status           │
                    │ retry_count      │
                    └──────────────────┘
```

### 9.2 SQL Schema

```sql
-- Fortnox租户表
CREATE TABLE fortnox_tenants (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    organization_id UUID NOT NULL REFERENCES organizations(id),
    
    -- OAuth Tokens
    access_token TEXT NOT NULL,
    refresh_token TEXT NOT NULL,
    expires_at TIMESTAMP NOT NULL,
    scope TEXT,
    
    -- 公司信息
    company_name VARCHAR(255),
    company_org_number VARCHAR(20),
    
    -- 设置
    default_voucher_series VARCHAR(10) DEFAULT 'A',
    default_account_code INTEGER DEFAULT 5460,
    auto_attach_pdf BOOLEAN DEFAULT true,
    
    -- 状态
    is_active BOOLEAN DEFAULT true,
    last_sync_at TIMESTAMP,
    
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW(),
    
    UNIQUE(organization_id)
);

-- Fortnox发票处理记录
CREATE TABLE fortnox_invoices (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES fortnox_tenants(id),
    
    -- 文件信息
    original_filename VARCHAR(255),
    storage_path TEXT,
    file_size INTEGER,
    
    -- OCR提取结果
    extraction_data JSONB,
    extraction_confidence DECIMAL(3,2),
    
    -- 供应商匹配
    supplier_number VARCHAR(50),
    supplier_match_confidence DECIMAL(3,2),
    supplier_match_action VARCHAR(20), -- USE_EXISTING, CREATE_NEW, SUGGEST_MATCH
    
    -- Fortnox凭证
    voucher_series VARCHAR(10),
    voucher_number VARCHAR(50),
    voucher_url TEXT,
    
    -- 处理状态
    status VARCHAR(20) DEFAULT 'pending', -- pending, processing, preview, imported, failed
    error_message TEXT,
    
    -- 用户操作
    reviewed_by UUID,
    reviewed_at TIMESTAMP,
    
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

-- 供应商缓存
CREATE TABLE supplier_cache (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES fortnox_tenants(id),
    
    supplier_number VARCHAR(50) NOT NULL,
    name VARCHAR(255),
    organisation_number VARCHAR(20),
    address TEXT,
    phone VARCHAR(50),
    email VARCHAR(255),
    
    cached_at TIMESTAMP DEFAULT NOW(),
    
    UNIQUE(tenant_id, supplier_number)
);

-- 处理队列
CREATE TABLE processing_queue (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    invoice_id UUID NOT NULL REFERENCES fortnox_invoices(id),
    
    status VARCHAR(20) DEFAULT 'queued', -- queued, processing, completed, failed
    priority INTEGER DEFAULT 5,
    retry_count INTEGER DEFAULT 0,
    max_retries INTEGER DEFAULT 3,
    
    scheduled_at TIMESTAMP DEFAULT NOW(),
    started_at TIMESTAMP,
    completed_at TIMESTAMP,
    error_message TEXT
);

-- 索引
CREATE INDEX idx_fortnox_invoices_tenant ON fortnox_invoices(tenant_id);
CREATE INDEX idx_fortnox_invoices_status ON fortnox_invoices(status);
CREATE INDEX idx_supplier_cache_tenant ON supplier_cache(tenant_id);
CREATE INDEX idx_processing_queue_status ON processing_queue(status);
```

---

## 安全设计

### 10.1 认证安全

**Token存储:**
- Access Token和Refresh Token使用AES-256加密存储
- 加密密钥存储在Azure Key Vault / AWS Secrets Manager
- Token定期轮换

**OAuth安全:**
- 使用state参数防止CSRF攻击
- 强制HTTPS回调
- 授权码一次性使用

### 10.2 数据安全

**传输安全:**
- 所有API通信强制TLS 1.3
- 证书固定(Certificate Pinning)防止中间人攻击

**存储安全:**
- 发票PDF加密存储(AES-256)
- 数据库连接使用SSL
- 敏感字段加密(组织号、银行信息)

### 10.3 访问控制

```python
# 权限检查装饰器
async def require_fortnox_connection(func):
    @wraps(func)
    async def wrapper(request: Request, *args, **kwargs):
        tenant_id = request.headers.get('X-Tenant-ID')
        
        # 检查是否已连接Fortnox
        connection = await get_fortnox_connection(tenant_id)
        if not connection or not connection.is_active:
            raise HTTPException(
                status_code=401,
                detail="Fortnox connection required"
            )
        
        # 检查Token是否有效
        if connection.is_token_expired():
            await refresh_fortnox_token(connection)
        
        return await func(request, *args, **kwargs)
    return wrapper
```

---

## 错误处理

### 11.1 错误分类

| 错误类型 | 示例 | 处理策略 |
|---------|------|----------|
| **认证错误** | Token过期、无效 | 自动刷新或提示重新授权 |
| **API限制** | 429 Too Many Requests | 指数退避重试 |
| **数据错误** | 无效的组织号格式 | 返回具体验证错误 |
| **网络错误** | 连接超时 | 重试3次后失败 |
| **业务错误** | 供应商不存在 | 提供创建选项 |

### 11.2 错误响应格式

```json
{
    "error": {
        "code": "FORTNOX_TOKEN_EXPIRED",
        "message": "Fortnox access token has expired",
        "details": {
            "action": "RECONNECT_REQUIRED",
            "reconnect_url": "/api/v1/fortnox/auth/url"
        },
        "timestamp": "2024-01-15T10:30:00Z",
        "request_id": "req_123456"
    }
}
```

### 11.3 重试策略

```python
class FortnoxAPIRetry:
    """Fortnox API重试策略"""
    
    def __init__(self):
        self.max_retries = 3
        self.base_delay = 1  # 秒
    
    async def execute(self, func, *args, **kwargs):
        for attempt in range(self.max_retries):
            try:
                return await func(*args, **kwargs)
            except FortnoxAPIError as e:
                if e.status_code == 429:  # Rate limit
                    delay = self.base_delay * (2 ** attempt)
                    await asyncio.sleep(delay)
                elif e.status_code in [500, 502, 503, 504]:
                    if attempt < self.max_retries - 1:
                        delay = self.base_delay * (2 ** attempt)
                        await asyncio.sleep(delay)
                    else:
                        raise
                else:
                    raise
```

---

## 开发计划

### 12.1 里程碑

| 阶段 | 时间 | 目标 | 交付物 |
|------|------|------|--------|
| **M1** | Week 1-2 | 基础架构 | 认证模块、数据库 |
| **M2** | Week 3-4 | 核心功能 | 发票处理、供应商匹配 |
| **M3** | Week 5-6 | Fortnox集成 | API集成、凭证创建 |
| **M4** | Week 7-8 | UI开发 | 前端界面、用户流程 |
| **M5** | Week 9-10 | 测试优化 | 测试、性能优化 |
| **M6** | Week 11-12 | 上线准备 | 文档、审核、部署 |

### 12.2 任务分解

**Week 1-2: 基础架构**
- [ ] 创建Fortnox开发者账号
- [ ] 设计数据库Schema
- [ ] 实现OAuth2认证流程
- [ ] Token管理和刷新机制
- [ ] 基础API客户端

**Week 3-4: 核心功能**
- [ ] 集成Invoice Master OCR
- [ ] 实现供应商匹配算法
- [ ] 文件上传和存储
- [ ] 异步处理队列

**Week 5-6: Fortnox集成**
- [ ] 供应商API集成
- [ ] 凭证创建逻辑
- [ ] 文件附件上传
- [ ] 错误处理和重试

**Week 7-8: UI开发**
- [ ] 连接设置页面
- [ ] 发票上传界面
- [ ] 结果预览/编辑页面
- [ ] 历史记录页面

**Week 9-10: 测试优化**
- [ ] 单元测试 (目标80%覆盖率)
- [ ] 集成测试
- [ ] 性能测试
- [ ] 安全审计

**Week 11-12: 上线准备**
- [ ] 用户文档
- [ ] API文档
- [ ] Fortnox审核申请
- [ ] 生产环境部署

---

## 测试策略

### 13.1 测试类型

| 测试类型 | 工具 | 覆盖率目标 | 说明 |
|---------|------|-----------|------|
| **单元测试** | pytest | 80% | 核心逻辑 |
| **集成测试** | pytest + httpx | - | Fortnox API交互 |
| **E2E测试** | Playwright | 核心流程 | 用户场景 |
| **性能测试** | Locust | - | 并发处理 |
| **安全测试** | bandit, safety | - | 漏洞扫描 |

### 13.2 测试用例示例

```python
# 供应商匹配测试
class TestSupplierMatcher:
    async def test_exact_org_number_match(self):
        """测试组织号精确匹配"""
        matcher = FortnoxSupplierMatcher(mock_client)
        
        result = await matcher.match(
            tenant_id="test",
            extraction=ExtractionResult(
                supplier_org_number="556677-8899"
            )
        )
        
        assert result.action == 'USE_EXISTING'
        assert result.confidence == 1.0
    
    async def test_fuzzy_name_match(self):
        """测试名称模糊匹配"""
        result = await matcher.match(
            tenant_id="test",
            extraction=ExtractionResult(
                supplier_name="ABC Company AB"
            )
        )
        
        assert result.confidence > 0.85

# Fortnox API集成测试
class TestFortnoxIntegration:
    async def test_create_voucher(self):
        """测试创建会计凭证"""
        creator = FortnoxVoucherCreator(client)
        
        result = await creator.create_voucher(
            tenant_id="test",
            extraction=mock_extraction,
            supplier_number="123",
            settings=mock_settings
        )
        
        assert result.voucher_id is not None
```

---

## 部署方案

### 14.1 架构部署图

```
┌─────────────────────────────────────────────────────────────┐
│                         Azure                                │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐      │
│  │  Container   │  │  PostgreSQL  │  │     Blob     │      │
│  │   Apps       │  │   Flexible   │  │   Storage    │      │
│  │  (FastAPI)   │  │   Server     │  │              │      │
│  └──────────────┘  └──────────────┘  └──────────────┘      │
│         │                 │                 │               │
│         └─────────────────┼─────────────────┘               │
│                           │                                 │
│                    ┌──────▼──────┐                         │
│                    │    Redis    │                         │
│                    │    Cache    │                         │
│                    └─────────────┘                         │
└─────────────────────────────────────────────────────────────┘
```

### 14.2 资源配置

**Container Apps:**
- CPU: 1 vCPU
- Memory: 2 GiB
- Min replicas: 1
- Max replicas: 5

**PostgreSQL:**
- SKU: Standard_B1ms
- Storage: 32 GB
- Backup: 7 days

**Blob Storage:**
- Tier: Hot
- Redundancy: LRS

### 14.3 部署流程

```bash
# 1. 基础设施部署
az group create --name invoice-master-rg --location swedencentral

# 2. 数据库部署
az postgres flexible-server create \
  --name invoice-master-db \
  --resource-group invoice-master-rg \
  --sku-name Standard_B1ms

# 3. 应用部署
az containerapp create \
  --name invoice-master-fortnox \
  --resource-group invoice-master-rg \
  --image invoicemaster.azurecr.io/fortnox-integration:latest \
  --cpu 1 --memory 2Gi \
  --min-replicas 1 --max-replicas 5
```

---

## 附录

### A. Fortnox API参考

**常用端点速查:**

| 功能 | 方法 | 端点 |
|------|------|------|
| 获取公司信息 | GET | /3/companyinformation |
| 获取供应商列表 | GET | /3/suppliers |
| 创建供应商 | POST | /3/suppliers |
| 获取会计科目 | GET | /3/accounts |
| 创建凭证 | POST | /3/vouchers |
| 上传文件 | POST | /3/inbox |

### B. 科目表参考 (BAS2024)

**常用费用科目:**

| 代码 | 名称 | 说明 |
|------|------|------|
| 2440 | Leverantörsskulder | 应付账款 |
| 2610 | Ingående moms | 进项VAT 25% |
| 2620 | Ingående moms | 进项VAT 12% |
| 2630 | Ingående moms | 进项VAT 6% |
| 5460 | Kontorsmaterial | 办公用品 |
| 5710 | Frakter | 运输费 |
| 6100 | Övriga externa tjänster | 其他外部服务 |
| 6210 | Konsultarvoden | 咨询费 |

### C. 错误代码表

| 代码 | 说明 | HTTP状态 |
|------|------|---------|
| FORTNOX_TOKEN_EXPIRED | Token过期 | 401 |
| FORTNOX_RATE_LIMITED | 请求过于频繁 | 429 |
| SUPPLIER_NOT_FOUND | 供应商不存在 | 404 |
| INVALID_ORG_NUMBER | 无效的组织号 | 400 |
| EXTRACTION_FAILED | OCR提取失败 | 422 |

### D. 相关链接

- [Fortnox Developer Portal](https://developer.fortnox.se/)
- [Fortnox API Docs](https://api.fortnox.se/apidocs)
- [BAS Kontoplan](https://www.bas.se/)

---

**文档版本历史:**

| 版本 | 日期 | 作者 | 变更说明 |
|------|------|------|---------|
| 1.0 | 2026-02-01 | Claude Code | 初始版本，添加Fortnox集成模式说明和UI设计规范 |

---

**审批:**

- [ ] 技术负责人
- [ ] 产品经理
- [ ] 安全团队
