Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ tasks:

db:seed:
desc: Seed the Billing module database
cmd: php artisan modules:seed --module=billing
cmd: '{{.APP}} php artisan modules:seed --module=billing'

# ── Code Generation ────────────────────────────────────────────

types:generate:
desc: Generate TypeScript types from PHP DTOs and enums
cmd: php artisan module:generate-types billing
cmd: '{{.APP}} php artisan module:generate-types billing'
44 changes: 24 additions & 20 deletions src/Filament/Widgets/ConversionChartWidget.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,27 +77,31 @@ protected function getData(): array
{
$buckets = $this->buildMonthlyBuckets();

$rows = CheckoutSession::whereBetween('created_at', [$this->startDate, $this->endDate])
->whereIn('status', [
CheckoutSessionStatus::Completed->value,
CheckoutSessionStatus::Abandoned->value,
CheckoutSessionStatus::Expired->value,
])
->selectRaw(
'DATE_FORMAT(created_at, "%Y-%m") as month, SUM(status = ?) as completed, COUNT(*) as total',
[CheckoutSessionStatus::Completed->value]
)
->groupBy('month')
->orderBy('month')
->get();

foreach ($rows as $row) {
$month = (string) $row->getAttribute('month');
$total = (int) $row->getAttribute('total');
$completed = (int) $row->getAttribute('completed');
if (array_key_exists($month, $buckets) && $total > 0) {
$buckets[$month] = round($completed / $total * 100, 1);
try {
$rows = CheckoutSession::whereBetween('created_at', [$this->startDate, $this->endDate])
->whereIn('status', [
CheckoutSessionStatus::Completed->value,
CheckoutSessionStatus::Abandoned->value,
CheckoutSessionStatus::Expired->value,
])
->selectRaw(
'DATE_FORMAT(created_at, "%Y-%m") as month, SUM(status = ?) as completed, COUNT(*) as total',
[CheckoutSessionStatus::Completed->value]
)
->groupBy('month')
->orderBy('month')
->get();

foreach ($rows as $row) {
$month = (string) $row->getAttribute('month');
$total = (int) $row->getAttribute('total');
$completed = (int) $row->getAttribute('completed');
if (array_key_exists($month, $buckets) && $total > 0) {
$buckets[$month] = round($completed / $total * 100, 1);
}
}
} catch (\Throwable $e) {
report($e);
}

$labels = array_map(
Expand Down
26 changes: 15 additions & 11 deletions src/Filament/Widgets/RevenueChartWidget.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,18 +59,22 @@ protected function getData(): array
{
$buckets = $this->buildMonthlyBuckets();

$rows = Payment::where('status', PaymentStatus::Succeeded)
->whereBetween('created_at', [$this->startDate, $this->endDate])
->selectRaw('DATE_FORMAT(created_at, "%Y-%m") as month, SUM(amount) as total')
->groupBy('month')
->orderBy('month')
->get();

foreach ($rows as $row) {
$month = (string) $row->getAttribute('month');
if (array_key_exists($month, $buckets)) {
$buckets[$month] = (int) $row->getAttribute('total');
try {
$rows = Payment::where('status', PaymentStatus::Succeeded)
->whereBetween('created_at', [$this->startDate, $this->endDate])
->selectRaw('DATE_FORMAT(created_at, "%Y-%m") as month, SUM(amount) as total')
->groupBy('month')
->orderBy('month')
->get();

foreach ($rows as $row) {
$month = (string) $row->getAttribute('month');
if (array_key_exists($month, $buckets)) {
$buckets[$month] = (int) $row->getAttribute('total');
}
}
} catch (\Throwable $e) {
report($e);
}

$labels = array_map(
Expand Down
24 changes: 14 additions & 10 deletions src/Filament/Widgets/SubscriptionsChartWidget.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,17 +58,21 @@ protected function getData(): array
{
$buckets = $this->buildMonthlyBuckets();

$rows = Subscription::whereBetween('created_at', [$this->startDate, $this->endDate])
->selectRaw('DATE_FORMAT(created_at, "%Y-%m") as month, COUNT(*) as total')
->groupBy('month')
->orderBy('month')
->get();

foreach ($rows as $row) {
$month = (string) $row->getAttribute('month');
if (array_key_exists($month, $buckets)) {
$buckets[$month] = (int) $row->getAttribute('total');
try {
$rows = Subscription::whereBetween('created_at', [$this->startDate, $this->endDate])
->selectRaw('DATE_FORMAT(created_at, "%Y-%m") as month, COUNT(*) as total')
->groupBy('month')
->orderBy('month')
->get();

foreach ($rows as $row) {
$month = (string) $row->getAttribute('month');
if (array_key_exists($month, $buckets)) {
$buckets[$month] = (int) $row->getAttribute('total');
}
}
} catch (\Throwable $e) {
report($e);
}

$labels = array_map(
Expand Down
4 changes: 2 additions & 2 deletions tests/Feature/BillingServiceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ protected function setUp(): void

$manager = $this->createMock(PaymentGatewayManager::class);
$manager->method('driver')->willReturn($this->gateway);
$this->app->instance(PaymentGatewayManager::class, $manager);
app()->instance(PaymentGatewayManager::class, $manager);

$this->billingService = $this->app->make(BillingService::class);
$this->billingService = app()->make(BillingService::class);
}

public function test_process_checkout_creates_customer(): void
Expand Down
2 changes: 1 addition & 1 deletion tests/Feature/CheckoutControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ protected function setUp(): void

$manager = $this->createMock(PaymentGatewayManager::class);
$manager->method('driver')->willReturn($gateway);
$this->app->instance(PaymentGatewayManager::class, $manager);
app()->instance(PaymentGatewayManager::class, $manager);
}

public function test_checkout_requires_authentication(): void
Expand Down
2 changes: 1 addition & 1 deletion tests/Feature/CheckoutSessionValidationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ protected function setUp(): void

$manager = $this->createMock(PaymentGatewayManager::class);
$manager->method('driver')->willReturn($gateway);
$this->app->instance(PaymentGatewayManager::class, $manager);
app()->instance(PaymentGatewayManager::class, $manager);
}

public function test_show_rejects_expired_checkout_session(): void
Expand Down
4 changes: 2 additions & 2 deletions tests/Feature/DatabaseSeederTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ class DatabaseSeederTest extends TestCase

public function test_module_database_seeder_runs(): void
{
$this->seed(DatabaseSeeder::class);
$this->expectNotToPerformAssertions();

$this->assertTrue(true);
$this->seed(DatabaseSeeder::class);
}
}
2 changes: 1 addition & 1 deletion tests/Feature/SubscriptionCancelTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ protected function setUp(): void

$manager = $this->createMock(PaymentGatewayManager::class);
$manager->method('driver')->willReturn($this->gateway);
$this->app->instance(PaymentGatewayManager::class, $manager);
app()->instance(PaymentGatewayManager::class, $manager);
}

public function test_cancel_subscription_requires_auth(): void
Expand Down
2 changes: 1 addition & 1 deletion tests/Feature/SubscriptionResumeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ protected function setUp(): void

$manager = $this->createMock(PaymentGatewayManager::class);
$manager->method('driver')->willReturn($this->gateway);
$this->app->instance(PaymentGatewayManager::class, $manager);
app()->instance(PaymentGatewayManager::class, $manager);
}

public function test_resume_subscription_requires_auth(): void
Expand Down
4 changes: 2 additions & 2 deletions tests/Feature/WebhookIdempotencyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ protected function setUp(): void

$manager = $this->createMock(PaymentGatewayManager::class);
$manager->method('driver')->willReturn($this->gateway);
$this->app->instance(PaymentGatewayManager::class, $manager);
app()->instance(PaymentGatewayManager::class, $manager);

$this->billingService = $this->app->make(BillingService::class);
$this->billingService = app()->make(BillingService::class);
}

public function test_duplicate_webhook_event_is_skipped(): void
Expand Down
4 changes: 2 additions & 2 deletions tests/Support/BillingTestHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public static function createSubscriberFixtures(): void
// --- Active subscriber ---
$subscriber = User::firstOrCreate(
['email' => 'subscriber@example.com'],
['name' => 'Subscriber User', 'password' => Hash::make('secretsauce'), 'email_verified_at' => now()],
['name' => 'Subscriber User', 'password' => Hash::make('password'), 'email_verified_at' => now()],
);

$subscriber->assignRole('user');
Expand All @@ -56,7 +56,7 @@ public static function createSubscriberFixtures(): void
// --- Cancelled subscriber (pending cancellation) ---
$cancelled = User::firstOrCreate(
['email' => 'cancelled@example.com'],
['name' => 'Cancelled User', 'password' => Hash::make('secretsauce'), 'email_verified_at' => now()],
['name' => 'Cancelled User', 'password' => Hash::make('password'), 'email_verified_at' => now()],
);

$cancelled->assignRole('user');
Expand Down
Loading