From 7262617897eebe074bca4ade7ff30863beb96aea Mon Sep 17 00:00:00 2001 From: Codex Agent Date: Thu, 5 Feb 2026 10:54:05 +0100 Subject: [PATCH] Refactor checkout health resource --- .../Pages/ListTenantCheckoutHealths.php | 16 ++ .../Tables/TenantCheckoutHealthTable.php} | 163 ++++-------------- .../TenantCheckoutHealthResource.php | 121 +++++++++++++ .../Pages/ListTenantLemonSqueezyHealths.php | 16 -- .../TenantLemonSqueezyHealthResource.php | 121 ------------- resources/lang/de/admin.php | 4 +- resources/lang/en/admin.php | 4 +- tests/Unit/SuperAdminNavigationGroupsTest.php | 4 +- .../Unit/TenantCheckoutHealthResourceTest.php | 91 ++++++++++ 9 files changed, 266 insertions(+), 274 deletions(-) create mode 100644 app/Filament/Clusters/DailyOps/Resources/TenantCheckoutHealths/Pages/ListTenantCheckoutHealths.php rename app/Filament/Clusters/DailyOps/Resources/{TenantLemonSqueezyHealths/Tables/TenantLemonSqueezyHealthTable.php => TenantCheckoutHealths/Tables/TenantCheckoutHealthTable.php} (56%) create mode 100644 app/Filament/Clusters/DailyOps/Resources/TenantCheckoutHealths/TenantCheckoutHealthResource.php delete mode 100644 app/Filament/Clusters/DailyOps/Resources/TenantLemonSqueezyHealths/Pages/ListTenantLemonSqueezyHealths.php delete mode 100644 app/Filament/Clusters/DailyOps/Resources/TenantLemonSqueezyHealths/TenantLemonSqueezyHealthResource.php create mode 100644 tests/Unit/TenantCheckoutHealthResourceTest.php diff --git a/app/Filament/Clusters/DailyOps/Resources/TenantCheckoutHealths/Pages/ListTenantCheckoutHealths.php b/app/Filament/Clusters/DailyOps/Resources/TenantCheckoutHealths/Pages/ListTenantCheckoutHealths.php new file mode 100644 index 00000000..5c963a2c --- /dev/null +++ b/app/Filament/Clusters/DailyOps/Resources/TenantCheckoutHealths/Pages/ListTenantCheckoutHealths.php @@ -0,0 +1,16 @@ +label(__('admin.tenants.fields.contact_email')) ->searchable() ->toggleable(isToggledHiddenByDefault: true), - TextColumn::make('lemonsqueezy_customer_id') - ->label('Lemon Squeezy customer') - ->toggleable(isToggledHiddenByDefault: true) - ->copyable() - ->formatStateUsing(fn (?string $state) => $state ?: '—'), TextColumn::make('subscription_status') ->label('Subscription') ->badge() @@ -56,134 +48,77 @@ class TenantLemonSqueezyHealthTable ->badge() ->color(fn (string $state) => $state === '—' ? 'gray' : 'success') ->toggleable(isToggledHiddenByDefault: true), - TextColumn::make('lemonsqueezy_subscription_id') - ->label('Lemon Squeezy subscription') - ->toggleable(isToggledHiddenByDefault: true) - ->copyable() - ->getStateUsing(fn (Tenant $record) => $record->activeResellerPackage?->lemonsqueezy_subscription_id) - ->formatStateUsing(fn (?string $state) => $state ?: '—'), - IconColumn::make('missing_lemonsqueezy_subscription') - ->label('Missing Lemon Squeezy subscription') - ->boolean() - ->getStateUsing(fn (Tenant $record) => self::missingLemonSqueezySubscription($record)), IconColumn::make('status_mismatch') ->label('Status mismatch') ->boolean() ->getStateUsing(fn (Tenant $record) => self::hasStatusMismatch($record)), - TextColumn::make('lemonsqueezy_customer_duplicates') - ->label('Lemon Squeezy duplicates') - ->sortable() - ->toggleable(isToggledHiddenByDefault: true) - ->formatStateUsing(fn (?int $state) => $state && $state > 1 ? (string) $state : '—'), - TextColumn::make('lemonsqueezy_sync_status') - ->label('Lemon Squeezy sync') - ->badge() - ->color(fn (?string $state) => match ($state) { - 'synced' => 'success', - 'syncing' => 'warning', - 'pulled' => 'info', - 'dry-run' => 'gray', - 'failed', 'pull-failed' => 'danger', - default => 'gray', - }) - ->formatStateUsing(fn (?string $state) => $state ? Str::headline($state) : '—') - ->getStateUsing(fn (Tenant $record) => $record->activeResellerPackage?->package?->lemonsqueezy_sync_status) - ->toggleable(isToggledHiddenByDefault: true), - TextColumn::make('lemonsqueezy_synced_at') - ->label('Lemon Squeezy synced') - ->badge() - ->color(fn ($state) => self::syncAgeColor($state)) - ->formatStateUsing(fn ($state) => $state?->diffForHumans() ?? '—') - ->getStateUsing(fn (Tenant $record) => $record->activeResellerPackage?->package?->lemonsqueezy_synced_at), - TextColumn::make('last_lemonsqueezy_transaction_at') - ->label('Last Lemon Squeezy tx') + TextColumn::make('last_checkout_transaction_at') + ->label('Last transaction') ->badge() ->color(fn (?Carbon $state) => self::transactionAgeColor($state)) - ->getStateUsing(fn (Tenant $record) => $record->last_lemonsqueezy_transaction_at - ? Carbon::parse($record->last_lemonsqueezy_transaction_at) + ->getStateUsing(fn (Tenant $record) => $record->last_checkout_transaction_at + ? Carbon::parse($record->last_checkout_transaction_at) : null) ->formatStateUsing(fn (?Carbon $state) => $state?->diffForHumans() ?? '—') ->toggleable(isToggledHiddenByDefault: true), - TextColumn::make('lemonsqueezy_transaction_count_window') - ->label('Lemon Squeezy tx (30d)') + TextColumn::make('checkout_transaction_count_window') + ->label('Transactions (30d)') ->default('0') ->sortable() ->toggleable(), - TextColumn::make('lemonsqueezy_transaction_total_window') - ->label('Lemon Squeezy total (30d)') + TextColumn::make('checkout_transaction_total_window') + ->label('Total (30d)') ->default(0) ->money('EUR') ->sortable() ->toggleable(), - TextColumn::make('lemonsqueezy_refund_count_window') + TextColumn::make('checkout_refund_count_window') ->label('Refunds (30d)') ->badge() ->color(fn (?int $state) => $state && $state > 0 ? 'danger' : 'gray') ->default('0') ->sortable() ->toggleable(isToggledHiddenByDefault: true), - TextColumn::make('lemonsqueezy_refund_total_window') + TextColumn::make('checkout_refund_total_window') ->label('Refund total (30d)') ->default(0) ->money('EUR') ->sortable() ->toggleable(isToggledHiddenByDefault: true), - TextColumn::make('lemonsqueezy_checkout_requires_action_count') + TextColumn::make('checkout_requires_action_count') ->label('Checkout action required') ->badge() ->color(fn (?int $state) => $state && $state > 0 ? 'warning' : 'gray') ->default('0') ->sortable() ->toggleable(isToggledHiddenByDefault: true), - TextColumn::make('lemonsqueezy_checkout_processing_count') + TextColumn::make('checkout_processing_count') ->label('Checkout processing') ->badge() ->color(fn (?int $state) => $state && $state > 0 ? 'warning' : 'gray') ->default('0') ->sortable() ->toggleable(isToggledHiddenByDefault: true), - TextColumn::make('lemonsqueezy_checkout_expired_count') + TextColumn::make('checkout_expired_count') ->label('Checkout expired') ->badge() ->color(fn (?int $state) => $state && $state > 0 ? 'danger' : 'gray') ->default('0') ->sortable() ->toggleable(isToggledHiddenByDefault: true), - TextColumn::make('lemonsqueezy_transaction_count') - ->label('Lemon Squeezy tx (all)') + TextColumn::make('checkout_transaction_count') + ->label('Transactions (all)') ->default('0') ->sortable() ->toggleable(isToggledHiddenByDefault: true), - TextColumn::make('lemonsqueezy_transaction_total') - ->label('Lemon Squeezy total (all)') + TextColumn::make('checkout_transaction_total') + ->label('Total (all)') ->default(0) ->money('EUR') ->sortable() ->toggleable(isToggledHiddenByDefault: true), ]) ->filters([ - Filter::make('missing_lemonsqueezy_customer') - ->label('Missing Lemon Squeezy customer') - ->indicator('Missing Lemon Squeezy customer') - ->query(fn (Builder $query) => $query->whereNull('lemonsqueezy_customer_id')), - Filter::make('missing_lemonsqueezy_subscription') - ->label('Missing Lemon Squeezy subscription') - ->indicator('Missing Lemon Squeezy subscription') - ->query(fn (Builder $query) => $query->whereHas('activeResellerPackage', fn (Builder $query) => $query - ->where('active', true) - ->whereNull('lemonsqueezy_subscription_id'))), - Filter::make('duplicate_lemonsqueezy_customer') - ->label('Duplicate Lemon Squeezy customer') - ->indicator('Duplicate Lemon Squeezy customer') - ->query(fn (Builder $query) => $query - ->whereNotNull('lemonsqueezy_customer_id') - ->whereIn('lemonsqueezy_customer_id', function ($subquery) { - $subquery->select('lemonsqueezy_customer_id') - ->from('tenants') - ->whereNotNull('lemonsqueezy_customer_id') - ->groupBy('lemonsqueezy_customer_id') - ->havingRaw('count(*) > 1'); - })), Filter::make('status_mismatch') ->label('Status mismatch') ->indicator('Status mismatch') @@ -205,39 +140,24 @@ class TenantLemonSqueezyHealthTable ->where('is_suspended', false) ->whereNull('pending_deletion_at') ->whereNull('anonymized_at')), - Filter::make('lemonsqueezy_sync_failed') - ->label('Lemon Squeezy sync failed') - ->indicator('Lemon Squeezy sync failed') - ->query(fn (Builder $query) => $query->whereHas('activeResellerPackage.package', fn (Builder $query) => $query - ->whereIn('lemonsqueezy_sync_status', self::FAILED_SYNC_STATUSES))), - Filter::make('lemonsqueezy_sync_stale') - ->label('Lemon Squeezy sync stale') - ->indicator('Lemon Squeezy sync stale') - ->query(fn (Builder $query) => $query->whereHas('activeResellerPackage.package', fn (Builder $query) => $query - ->whereNotNull('lemonsqueezy_synced_at') - ->where('lemonsqueezy_synced_at', '<', now()->subDays(TenantLemonSqueezyHealthResource::STALE_SYNC_DAYS)))), - Filter::make('lemonsqueezy_sync_missing') - ->label('Missing Lemon Squeezy sync timestamp') - ->indicator('Missing Lemon Squeezy sync timestamp') - ->query(fn (Builder $query) => $query->whereHas('activeResellerPackage.package', fn (Builder $query) => $query - ->whereNull('lemonsqueezy_synced_at'))), - Filter::make('lemonsqueezy_transaction_stale') - ->label('Stale Lemon Squeezy transactions') - ->indicator('Stale Lemon Squeezy transactions') + Filter::make('checkout_transaction_stale') + ->label('Stale transactions') + ->indicator('Stale transactions') ->query(function (Builder $query): Builder { - $cutoff = now()->subDays(TenantLemonSqueezyHealthResource::TRANSACTION_WINDOW_DAYS); + $cutoff = now()->subDays(TenantCheckoutHealthResource::TRANSACTION_WINDOW_DAYS); + $provider = TenantCheckoutHealthResource::provider(); return $query - ->whereHas('purchases', fn (Builder $query) => $query->where('provider', 'lemonsqueezy')) + ->whereHas('purchases', fn (Builder $query) => $query->where('provider', $provider)) ->whereDoesntHave('purchases', fn (Builder $query) => $query - ->where('provider', 'lemonsqueezy') + ->where('provider', $provider) ->where('purchased_at', '>=', $cutoff)); }), Filter::make('checkout_attention') ->label('Checkout attention') ->indicator('Checkout attention') ->query(fn (Builder $query) => $query->whereHas('checkoutSessions', function (Builder $query) { - $query->where('provider', 'lemonsqueezy') + $query->where('provider', TenantCheckoutHealthResource::provider()) ->where(function (Builder $query) { $query->whereIn('status', [ CheckoutSession::STATUS_REQUIRES_CUSTOMER_ACTION, @@ -274,10 +194,11 @@ class TenantLemonSqueezyHealthTable return $query; } - $cutoff = now()->subDays(TenantLemonSqueezyHealthResource::TRANSACTION_WINDOW_DAYS); + $cutoff = now()->subDays(TenantCheckoutHealthResource::TRANSACTION_WINDOW_DAYS); + $provider = TenantCheckoutHealthResource::provider(); return $query->whereHas('purchases', fn (Builder $query) => $query - ->where('provider', 'lemonsqueezy') + ->where('provider', $provider) ->where('refunded', true) ->where('purchased_at', '>=', $cutoff), '>=', $min); }), @@ -314,13 +235,6 @@ class TenantLemonSqueezyHealthTable return false; } - private static function missingLemonSqueezySubscription(Tenant $record): bool - { - $package = $record->activeResellerPackage; - - return $package && $package->active && ! $package->lemonsqueezy_subscription_id; - } - private static function applyStatusMismatchFilter(Builder $query): Builder { return $query->where(function (Builder $query) { @@ -338,26 +252,13 @@ class TenantLemonSqueezyHealthTable }); } - private static function syncAgeColor($state): string - { - if (! $state) { - return 'gray'; - } - - if ($state->lt(now()->subDays(TenantLemonSqueezyHealthResource::STALE_SYNC_DAYS))) { - return 'danger'; - } - - return 'success'; - } - private static function transactionAgeColor(?Carbon $state): string { if (! $state) { return 'gray'; } - if ($state->lt(now()->subDays(TenantLemonSqueezyHealthResource::TRANSACTION_WINDOW_DAYS))) { + if ($state->lt(now()->subDays(TenantCheckoutHealthResource::TRANSACTION_WINDOW_DAYS))) { return 'danger'; } diff --git a/app/Filament/Clusters/DailyOps/Resources/TenantCheckoutHealths/TenantCheckoutHealthResource.php b/app/Filament/Clusters/DailyOps/Resources/TenantCheckoutHealths/TenantCheckoutHealthResource.php new file mode 100644 index 00000000..945c0633 --- /dev/null +++ b/app/Filament/Clusters/DailyOps/Resources/TenantCheckoutHealths/TenantCheckoutHealthResource.php @@ -0,0 +1,121 @@ +subDays(self::TRANSACTION_WINDOW_DAYS); + + return parent::getEloquentQuery() + ->with(['activeResellerPackage.package']) + ->withExists('activeResellerPackage as has_active_reseller_package') + ->withCount([ + 'purchases as checkout_transaction_count' => fn (Builder $query) => $query + ->where('provider', $provider) + ->where('refunded', false), + 'purchases as checkout_transaction_count_window' => fn (Builder $query) => $query + ->where('provider', $provider) + ->where('refunded', false) + ->where('purchased_at', '>=', $windowStart), + 'purchases as checkout_refund_count_window' => fn (Builder $query) => $query + ->where('provider', $provider) + ->where('refunded', true) + ->where('purchased_at', '>=', $windowStart), + 'checkoutSessions as checkout_requires_action_count' => fn (Builder $query) => $query + ->where('provider', $provider) + ->where('status', CheckoutSession::STATUS_REQUIRES_CUSTOMER_ACTION), + 'checkoutSessions as checkout_processing_count' => fn (Builder $query) => $query + ->where('provider', $provider) + ->where('status', CheckoutSession::STATUS_PROCESSING), + 'checkoutSessions as checkout_expired_count' => fn (Builder $query) => $query + ->where('provider', $provider) + ->whereNotIn('status', [ + CheckoutSession::STATUS_COMPLETED, + CheckoutSession::STATUS_CANCELLED, + ]) + ->whereNotNull('expires_at') + ->where('expires_at', '<', now()), + ]) + ->withSum([ + 'purchases as checkout_transaction_total' => fn (Builder $query) => $query + ->where('provider', $provider) + ->where('refunded', false), + ], 'price') + ->withSum([ + 'purchases as checkout_transaction_total_window' => fn (Builder $query) => $query + ->where('provider', $provider) + ->where('refunded', false) + ->where('purchased_at', '>=', $windowStart), + ], 'price') + ->withSum([ + 'purchases as checkout_refund_total_window' => fn (Builder $query) => $query + ->where('provider', $provider) + ->where('refunded', true) + ->where('purchased_at', '>=', $windowStart), + ], 'price') + ->withMax([ + 'purchases as last_checkout_transaction_at' => fn (Builder $query) => $query + ->where('provider', $provider), + ], 'purchased_at'); + } + + public static function getPages(): array + { + return [ + 'index' => ListTenantCheckoutHealths::route('/'), + ]; + } + + public static function provider(): string + { + return (string) config('checkout.default_provider', self::DEFAULT_PROVIDER); + } +} diff --git a/app/Filament/Clusters/DailyOps/Resources/TenantLemonSqueezyHealths/Pages/ListTenantLemonSqueezyHealths.php b/app/Filament/Clusters/DailyOps/Resources/TenantLemonSqueezyHealths/Pages/ListTenantLemonSqueezyHealths.php deleted file mode 100644 index 50be0ad9..00000000 --- a/app/Filament/Clusters/DailyOps/Resources/TenantLemonSqueezyHealths/Pages/ListTenantLemonSqueezyHealths.php +++ /dev/null @@ -1,16 +0,0 @@ -subDays(self::TRANSACTION_WINDOW_DAYS); - - return parent::getEloquentQuery() - ->with(['activeResellerPackage.package']) - ->withExists('activeResellerPackage as has_active_reseller_package') - ->addSelect([ - 'lemonsqueezy_customer_duplicates' => Tenant::query() - ->selectRaw('count(*)') - ->whereColumn('lemonsqueezy_customer_id', 'tenants.lemonsqueezy_customer_id') - ->whereNotNull('lemonsqueezy_customer_id'), - ]) - ->withCount([ - 'purchases as lemonsqueezy_transaction_count' => fn (Builder $query) => $query - ->where('provider', 'lemonsqueezy') - ->where('refunded', false), - 'purchases as lemonsqueezy_transaction_count_window' => fn (Builder $query) => $query - ->where('provider', 'lemonsqueezy') - ->where('refunded', false) - ->where('purchased_at', '>=', $windowStart), - 'purchases as lemonsqueezy_refund_count_window' => fn (Builder $query) => $query - ->where('provider', 'lemonsqueezy') - ->where('refunded', true) - ->where('purchased_at', '>=', $windowStart), - 'checkoutSessions as lemonsqueezy_checkout_requires_action_count' => fn (Builder $query) => $query - ->where('provider', CheckoutSession::PROVIDER_LEMONSQUEEZY) - ->where('status', CheckoutSession::STATUS_REQUIRES_CUSTOMER_ACTION), - 'checkoutSessions as lemonsqueezy_checkout_processing_count' => fn (Builder $query) => $query - ->where('provider', CheckoutSession::PROVIDER_LEMONSQUEEZY) - ->where('status', CheckoutSession::STATUS_PROCESSING), - 'checkoutSessions as lemonsqueezy_checkout_expired_count' => fn (Builder $query) => $query - ->where('provider', CheckoutSession::PROVIDER_LEMONSQUEEZY) - ->whereNotIn('status', [ - CheckoutSession::STATUS_COMPLETED, - CheckoutSession::STATUS_CANCELLED, - ]) - ->whereNotNull('expires_at') - ->where('expires_at', '<', now()), - ]) - ->withSum([ - 'purchases as lemonsqueezy_transaction_total' => fn (Builder $query) => $query - ->where('provider', 'lemonsqueezy') - ->where('refunded', false), - ], 'price') - ->withSum([ - 'purchases as lemonsqueezy_transaction_total_window' => fn (Builder $query) => $query - ->where('provider', 'lemonsqueezy') - ->where('refunded', false) - ->where('purchased_at', '>=', $windowStart), - ], 'price') - ->withSum([ - 'purchases as lemonsqueezy_refund_total_window' => fn (Builder $query) => $query - ->where('provider', 'lemonsqueezy') - ->where('refunded', true) - ->where('purchased_at', '>=', $windowStart), - ], 'price') - ->withMax([ - 'purchases as last_lemonsqueezy_transaction_at' => fn (Builder $query) => $query - ->where('provider', 'lemonsqueezy'), - ], 'purchased_at'); - } - - public static function getPages(): array - { - return [ - 'index' => ListTenantLemonSqueezyHealths::route('/'), - ]; - } -} diff --git a/resources/lang/de/admin.php b/resources/lang/de/admin.php index a69dd57f..14e86a22 100644 --- a/resources/lang/de/admin.php +++ b/resources/lang/de/admin.php @@ -186,9 +186,9 @@ return [ 'deleted' => 'Gelöscht', ], ], - 'lemonsqueezy_health' => [ + 'checkout_health' => [ 'navigation' => [ - 'label' => 'PayPal-Status', + 'label' => 'Checkout-Status', ], ], 'integrations_health' => [ diff --git a/resources/lang/en/admin.php b/resources/lang/en/admin.php index c7460fff..ddde63d2 100644 --- a/resources/lang/en/admin.php +++ b/resources/lang/en/admin.php @@ -186,9 +186,9 @@ return [ 'deleted' => 'Deleted', ], ], - 'lemonsqueezy_health' => [ + 'checkout_health' => [ 'navigation' => [ - 'label' => 'PayPal health', + 'label' => 'Checkout health', ], ], 'integrations_health' => [ diff --git a/tests/Unit/SuperAdminNavigationGroupsTest.php b/tests/Unit/SuperAdminNavigationGroupsTest.php index 7b8f68af..5563a7c7 100644 --- a/tests/Unit/SuperAdminNavigationGroupsTest.php +++ b/tests/Unit/SuperAdminNavigationGroupsTest.php @@ -33,7 +33,7 @@ class SuperAdminNavigationGroupsTest extends TestCase \App\Filament\Resources\PackageResource::class => 'admin.nav.commercial', \App\Filament\Resources\PhotoboothSettings\PhotoboothSettingResource::class => 'admin.nav.storage', \App\Filament\Resources\PurchaseResource::class => 'admin.nav.billing', - \App\Filament\Clusters\DailyOps\Resources\TenantLemonSqueezyHealths\TenantLemonSqueezyHealthResource::class => 'admin.nav.billing', + \App\Filament\Clusters\DailyOps\Resources\TenantCheckoutHealths\TenantCheckoutHealthResource::class => 'admin.nav.billing', \App\Filament\Resources\PurchaseHistoryResource::class => 'admin.nav.commercial', \App\Filament\Resources\EventPurchaseResource::class => 'admin.nav.commercial', \App\Filament\Resources\TenantPackageResource::class => 'admin.nav.commercial', @@ -57,7 +57,7 @@ class SuperAdminNavigationGroupsTest extends TestCase \App\Filament\Resources\PhotoResource::class => DailyOpsCluster::class, \App\Filament\Resources\TenantResource::class => DailyOpsCluster::class, \App\Filament\Resources\PurchaseResource::class => DailyOpsCluster::class, - \App\Filament\Clusters\DailyOps\Resources\TenantLemonSqueezyHealths\TenantLemonSqueezyHealthResource::class => DailyOpsCluster::class, + \App\Filament\Clusters\DailyOps\Resources\TenantCheckoutHealths\TenantCheckoutHealthResource::class => DailyOpsCluster::class, \App\Filament\Resources\TenantFeedbackResource::class => DailyOpsCluster::class, \App\Filament\SuperAdmin\Pages\OpsHealthDashboard::class => DailyOpsCluster::class, \App\Filament\SuperAdmin\Pages\IntegrationsHealthDashboard::class => DailyOpsCluster::class, diff --git a/tests/Unit/TenantCheckoutHealthResourceTest.php b/tests/Unit/TenantCheckoutHealthResourceTest.php new file mode 100644 index 00000000..18286cab --- /dev/null +++ b/tests/Unit/TenantCheckoutHealthResourceTest.php @@ -0,0 +1,91 @@ +create(); + $package = Package::factory()->endcustomer()->paid()->create(); + + PackagePurchase::factory()->create([ + 'tenant_id' => $tenant->id, + 'package_id' => $package->id, + 'provider' => CheckoutSession::PROVIDER_PAYPAL, + 'price' => 100.00, + 'refunded' => false, + 'purchased_at' => now()->subDay(), + ]); + + PackagePurchase::factory()->create([ + 'tenant_id' => $tenant->id, + 'package_id' => $package->id, + 'provider' => CheckoutSession::PROVIDER_LEMONSQUEEZY, + 'price' => 50.00, + 'refunded' => false, + 'purchased_at' => now()->subDay(), + ]); + + CheckoutSession::query()->create([ + 'id' => (string) Str::uuid(), + 'tenant_id' => $tenant->id, + 'package_id' => $package->id, + 'package_snapshot' => [ + 'id' => $package->id, + 'name' => $package->name, + 'price' => $package->price, + 'currency' => $package->currency ?? 'EUR', + ], + 'status' => CheckoutSession::STATUS_PROCESSING, + 'provider' => CheckoutSession::PROVIDER_PAYPAL, + 'status_history' => [], + 'currency' => $package->currency ?? 'EUR', + 'amount_subtotal' => 100.00, + 'amount_total' => 100.00, + 'amount_discount' => 0.00, + ]); + + CheckoutSession::query()->create([ + 'id' => (string) Str::uuid(), + 'tenant_id' => $tenant->id, + 'package_id' => $package->id, + 'package_snapshot' => [ + 'id' => $package->id, + 'name' => $package->name, + 'price' => $package->price, + 'currency' => $package->currency ?? 'EUR', + ], + 'status' => CheckoutSession::STATUS_REQUIRES_CUSTOMER_ACTION, + 'provider' => CheckoutSession::PROVIDER_LEMONSQUEEZY, + 'status_history' => [], + 'currency' => $package->currency ?? 'EUR', + 'amount_subtotal' => 50.00, + 'amount_total' => 50.00, + 'amount_discount' => 0.00, + ]); + + $tenantWithCounts = TenantCheckoutHealthResource::getEloquentQuery() + ->whereKey($tenant->id) + ->firstOrFail(); + + $this->assertSame(1, (int) $tenantWithCounts->checkout_transaction_count); + $this->assertSame(1, (int) $tenantWithCounts->checkout_transaction_count_window); + $this->assertSame(0, (int) $tenantWithCounts->checkout_refund_count_window); + $this->assertSame(100.00, (float) $tenantWithCounts->checkout_transaction_total); + $this->assertSame(100.00, (float) $tenantWithCounts->checkout_transaction_total_window); + $this->assertSame(1, (int) $tenantWithCounts->checkout_processing_count); + $this->assertSame(0, (int) $tenantWithCounts->checkout_requires_action_count); + } +}