feat(billing): renewals, coupons, VAT numbering, billing cycles and invoice payment tracking
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
ea9a53f742
commit
25f23d541e
@@ -173,4 +173,86 @@ class ServiceExpiryTest extends TestCase
|
||||
$invoice = Invoice::find($service->refresh()->invoice_id);
|
||||
$this->assertSame(90_000, (int) $invoice->total);
|
||||
}
|
||||
|
||||
public function test_auto_renew_service_is_paid_from_balance_and_extended(): void
|
||||
{
|
||||
$service = $this->createExpiringService(90_000);
|
||||
$service->update(['auto_renew' => true]);
|
||||
$service->customer->update(['total' => 200_000]);
|
||||
$oldExpiry = $service->expires_at->copy();
|
||||
|
||||
$this->artisan('service:expiry')->assertExitCode(0);
|
||||
|
||||
$service->refresh();
|
||||
$this->assertSame(InvoiceStatus::PAID->value, Invoice::find($service->invoice_id)->status);
|
||||
$this->assertTrue($service->expires_at->greaterThan($oldExpiry));
|
||||
$this->assertSame(110_000, (int) $service->customer->fresh()->total);
|
||||
|
||||
$log = \App\Models\ActivityLog::where('subject_id', $service->id)->where('event', 'renewed')->first();
|
||||
$this->assertSame('system', $log->causer_type);
|
||||
$this->assertSame('balance', $log->new_values['payment_method']);
|
||||
}
|
||||
|
||||
public function test_auto_renew_with_insufficient_balance_keeps_invoice_unpaid_and_retries_after_topup(): void
|
||||
{
|
||||
$service = $this->createExpiringService(90_000);
|
||||
$service->update(['auto_renew' => true]);
|
||||
$service->customer->update(['total' => 10_000]);
|
||||
|
||||
$this->artisan('service:expiry')->assertExitCode(0);
|
||||
$this->assertSame(InvoiceStatus::UNPAID->value, Invoice::find($service->refresh()->invoice_id)->status);
|
||||
$this->assertSame(10_000, (int) $service->customer->fresh()->total);
|
||||
|
||||
$service->customer->update(['total' => 100_000]);
|
||||
$this->artisan('service:expiry')->assertExitCode(0);
|
||||
$this->assertSame(InvoiceStatus::PAID->value, Invoice::find($service->invoice_id)->status);
|
||||
}
|
||||
|
||||
public function test_service_without_auto_renew_is_not_charged(): void
|
||||
{
|
||||
$service = $this->createExpiringService(90_000);
|
||||
$service->customer->update(['total' => 200_000]);
|
||||
|
||||
$this->artisan('service:expiry')->assertExitCode(0);
|
||||
|
||||
$this->assertSame(InvoiceStatus::UNPAID->value, Invoice::find($service->refresh()->invoice_id)->status);
|
||||
$this->assertSame(200_000, (int) $service->customer->fresh()->total);
|
||||
}
|
||||
|
||||
public function test_renewal_invoice_creates_customer_notification_and_queues_email(): void
|
||||
{
|
||||
\Illuminate\Support\Facades\Queue::fake();
|
||||
\Illuminate\Support\Facades\DB::table('setting')->updateOrInsert(['key' => 'email_settings'], ['value' => json_encode(['auto_disabled' => []])]);
|
||||
$service = $this->createExpiringService(90_000);
|
||||
|
||||
$this->artisan('service:expiry')->assertExitCode(0);
|
||||
|
||||
$invoice = Invoice::find($service->refresh()->invoice_id);
|
||||
$notification = \App\Models\Notification::where('recipient_type', 'client')
|
||||
->where('recipient_id', $service->customer_id)->first();
|
||||
$this->assertNotNull($notification);
|
||||
$this->assertSame('renewal_invoice', $notification->type);
|
||||
$this->assertStringContainsString($invoice->invoice_number, $notification->body);
|
||||
$this->assertStringContainsString('?invoice=' . $invoice->invoice_number, $notification->link);
|
||||
|
||||
\Illuminate\Support\Facades\Queue::assertPushed(
|
||||
\App\Jobs\Mail\SendInvoiceMailJob::class,
|
||||
fn ($job) => $job->invoiceId === $invoice->id && $job->type === 'renewal',
|
||||
);
|
||||
|
||||
$this->artisan('service:expiry')->assertExitCode(0);
|
||||
$this->assertSame(1, \App\Models\Notification::where('recipient_id', $service->customer_id)->count());
|
||||
}
|
||||
|
||||
public function test_email_is_skipped_when_invoice_notification_is_auto_disabled(): void
|
||||
{
|
||||
\Illuminate\Support\Facades\Queue::fake();
|
||||
\Illuminate\Support\Facades\DB::table('setting')->updateOrInsert(['key' => 'email_settings'], ['value' => json_encode(['auto_disabled' => ['invoice_notification']])]);
|
||||
$service = $this->createExpiringService(90_000);
|
||||
|
||||
$this->artisan('service:expiry')->assertExitCode(0);
|
||||
|
||||
$this->assertSame(1, \App\Models\Notification::where('recipient_id', $service->customer_id)->count());
|
||||
\Illuminate\Support\Facades\Queue::assertNotPushed(\App\Jobs\Mail\SendInvoiceMailJob::class);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user