Projet client · Réf. CW-02

My Fruitopia

La cueillette du matin, commandée avant l’heure limite et suivie jusqu’à la porte.

Plateformes

iOS · Android

  • La liste du 16 septembre, avec la date de livraison et une clôture à 14 h 30 au-dessus : mangue Alphonso à 130 Rs avec 30 kg restants, pastèque à la pièce, avocat indiqué en abondance, piment au paquet.
    Liste du jour
  • La commande FR01003 sur une carte d’Ébène Cybercity, l’adresse de livraison épinglée, et une frise en trois étapes — confirmée, en livraison, livrée — montrant que le vendeur n’est pas encore parti.
    Suivi en direct
  • Mangue Alphonso à 120 Rs le kilo, avec une alerte « plus que 30 kg », une note indiquant que le vendeur la vend au kilo, un sélecteur de quantité et un bouton d’ajout portant le total courant.
    Fiche produit
  • Mes commandes, filtre « toutes » : une commande en attente de 275 Rs avec une action de suivi, au-dessus d’une commande livrée de 435 Rs de la veille.
    Mes commandes
Écrans de l’application en fonctionnement, capturés par son propre pilote de tests d’intégration sur une base amorcée en local. Catalogue et commandes inventés : jamais de données de production.

Vendre des fruits et des légumes à la journée n’a rien du commerce de détail. Ce qui existe change chaque matin, cela se vend au kilo ou à la pièce, et cela doit arriver sur un bureau ou devant une porte tant que cela vaut encore la peine d’être acheté. Un catalogue est la mauvaise forme pour ça. Une liste valable un jour est la bonne.

Le rôle de StyloTech

Définition du produit, design d’interface, architecture applicative, application Flutter côté client comme côté vendeur, backend et modèle de données Supabase, règles de commande, de stock et de livraison, suivi en direct, notifications — et le travail de mise en production en cours.

Bientôt sur les stores
  • App Store
  • Google Play

Les deux fiches sont en cours de publication sous le nom actuel de l’application. L’application, elle, tourne déjà.

Site du produitfruitopia.stylotech.mu
Plateformes
  • iOS
  • Android
Construit avec
Application
  • Flutter
  • Dart
  • Cubit
  • go_router
  • get_it
  • Clean Architecture
Backend
  • Supabase
  • PostgreSQL
  • Row Level Security
  • Postgres RPC
  • Supabase Realtime
Infrastructure
  • Firebase Cloud Messaging
  • Supabase Edge Functions
  • Supabase Storage
Ce que StyloTech a livré
  • Stratégie produit
  • Design UI/UX
  • Design system
  • Architecture technique
  • Développement mobile
  • Développement backend
  • Conception de la base de données
  • Intégrations
  • Tests & assurance qualité
  • Mise en production
  • Maintenance

Ce que ça fait

Chaque élément répond à une tâche réelle le jour de l’épreuve, pas à une liste de fonctionnalités. Le site du produit les détaille un par un.

01

La liste du jour

Le vendeur publie ce qui est réellement arrivé le matin, avec sa date de livraison, son heure limite, les régions desservies et les frais de livraison. Rien n’y est en rupture, puisque la liste est le stock.

02

Tarifé dans son unité

Au kilo, au demi-kilo, par paliers de 100 g, ou à la pièce, à la boîte, au paquet, à la douzaine ou au bouquet. Chaque article garde l’unité dans laquelle il se vend au lieu d’être ramené à une seule.

03

Des commandes qui se ferment

L’heure limite et la date de livraison sont à l’écran dès le premier geste. Une fois l’heure passée, la liste n’est plus proposée : personne ne remplit un panier pour une camionnette déjà partie.

04

Une commande par jour de livraison

Commander à nouveau pour le même jour complète la commande déjà ouverte au lieu d’en créer une seconde — une livraison pour le client, un colis pour le vendeur, et une ligne par article sur la liste de préparation.

05

Domicile ou bureau

Une région de livraison, et un immeuble de bureaux lorsque c’est la destination. Choisi une fois et retenu ensuite ; sur une commande fusionnée, c’est le dernier passage en caisse qui décide où elle va.

06

Suivi de livraison en direct

Une fois la camionnette partie : sa position sur une carte, les arrêts restants et une estimation qui bouge avec elle — sans rien rafraîchir.

07

Historique des commandes

Commandes en cours et livrées, avec leurs articles, leurs totaux et leurs dates, pour que recommander l’habituel prenne quelques secondes.

08

Notifications push

La liste du jour qui paraît, une commande confirmée, une livraison qui démarre — on est prévenu plutôt que d’aller voir. Le vendeur choisit les alertes qu’il souhaite.

09

Diffusions

Un message à tous les clients d’une région choisie, rédigé et envoyé depuis le téléphone du vendeur.

10

Stock & prix de revient

Produits, photos, catégories et prix de revient, tenus derrière la liste que voit le client.

11

Préparation de la tournée

Les commandes du jour rassemblées en une tournée par région et par immeuble, puis parcourues arrêt par arrêt, la progression étant enregistrée au fur et à mesure.

12

Chiffre d’affaires & marge

Les recettes, la marge calculée sur le prix de revient figé au moment de la commande, et ce qui s’est réellement vendu.

Un panier, retracé

Un client met une mangue dans son panier et valide sa commande. Voici tout ce qui se passe entre ce geste et la camionnette qui arrive à sa porte.

