---
name: s11-expense-claims-toPrepare
scenario: S11
description: Notes de frais en attente de review (toPrepare) — le filtre composé AND (payableType + bookkeepingStatus) peut échouer; workaround via filtre simple + post-filtrage sur le champ state
tags: [payables, expense-claim, bookkeeping, toPrepare, compound-filter]
when_to_use: Quand on veut lister les notes de frais employés non encore traitées en comptabilité (en attente de review pour export ERP)
risk: P2 — Le filtre composé AND combinant payableType et bookkeepingStatus peut retourner une erreur serveur ("Unable to get payables"). Workaround : filtrer sur un seul champ, puis post-filtrer sur le champ `state` du payable retourné.
---

# Instructions — Notes de frais non traitées (expenseClaim + toPrepare)

## ⚠️ Filtre composé AND : risque d'erreur serveur

Un filtre `AND` combinant `payableType=expenseClaim` et `bookkeepingStatus=toPrepare` peut retourner `"Unable to get payables"` (erreur serveur — comportement observé sur ce MCP).

**Pattern robuste recommandé** :
1. Filtrer sur un seul critère (le plus sélectif en premier)
2. Post-filtrer sur le champ `state` des payables retournés

## ⚠️ Champ `state` = valeur de bookkeepingStatus

Le payable retourné expose le statut comptable via `state` (pas `bookkeepingStatus`) :
- `state: "toPrepare"` → non encore traité
- `state: "toExport"` → prêt pour l'ERP
- `state: "exported"` → déjà exporté

## Approche recommandée (filtre simple + post-filtrage)

### Option A — Filtrer par payableType, post-filtrer par state

```json
// Appel 1 : get_payables avec payableType seulement
{
  "companyId": "...",
  "filters": { "field": "payableType", "operator": "=", "value": ["expenseClaim"] },
  "pageSize": 100
}
```
→ Post-filtrer côté client : `payables.filter(p => p.state === "toPrepare")`

Paginer si `meta.pagination.hasNextPage=true` (utiliser `cursor` pour la page suivante).

### Option B — Filtrer par bookkeepingStatus, post-filtrer par payableType

```json
// Appel 1 : get_payables avec bookkeepingStatus seulement
{
  "companyId": "...",
  "filters": { "field": "bookkeepingStatus", "operator": "=", "value": ["toPrepare"] },
  "pageSize": 100
}
```
→ Post-filtrer côté client : `payables.filter(p => p.payableType === "expenseClaim")`

**Option B préférée** quand le nombre total de `toPrepare` est faible — la liste `toPrepare` est généralement plus petite que la liste totale des notes de frais.

## Champs clés du payable expenseClaim

| Champ | Type | Description |
|-------|------|-------------|
| `id` | string | ID unique de la note de frais |
| `state` | string | Statut comptable : `toPrepare` \| `toExport` \| `exported` |
| `payableType` | string | `"expenseClaim"` |
| `memberId` | string | ID de l'employé demandeur (= `counterparty.memberId`) |
| `description` | string | Description de la dépense |
| `amount` | {amount, currency, precision} | Montant total (coefficient entier, diviser par 10^precision) |
| `spendingAmount` | {amount, currency, precision} | Montant dans la devise d'origine (peut différer de `amount`) |
| `spendingCurrency` | string | Devise de la dépense originale (peut être MAD, GBP, USD, etc.) |
| `functionalAmount` | {amount, currency, precision} | Équivalent en devise fonctionnelle (EUR) |
| `itemLines[].analyticalFieldAssociations` | array | Cost center assigné + champs analytiques |
| `allocations[].amount` | amount | Montant déjà remboursé à l'employé |
| `documentaryEvidence.validity.valid` | bool | Justificatif valide |
| `creationDate` | ISO datetime | Date de la dépense déclarée |
| `firstCreatedAt` | ISO datetime | Date d'entrée dans Spendesk |

## ⚠️ Cas de devises multiples

Une note de frais peut être en devise locale (MAD, GBP, USD…) :
- `spendingCurrency` = devise de la dépense
- `currency` = `functionalCurrency` = EUR (en général)
- `spendingExchangeRate` = taux de conversion appliqué
- Toujours utiliser `functionalAmount` pour des comparaisons EUR

Exemple réel :
```json
{
  "currency": "EUR",
  "spendingCurrency": "MAD",
  "functionalExchangeRate": 1,
  "spendingExchangeRate": 10.8821,
  "amount": { "amount": 1213, "currency": "EUR", "precision": 2 },
  "spendingAmount": { "amount": 13200, "currency": "MAD", "precision": 2 }
}
// Dépense de 132.00 MAD = 12.13 EUR
```

## ⚠️ allocations présentes même sur toPrepare

Les notes de frais `toPrepare` ont souvent des `allocations[]` non vides — le remboursement à l'employé (paiement) est déjà effectué, mais la pièce n'a pas encore été validée pour export ERP. Paiement ≠ export comptable.

## Exemple live (données réelles, 20 premières notes de frais sans filtre de statut)

Sur les 20 premières `expenseClaim` retournées :
- 11 en `toPrepare` (à traiter)
- 9 en `exported` (déjà en compta)
- `hasNextPage: true` → il en existe davantage

Exemples `toPrepare` :
| Description | Montant | Requérant |
|-------------|---------|-----------|
| "Dinner" | 14,10 EUR | `__zal3grncttvu` |
| "Billet de train aller 08/01/2023" | 84,00 EUR | `-8zlmfu9gm8-ag` |
| "flight to Berlin Feb 21st 2022" | 248,67 EUR | `HJHszqYeW` |
| "Spendesk Connect 2022" | 69,00 EUR | `9a74l4w9ams5lv` |
| "Breakfast for Raji and I" | 12,13 EUR (= 132 MAD) | `-88mp0xaj8r_r_` |

## Exemple de prompts

- "Liste les notes de frais en attente de review" → payableType=expenseClaim + post-filtrage state=toPrepare
- "Quelles notes de frais dois-je traiter avant la clôture ?" → bookkeepingStatus=toPrepare + post-filtrage payableType=expenseClaim
- "Notes de frais en devises étrangères non exportées" → expenseClaim + toPrepare + spendingCurrency != EUR
- "Montant total des NDF en attente" → sum(functionalAmount) sur expenseClaim où state=toPrepare
