- Nạp tiền: mã thanh toán sống 30 phút (ManualDepositDraft), khôi phục/huỷ mã, lệnh dọn mã hết hạn; total_pending chỉ tính giao dịch đã nộp chứng từ, admin duyệt/từ chối/xoá giao dịch - Gia hạn dịch vụ: phát hành invoice gia hạn trên order gốc và trừ số dư ngay - Đồng bộ domain từ Plesk theo server, ServiceOrderRecorder tạo order/invoice cho dịch vụ admin dựng - Danh sách dịch vụ trả field theo loại (service_plan, dns_records_count), đếm dịch vụ theo loại Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
105 lines
3.8 KiB
PHP
105 lines
3.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Tests\Feature\Api\Service;
|
|
|
|
use App\Enums\Invoice\InvoiceStatus;
|
|
use App\Models\Customer;
|
|
use App\Models\Invoice;
|
|
use App\Models\Order;
|
|
use App\Models\OrderItem;
|
|
use App\Models\Product;
|
|
use App\Models\ProductPricings;
|
|
use App\Models\Service;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Tests\TestCase;
|
|
|
|
class RenewServiceTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
private Customer $customer;
|
|
private Service $service;
|
|
private Order $order;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
$this->customer = Customer::factory()->create(['total' => 100_000]);
|
|
$product = Product::factory()->create(['product_type' => 'hosting', 'is_active' => true]);
|
|
ProductPricings::create(['product_id' => $product->id, 'billing_cycle' => 'monthly', 'price' => 15_000, 'is_active' => true]);
|
|
|
|
$this->order = Order::create([
|
|
'order_num' => 'ORD-RENEW-TEST',
|
|
'customer_id' => $this->customer->id,
|
|
'total' => 15_000,
|
|
'status' => 1,
|
|
]);
|
|
|
|
$orderItem = OrderItem::create([
|
|
'order_id' => $this->order->id,
|
|
'product_id' => $product->id,
|
|
'billing_cycle' => 'monthly',
|
|
'months' => 1,
|
|
'quantity' => 1,
|
|
'price' => 15_000,
|
|
'addons_meta' => [],
|
|
]);
|
|
|
|
$this->service = Service::create([
|
|
'customer_id' => $this->customer->id,
|
|
'product_id' => $product->id,
|
|
'order_item_id' => $orderItem->id,
|
|
'type' => 'hosting',
|
|
'status' => 'active',
|
|
'started_at' => now()->subMonth(),
|
|
'expires_at' => now()->addDays(5),
|
|
]);
|
|
}
|
|
|
|
private function renew(int $months)
|
|
{
|
|
return $this->actingAs($this->customer, 'client')
|
|
->withHeaders(['x-access-token' => $this->buildClientAccessToken()])
|
|
->postJson("/v1/client/dich-vu/{$this->service->id}/renew", ['months' => $months]);
|
|
}
|
|
|
|
public function test_renew_charges_balance_and_extends_expiry_on_the_same_order(): void
|
|
{
|
|
$expiresAt = $this->service->expires_at->copy();
|
|
|
|
$this->renew(3)->assertOk()->assertJsonPath('success', true);
|
|
|
|
$invoice = Invoice::where('invoice_type', 2)->sole();
|
|
$this->assertSame($this->order->id, (int) $invoice->order_id);
|
|
$this->assertSame(InvoiceStatus::PAID->value, (int) $invoice->status);
|
|
$this->assertSame(45_000, (int) $invoice->total);
|
|
$this->assertSame(55_000, (int) $this->customer->fresh()->total);
|
|
$this->assertSame(1, Order::count());
|
|
$this->assertTrue($this->service->fresh()->expires_at->isSameDay($expiresAt->addMonths(3)));
|
|
$this->assertDatabaseHas('transactions', ['customer_id' => $this->customer->id, 'amount' => 45_000]);
|
|
}
|
|
|
|
public function test_insufficient_balance_keeps_unpaid_invoice_and_does_not_extend(): void
|
|
{
|
|
$this->customer->update(['total' => 10_000]);
|
|
$expiresAt = $this->service->expires_at->copy();
|
|
|
|
$this->renew(1)->assertStatus(422)->assertJsonPath('data.status', InvoiceStatus::UNPAID->value);
|
|
|
|
$this->assertSame(10_000, (int) $this->customer->fresh()->total);
|
|
$this->assertTrue($this->service->fresh()->expires_at->equalTo($expiresAt));
|
|
|
|
$this->customer->update(['total' => 100_000]);
|
|
$this->renew(2)->assertOk();
|
|
|
|
$invoice = Invoice::where('invoice_type', 2)->sole();
|
|
$this->assertSame(InvoiceStatus::PAID->value, (int) $invoice->status);
|
|
$this->assertSame(30_000, (int) $invoice->total);
|
|
$this->assertSame(1, $invoice->invoiceItems()->count());
|
|
$this->assertSame(70_000, (int) $this->customer->fresh()->total);
|
|
}
|
|
}
|