# Sales and Aggregation Implementation Plan

## 1. Data Model Proposals

### 1.1 `sag_salgsvarer` Improvements
We will enhance the existing `sag_salgsvarer` table to support full billing requirements, margin calculation, and product linking.

**Current Fields:**
- `id`, `sag_id`, `type` (sale), `description`, `quantity`, `unit`, `unit_price`, `amount`, `currency`, `status`, `line_date`

**Proposed Additions:**
| Field | Type | Description |
|-------|------|-------------|
| `product_id` | INT (FK) | Link to new `products` catalog (nullable) |
| `cost_price` | DECIMAL | For calculating Gross Profit (DB) per line |
| `discount_percent` | DECIMAL | Discount given on standard price |
| `vat_rate` | DECIMAL | Default 25.00 for DK |
| `supplier_id` | INT (FK) | Reference to `vendors` table (if exists) or string |
| `billing_method` | VARCHAR | `invoice`, `prepaid`, `internal` (matches `tmodule_times`) |
| `is_subscription` | BOOLEAN | If true, pushes to subscription system instead of one-off invoice |

### 1.2 New `products` Table
A central catalog for standard items (Hardware, Licenses, Fees) to speed up entry and standardize reporting.

```sql
CREATE TABLE products (
    id SERIAL PRIMARY KEY,
    sku VARCHAR(50) UNIQUE,
    name VARCHAR(255) NOT NULL,
    description TEXT,
    category VARCHAR(50),       -- 'hardware', 'license', 'consulting'
    cost_price DECIMAL(10,2),
    sales_price DECIMAL(10,2),  -- Suggested RRP
    unit VARCHAR(20) DEFAULT 'stk',
    supplier_id INTEGER,
    is_active BOOLEAN DEFAULT TRUE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
```

### 1.3 Aggregation Rules
The system will distinguish between **Direct** costs/revenue (on the case itself) and **Aggregated** (from sub-cases).

- **Direct Revenue** = (Sum of `sag_salgsvarer.amount`) + (Sum of `tmodule_times` where `billable=true` * `hourly_rate`)
- **Total Revenue** = Direct Revenue + Sum(Child Cases Total Revenue)

## 2. UI Structure for "Varer" (Items) Tab

The "Varer" tab on the Case Detail page will have a split entry/view design.

### 2.1 Top Section: Quick Add
A horizontal form to quickly add lines:
- **Product Lookup**: Searchable dropdown.
- **Manual Override**: Description field auto-filled but editable.
- **Numbers**: Qty, Unit, Price.
- **Result**: Total Price auto-calculated.
- **Action**: "Add Line" button.

### 2.2 Main List: Combined Billing View
A unified table showing everything billable on this case:

| Type | Date | Description | Qty | Price | Disc | Total | Status | Actions |
|------|------|-------------|-----|-------|------|-------|--------|---------|
| 🕒 Time | 02-02 | Konsulentbistand | 2.5 | 1200 | 0% | 3000 | `Approved` | [Edit Time] |
| 📦 Item | 02-02 | Ubiquiti Switch | 1 | 2500 | 10% | 2250 | `Draft` | [Edit] [Del] |
| 🔄 Sub | -- | *Sub-case: Installation i Aarhus* | -- | -- | -- | 5400 | `Calculated` | [Go to Case] |

### 2.3 Summary Footer (Sticky)
- **Materials**: Total of Items.
- **Labor**: Total of Time.
- **Sub-cases**: Total of Children.
- **Grand Total**: Ex VAT and Inc VAT.
- **Margin**: (Sales - Cost) / Sales %.
- **Action**: "Create Invoice Proposal" button.

## 3. Aggregation Logic (Recursive)

We will implement a `SalesAggregator` service that traverses the case tree.

**Algorithm:**
1. **Inputs**: `case_id`.
2. **Fetch Direct Items**: Query `sag_salgsvarer` for this case.
3. **Fetch Direct Time**: Query `tmodule_times` for this case. Calculate value using `hourly_rate`.
4. **Fetch Children**: Query `sag_relationer` (or `sag_sager` parent_id) to find children.
5. **Recursion**: For each child, recursively call `get_case_totals(child_id)`.
6. **Summation**: Return object with `own_total` and `sub_total`.

**Python Service Method:**
```python
def get_case_financials(case_id: int) -> CaseFinancials:
    # 1. Own items
    items = db.query(SagSalgsvarer).filter(sag_id=case_id).all()
    item_total = sum(i.amount for i in items)
    item_cost = sum(i.cost_price * i.quantity for i in items)

    # 2. Own time
    times = db.query(TmoduleTimes).filter(case_id=case_id, billable=True).all()
    time_total = sum(t.original_hours * get_hourly_rate(case_id) for t in times)

    # 3. Children
    children = db.query(SagRelationer).filter(kilde_sag_id=case_id).all()
    child_total = 0
    child_cost = 0

    for child in children:
        child_fin = get_case_financials(child.malsag_id)
        child_total += child_fin.total_revenue
        child_cost += child_fin.total_cost

    return CaseFinancials(
        revenue=item_total + time_total + child_total,
        cost=item_cost + child_cost,
        # ... breakdown fields
    )
```

## 4. Preparation for Billing (Status Flow)

We define a strict lifecycle for items to prevent double-billing.

### 4.1 Status Lifecycle for Items (`sag_salgsvarer`)
1.  **`draft`**: Default. Editable. Included in Preliminary Total.
2.  **`approved`**: Locked by Project Manager. Ready for Finance.
    - *Action*: Lock for Billing.
    - *Effect*: Rows become read-only.
3.  **`billed`**: Processed by Finance (exported to e-conomic).
    - *Action*: Integration Job runs.
    - *Effect*: Linked to `invoice_id` (new column).

### 4.2 Billing Triggers
- **Partial Billing**: Checkbox select specific `approved` lines -> Create Invoice Draft.
- **Full Billing**: Bill All Approved -> Generates invoice for all `approved` items and time.
- **Aggregation Billing**:
  - The invoicing engine must accept a `case_structure` to decide if it prints one line per sub-case or expands all lines. Default to **One line per sub-case** for cleanliness.

### 4.3 Validation
- Ensure all Approved items have a valid `cost_price` (warn if 0).
- Ensure Time Registrations are `approved` before they can be billed.
