Feat: Updating Cart Lifecycle Service, and Capping Abandoned Cart Days. Also Updating Cart Views

This commit is contained in:
2026-09-10 01:13:15 +03:00
parent 8f4c1a22ea
commit 864c8b19aa
7 changed files with 296 additions and 86 deletions
+12 -16
View File
@@ -5,7 +5,7 @@ namespace Modules\Core\Cart\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Event;
use Lunar\Models\Cart;
use Modules\Core\Cart\Filament\Resources\CartResource;
use Modules\Core\Cart\Services\CartLifecycleService;
use Modules\Core\Recovery\Events\CartAbandoned;
use Modules\Core\Recovery\Events\CheckoutAbandoned;
@@ -37,7 +37,14 @@ use Modules\Core\Recovery\Events\CheckoutAbandoned;
* sends (Checkout\Services\CheckoutService::setRecoveryConsent() is where
* that consent is actually recorded), and a non-consenting cart's
* abandonment must never be dispatched at all, not merely filtered later
* at send time — see docs referenced above for the legal reasoning.
* at send time — see docs referenced above for the legal reasoning. This
* consent filter stays here rather than on Modules\Core\Cart\Services\
* CartLifecycleService, whose two "abandoned" queries this command builds
* on — dispatch eligibility is this command's own concern, not part of
* what "abandoned" means to a staff member browsing the admin panel. (The
* non-empty-lines requirement, by contrast, IS part of what "abandoned"
* means either way, so it lives on CartLifecycleService::abandonedCarts()
* itself, not here.)
*/
class DetectAbandonedCarts extends Command
{
@@ -45,33 +52,22 @@ class DetectAbandonedCarts extends Command
protected $description = 'Dispatch CartAbandoned/CheckoutAbandoned for carts that just crossed the abandonment threshold.';
public function handle(): void
public function handle(CartLifecycleService $lifecycle): void
{
$cutoff = CartResource::abandonedCutoff();
$cartsAbandoned = 0;
$checkoutsAbandoned = 0;
Cart::query()
->whereDoesntHave('orders')
->where('updated_at', '<=', $cutoff)
$lifecycle->abandonedCarts(Cart::query())
->where('meta->recovery_consent', true)
->with('lines')
->chunkById(200, function ($carts) use (&$cartsAbandoned) {
foreach ($carts as $cart) {
if ($cart->lines->isEmpty()) {
continue;
}
Event::dispatch(new CartAbandoned($cart));
$cartsAbandoned++;
}
});
Cart::query()
->whereHas('orders', fn ($query) => $query->whereNull('placed_at'))
->where('updated_at', '<=', $cutoff)
$lifecycle->abandonedCheckouts(Cart::query())
->where('meta->recovery_consent', true)
->with(['orders' => fn ($query) => $query->whereNull('placed_at')])
->chunkById(200, function ($carts) use (&$checkoutsAbandoned) {