Aller au contenu principal

Gestion des Acomptes et Historique de Paiement

Vue d'ensemble

Cette fonctionnalité permet de gérer les paiements partiels (acomptes) et de tracer l'historique complet des paiements pour chaque commande.

Structure de la base de données

Table payment_transactions

Nouvelle table pour stocker l'historique des transactions de paiement :

- id: UUID (clé primaire)
- commande_id: UUID (référence à la commande)
- user_id: UUID (propriétaire)
- montant: DECIMAL(10,2)
- devise: VARCHAR(3) (EUR par défaut)
- type: ENUM ('acompte', 'solde', 'partiel', 'total')
- label: TEXT (ex: "Acompte", "Livraison", "Solde restant")
- payment_method: VARCHAR(50) ('card', 'cash', 'transfer', 'other')
- payment_link_id: UUID (lien vers payment_links si applicable)
- status: ENUM ('pending', 'completed', 'failed', 'refunded', 'cancelled')
- paid_at: TIMESTAMPTZ
- created_at: TIMESTAMPTZ
- updated_at: TIMESTAMPTZ
- notes: TEXT
- stripe_payment_intent_id: TEXT

Modifications de la table commandes

Ajout du champ :

- acompte_montant: DECIMAL(10,2) (montant d'acompte requis, optionnel)

Architecture du code

Entités

  1. PaymentTransaction (lib/features/calendar/domain/entities/payment_transaction.dart)

    • Représente une transaction de paiement
    • Propriétés calculées : isPaid, isPending, isEffective, montantFormate
  2. PaymentTransactionStatus (enum)

    • pending: En attente
    • completed: Payé ✅
    • failed: Échoué ❌
    • refunded: Remboursé ↩️
    • cancelled: Annulé 🚫
  3. PaymentTransactionType (enum)

    • acompte: Acompte 💰
    • solde: Solde 💵
    • partiel: Paiement partiel 📝
    • total: Paiement total ✅

Modifications de l'entité Commande

Nouvelles propriétés :

final double? acompteMontant;
final List<PaymentTransaction>? transactions;

Nouvelles méthodes calculées :

bool get acompteRequis          // Si un acompte est défini
double get montantPaye // Somme des transactions payées
double get montantRestant // Prix total - montant payé
bool get acomptePaye // Si l'acompte a été payé
bool get totalementPaye // Si tout est payé
double get pourcentagePaye // % de paiement
String get montantRestantFormate
String get montantPayeFormate

Repository

PaymentTransactionRepository avec implémentation Supabase :

Méthodes principales :

  • getTransactionsByCommande(commandeId): Récupère toutes les transactions d'une commande
  • createTransaction(...): Crée une nouvelle transaction
  • updateTransactionStatus(...): Met à jour le statut d'une transaction
  • markTransactionAsPaid(...): Marque une transaction comme payée
  • getTotalPaidAmount(commandeId): Calcule le montant total payé
  • getPendingTransactions(commandeId): Récupère les transactions en attente

UI

PaymentHistoryWidget (lib/features/calendar/presentation/widgets/payment_history_widget.dart)

Widget réutilisable qui affiche :

  • ✅ Un résumé du statut de paiement (Payé / Acompte payé / En attente)
  • 📊 Une barre de progression du paiement
  • 📋 La liste complète des transactions avec :
    • Label personnalisé
    • Montant
    • Statut (avec emoji et couleur)
    • Type de transaction
    • Date de paiement
    • Méthode de paiement
    • Notes

Cas d'usage

Scénario 1 : Sans acompte

Commande commande = Commande(
id: '123',
titre: 'Gâteau anniversaire',
prix: 100.0,
// acompteMontant est null (pas d'acompte)
);

// Créer une transaction pour le paiement total
PaymentTransaction transaction = await repository.createTransaction(
commandeId: '123',
montant: 100.0,
label: 'Paiement total',
type: PaymentTransactionType.total,
);

// Marquer comme payé
await repository.markTransactionAsPaid(transactionId: transaction.id);

Scénario 2 : Avec acompte

Commande commande = Commande(
id: '456',
titre: 'Gâteau mariage',
prix: 150.0,
acompteMontant: 50.0, // Acompte de 50€ requis
);

// 1. Créer la transaction d'acompte
PaymentTransaction acompte = await repository.createTransaction(
commandeId: '456',
montant: 50.0,
label: 'Acompte',
type: PaymentTransactionType.acompte,
paymentMethod: 'card',
);

// 2. Marquer l'acompte comme payé
await repository.markTransactionAsPaid(transactionId: acompte.id);

// Vérifier l'acompte
print(commande.acomptePaye); // true
print(commande.montantPaye); // 50.0
print(commande.montantRestant); // 100.0
print(commande.totalementPaye); // false

// 3. Plus tard, créer la transaction pour le solde
PaymentTransaction solde = await repository.createTransaction(
commandeId: '456',
montant: 100.0,
label: 'Livraison',
type: PaymentTransactionType.solde,
paymentMethod: 'cash',
);

// 4. Marquer le solde comme payé
await repository.markTransactionAsPaid(transactionId: solde.id);

// Vérifier le paiement complet
print(commande.montantPaye); // 150.0
print(commande.totalementPaye); // true

Scénario 3 : Paiements multiples

Commande commande = Commande(
id: '789',
titre: 'Gâteau entreprise',
prix: 300.0,
acompteMontant: 100.0,
);

// Créer plusieurs transactions
await repository.createTransaction(
commandeId: '789',
montant: 100.0,
label: 'Acompte',
type: PaymentTransactionType.acompte,
);

await repository.createTransaction(
commandeId: '789',
montant: 100.0,
label: 'Deuxième versement',
type: PaymentTransactionType.partiel,
);

await repository.createTransaction(
commandeId: '789',
montant: 100.0,
label: 'Solde final',
type: PaymentTransactionType.solde,
);

// L'historique complet sera visible dans le PaymentHistoryWidget

Intégration avec le chat IA

Pour permettre au chat IA d'envoyer des demandes de paiement :

1. Lors de la création de commande

// L'agent IA peut demander si un acompte est souhaité
"Souhaitez-vous demander un acompte ? (Oui/Non)"

// Si oui, demander le montant
"Quel montant d'acompte ? (suggéré: 30% = 45€)"

// Créer la commande avec l'acompte
Commande commande = Commande(
titre: 'Gâteau anniversaire',
prix: 150.0,
acompteMontant: 45.0,
);

2. Envoi du lien de paiement

// Créer une transaction pour l'acompte
PaymentTransaction transaction = await repository.createTransaction(
commandeId: commande.id,
montant: commande.acompteMontant!,
label: 'Acompte',
type: PaymentTransactionType.acompte,
paymentMethod: 'card',
);

// Créer un lien de paiement Stripe
// (existant dans payment_links)
PaymentLink link = await createStripePaymentLink(
amount: transaction.montant,
description: transaction.label,
);

// Lier la transaction au payment_link
await repository.updateTransaction(
transaction.copyWith(paymentLinkId: link.id),
);

// Envoyer via WhatsApp
"Bonjour, voici le lien pour payer l'acompte de 45€ : [lien]"

3. Webhook de confirmation

// Quand Stripe confirme le paiement
Future<void> onPaymentConfirmed(String paymentIntentId) async {
// Trouver la transaction liée
final transaction = await repository.getTransactionByPaymentIntent(paymentIntentId);

// Marquer comme payée
await repository.markTransactionAsPaid(
transactionId: transaction.id,
stripePaymentIntentId: paymentIntentId,
);

// Envoyer une confirmation via le chat IA
"Paiement de ${transaction.montantFormate} confirmé pour ${transaction.label} ✅"
}

Migrations à exécuter

Pour activer cette fonctionnalité, exécuter ces migrations dans l'ordre :

  1. supabase/migrations/create_payment_transactions.sql
  2. supabase/migrations/add_acompte_to_commandes.sql

Utilisation dans l'UI

Dans la page de détails de commande :

import 'package:kazacalendar_mobile/features/calendar/presentation/widgets/payment_history_widget.dart';

// Dans le build method
PaymentHistoryWidget(commande: commande)

Le widget affichera automatiquement :

  • L'historique complet si des transactions existent
  • Un message "Aucun paiement enregistré" sinon
  • Une barre de progression du paiement
  • Le statut global (Payé / Acompte payé / En attente)

Avantages de cette approche

  1. Flexibilité maximale : N'importe quel nombre de paiements partiels
  2. Traçabilité complète : Chaque paiement est enregistré avec tous les détails
  3. Compatible avec l'existant : Les commandes sans acompte fonctionnent normalement
  4. Calculs automatiques : Montant payé, restant, pourcentage calculés dynamiquement
  5. Intégration facile : Repository prêt à l'emploi avec toutes les méthodes nécessaires
  6. UI moderne : Widget réutilisable avec barre de progression et emojis

Prochaines étapes

Pour compléter l'intégration :

  1. Ajouter le PaymentHistoryWidget dans commande_details_page.dart
  2. Créer une interface pour ajouter manuellement des paiements (espèces, virement)
  3. Intégrer avec le chat IA pour l'envoi automatique de liens de paiement
  4. Ajouter des notifications quand un paiement est reçu
  5. Permettre l'édition/suppression de transactions en attente