Abo Einmalprodukte: Review-Gate (VIP), Verbindlichkeit & Summen-Layout

- Live-Review-Gate: Einmalprodukte nur fuer VIP im Sales Center sichtbar,
  Portal ausgeblendet (AboHelper::isOneTimeFeatureVisible + Gates in Controllern)
- Nur verbindlich bestaetigte Einmal-Artikel fliessen in die Lieferung;
  Service-Helfer confirmedItems/pendingItems/pendingGross
- Footer-Layout der Einmalprodukt-Liste: bestaetigte Summe + Gesamtbetrag,
  Trennstrich, offener Betrag und neue Gesamtsumme (dunkelgruen)
- Uebersetzungen DE/EN/ES/FR (onetime_new_total u.a.), Tests angepasst/ergaenzt

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Kevin 2026-06-08 14:59:22 +00:00
parent 2269ce031f
commit 8288ea59ac
16 changed files with 356 additions and 46 deletions

View file

@ -158,6 +158,40 @@ class AboOneTimeService
return null;
}
/**
* Verbindlich bestätigte Einmal-Artikel (werden mit der nächsten Lieferung versandt).
*
* @return \Illuminate\Support\Collection<int, UserAboOneTimeItem>
*/
public static function confirmedItems(UserAbo $userAbo): \Illuminate\Support\Collection
{
return $userAbo->one_time_items()
->with('product')
->get()
->filter(fn (UserAboOneTimeItem $item): bool => $item->isConfirmed())
->values();
}
/**
* Noch nicht bestätigte Einmal-Artikel (Entwurf, nicht verbindlich).
*
* @return \Illuminate\Support\Collection<int, UserAboOneTimeItem>
*/
public static function pendingItems(UserAbo $userAbo): \Illuminate\Support\Collection
{
return $userAbo->one_time_items()
->with('product')
->get()
->reject(fn (UserAboOneTimeItem $item): bool => $item->isConfirmed())
->values();
}
public static function pendingGross(UserAbo $userAbo): float
{
return round(self::pendingItems($userAbo)
->sum(fn (UserAboOneTimeItem $item): float => (float) $item->price * (int) $item->qty), 2);
}
public static function hasUnconfirmedChanges(UserAbo $userAbo): bool
{
return UserAboOneTimeItem::withTrashed()