Metadata-Version: 2.4
Name: fin-core
Version: 0.2.0
Summary: Brique réutilisable tiers & backbone financier (CDC-COCKPIT-FIN-v2) — embarquée par chaque app, données par app.
Author: Holding MAPFARM
License: Proprietary
Keywords: consolidation,finance,mapfarm,tiers
Requires-Python: >=3.11
Requires-Dist: pydantic>=2
Provides-Extra: db
Requires-Dist: psycopg[binary]>=3; extra == 'db'
Provides-Extra: test
Requires-Dist: psycopg[binary]>=3; extra == 'test'
Requires-Dist: pytest>=8; extra == 'test'
Requires-Dist: testcontainers[postgres]>=4; extra == 'test'
Description-Content-Type: text/markdown

# fin-core

**Brique logicielle réutilisable** — gestion des **tiers** et **backbone financier** (banque/trésorerie,
facturation réelle/analytique, comptabilité analytique) + **contrat de consolidation** vers le Cockpit.

Spécifiée par `CDC-COCKPIT-FIN-v2` (Holding MAPFARM). Chaque application du portefeuille (Harvest, Factory,
futures apps) **embarque** la brique et **possède ses propres données** (store par app) ; le Cockpit
**consolide** une vue holding par read-model HTTP et **n'écrit jamais** dans les stores des apps.

> Ce dépôt = **Livrable 0** du CDC : la brique seule (modèle générique §5, moteur de règles par
> `pattern_flux` §4, facturation/compta §6, contrat de consolidation §3.3). Le câblage consommateur
> (Harvest L1, Cockpit L2, Factory L3, pass-through/Connect L4, bascules cible L5) est hors de ce dépôt.

## Principes (non négociables — CDC §7)

| # | Règle |
|---|---|
| R1 | Pas de facture interne en mono-entité → **allocation analytique**, jamais facture. |
| R2 | Flottant de tiers ≠ trésorerie propre → **passif**, jamais « disponible ». |
| R3 | Le `pattern_flux` (`payable`/`receivable`/`pass_through`) détermine flux, flottant, facturation → **un seul moteur**. |
| R4 | TVA paramétrable par entité (`franchise` art. 293 B ↔ `assujetti`). |
| R5 | Tout objet financier tagué `entite_id` + `marque_id`. |
| R6 | Stripe Connect = module activable pour le pattern `pass_through` (cible). |
| R8 | Chaque app est **propriétaire** de ses données tiers/finance. |
| R9 | Contrat de consolidation **standardisé** émis vers le Cockpit. |
| R10 | **Séparabilité** : aucune donnée opérationnelle d'une app dans un store partagé en écriture. |

Calculs **Decimal-first** (`ROUND_HALF_UP`, 2 décimales), **HT canonique, EUR**.

## Installation (registre PyPI Gitea)

`--extra-index-url` (et non `--index-url`) : la brique vient du registre Gitea, ses
dépendances (pydantic, psycopg…) restent résolues depuis PyPI.

```bash
pip install --extra-index-url https://git.mapfarm.cloud/api/packages/mapfarm/pypi/simple/ fin-core
# avec l'adaptateur de persistance psycopg :
pip install --extra-index-url https://git.mapfarm.cloud/api/packages/mapfarm/pypi/simple/ "fin-core[db]"
```

## Usage

```python
from decimal import Decimal
from fin_core import Entite, Tiers, RegimeTVA, PatternFlux, RulesEngine
from fin_core.consolidation import build_report

ent = Entite(id="mapfarm", type="auto_entrepreneur", regime_tva=RegimeTVA.FRANCHISE)
tiers = Tiers(id="prod-1", marque_id="harvest", type="producteur", pattern_flux=PatternFlux.PASS_THROUGH)

engine = RulesEngine()
plan = engine.plan_flux(tiers, montant=Decimal("100.00"))   # → flottant si pass_through (R2/R3)
```

Commission **agent** (pass-through, §4.1 — grille palier × phase sur le GMV) :

```python
from fin_core import settle_agent, Palier, Phase, phase_for

phase = phase_for(months_elapsed=3)          # temporel 12 mois → LANCEMENT
s = settle_agent(tiers=agent_tiers, entite_id="harvest", gmv=Decimal("100.00"),
                 palier=Palier.PREMIUM, phase=phase)
# s.commission = 22.00 (retenue) · s.reversement = 78.00 · s.dette = flottant DetteTiers
```

Persistance (chaque app, sur **son** schéma) :

```python
import psycopg
from fin_core.schema import apply_schema

with psycopg.connect(dsn, autocommit=True) as conn:
    apply_schema(conn, "xavier__harvest__finance")   # DDL idempotent, store de l'app
```

## Dev

```bash
make install   # pip install -e ".[test]"
make lint      # ruff
make test      # pytest (DDL idempotent skip si pas de Docker)
make build     # sdist + wheel
```
