73 lines
1.9 KiB
Python
73 lines
1.9 KiB
Python
|
|
"""
|
|||
|
|
Modèle de données commun pour les tickets de courses.
|
|||
|
|
|
|||
|
|
Toutes les enseignes (Picnic, Leclerc, etc.) produisent
|
|||
|
|
une instance de Receipt après parsing. C'est le format
|
|||
|
|
JSON normalisé en sortie.
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
import json
|
|||
|
|
from dataclasses import dataclass, field, asdict
|
|||
|
|
from datetime import date
|
|||
|
|
from typing import Optional
|
|||
|
|
|
|||
|
|
|
|||
|
|
@dataclass
|
|||
|
|
class Item:
|
|||
|
|
"""Un article sur le ticket de courses."""
|
|||
|
|
|
|||
|
|
name: str
|
|||
|
|
"""Nom du produit tel qu'il apparaît sur le ticket."""
|
|||
|
|
|
|||
|
|
quantity: float
|
|||
|
|
"""Quantité achetée (ex: 2.0, 0.5)."""
|
|||
|
|
|
|||
|
|
unit: str
|
|||
|
|
"""Unité de mesure : 'pièce', 'kg', 'L', 'g', etc."""
|
|||
|
|
|
|||
|
|
unit_price: float
|
|||
|
|
"""Prix unitaire en euros."""
|
|||
|
|
|
|||
|
|
total_price: float
|
|||
|
|
"""Prix total pour cet article (quantity × unit_price)."""
|
|||
|
|
|
|||
|
|
category: Optional[str] = None
|
|||
|
|
"""Catégorie du produit, si disponible (ex: 'Fruits & Légumes')."""
|
|||
|
|
|
|||
|
|
|
|||
|
|
@dataclass
|
|||
|
|
class Receipt:
|
|||
|
|
"""Ticket de courses normalisé, toutes enseignes confondues."""
|
|||
|
|
|
|||
|
|
store: str
|
|||
|
|
"""Nom de l'enseigne : 'picnic' ou 'leclerc'."""
|
|||
|
|
|
|||
|
|
date: date
|
|||
|
|
"""Date de la commande ou de l'achat."""
|
|||
|
|
|
|||
|
|
total: float
|
|||
|
|
"""Montant total payé en euros."""
|
|||
|
|
|
|||
|
|
items: list[Item] = field(default_factory=list)
|
|||
|
|
"""Liste des articles achetés."""
|
|||
|
|
|
|||
|
|
currency: str = "EUR"
|
|||
|
|
"""Devise (EUR par défaut)."""
|
|||
|
|
|
|||
|
|
order_id: Optional[str] = None
|
|||
|
|
"""Identifiant de commande, si disponible."""
|
|||
|
|
|
|||
|
|
delivery_fee: Optional[float] = None
|
|||
|
|
"""Frais de livraison en euros, si applicable (None pour Leclerc)."""
|
|||
|
|
|
|||
|
|
def to_dict(self) -> dict:
|
|||
|
|
"""Convertit le ticket en dictionnaire JSON-sérialisable."""
|
|||
|
|
d = asdict(self)
|
|||
|
|
# La date n'est pas JSON-sérialisable nativement, on la convertit en string ISO
|
|||
|
|
d["date"] = self.date.isoformat()
|
|||
|
|
return d
|
|||
|
|
|
|||
|
|
def to_json(self) -> str:
|
|||
|
|
"""Sérialise le ticket en JSON formaté."""
|
|||
|
|
return json.dumps(self.to_dict(), ensure_ascii=False, indent=2)
|