Relevé dans supabase_checkout_data_source.dart, place_customer_order dans supabase/schema.sql et order_tracking_cubit.dart. Chaque nom ci-dessous apparaît dans le code exactement tel quel.

  1. 01

    La liste est le stock

    Chaque jour de livraison, le vendeur publie une liste : ce qui a été cueilli, à quel prix, ce qu’il en reste, les régions desservies, les frais de livraison et l’heure de clôture. L’accueil du client, c’est cette ligne et les articles qui y sont rattachés — pas un catalogue filtré sur ce qui se trouve être disponible.

    • daily_lists
    • daily_list_items
    • order_deadline
    • delivery_fee
  2. 02

    La commande se ferme à l’heure

    L’heure limite et la date de livraison sont à l’écran dès le premier geste. Une liste dont l’heure est passée est écartée au moment où la vitrine est chargée : elle cesse d’être proposée plutôt que d’échouer à la fin. Personne ne remplit un panier qu’il ne pourra pas valider.

    • order_deadline
    • delivery_date
  3. 03

    La validation est un appel, pas une insertion

    L’application n’écrit jamais dans la table des commandes. Elle confie le panier entier à une seule fonction security definer et laisse la base décider — ce qui fait que la commande, ses lignes et le stock sont soit tous faits, soit aucun.

    • place_customer_order
    • security definer
    Ce qui s’affiche en cas de refus

    « Not authenticated ». La fonction lit le client depuis auth.uid() et refuse net s’il n’y a personne, quoi que l’application ait envoyé.

  4. 04

    Deux gestes ne peuvent pas scinder une commande

    Avant toute lecture, la transaction prend un verrou consultatif indexé sur le client et la date de livraison. Deux validations simultanées pour le même jour verraient sinon toutes deux « aucune commande ouverte » et en inséreraient chacune une — précisément la scission que la fonction existe pour empêcher. La seconde attend, puis trouve la première.

    • pg_advisory_xact_lock
  5. 05

    Une commande ouverte est complétée, jamais doublée

    Une commande en attente et impayée, pour le même vendeur et le même jour, est celle à compléter ; ce qui est livré ou déjà réglé est une affaire close et en ouvre une nouvelle. Le même produit commandé deux fois vient grossir sa ligne au lieu de la répéter, pour que la liste de préparation garde une entrée par article — et l’adresse suit le dernier passage en caisse, parce qu’une commande fusionnée est une seule dépose.

    • orders
    • order_items
    • fulfillment
    • payment
  6. 06

    Le stock se retire de la liste au fil de l’eau

    Chaque ligne décrémente la liste du jour, plancher à zéro pour qu’une course ne la fasse pas passer en négatif. Le « plus que 8 » que lira le client suivant est le nombre que cette commande vient de déplacer.

    • decrement_daily_list_stock
    • daily_list_items
    • stock
  7. 07

    Les totaux sont recalculés, jamais rafistolés

    Une fois les lignes en place, le sous-total est resommé depuis les propres lignes de la commande et les frais de livraison ajoutés une seule fois. Une commande fusionnée ne peut jamais contredire ce qu’elle contient, et la livraison n’est jamais facturée deux fois.

    • subtotal
    • delivery_fee
    • total
  8. 08

    Le vendeur en est averti

    L’insertion de la commande déclenche un trigger et son complément un autre, chacun écrivant une notification — mais seulement si le vendeur a activé cette alerte. L’insertion de cette ligne appelle ensuite une edge function avec son seul identifiant : une requête forgée ne peut choisir ni le destinataire ni le texte, puisque la fonction lit les deux dans la table elle-même.

    • notify_seller_new_order
    • on_order_topped_up
    • notifications
    • push-on-notification
  9. 09

    Puis le client la regarde arriver

    L’écran de suivi s’abonne à deux flux de changements Postgres filtrés sur ce vendeur — la position de la camionnette et la progression de la tournée. La carte bouge quand elle bouge, et un arrêt terminé raccourcit l’estimation immédiatement plutôt qu’au prochain déplacement significatif.

    • seller_locations
    • delivery_progress
    • onPostgresChanges

Détail technique

Relevé dans le code source du projet, pas dans une plaquette commerciale.

Architecture
Clean Architecture orientée fonctionnalités, gestion d’état par Cubit, shells go_router et localisateur de services get_it ; la couche domaine est en Dart pur et renvoie une valeur Result au lieu de lever une exception à la frontière
Backend
Supabase — PostgreSQL avec sécurité au niveau des lignes, fonctions security definer pour la commande et le stock, et une edge function qui envoie le push
Modèle de données
18 migrations versionnées par-dessus le schéma de base, couvrant les listes du jour, les tournées et leur progression, les prix de revient, les frais de livraison, les rappels de panier et le moyen de paiement
Temps réel
Les flux de changements Postgres sur seller_locations et delivery_progress alimentent la carte du client ; la position de la camionnette et les arrêts restants se mettent à jour sans rafraîchissement
Capacités de l’appareil
Rendu cartographique (flutter_map), localisation de l’appareil (geolocator), prise et recadrage de photos produit, notifications locales en complément du push
Diffusion
Version 0.0.15, build 16 à ce jour ; une seule base Flutter pour iOS et Android, en anglais et en français, en thème clair et sombre

La cueillette du matin, commandée avant l’heure limite et suivie jusqu’à la porte.

My Fruitopia — Projet client — StyloTech