Add feature tests for domain order management
- Implement AdminDomainOrderTest to cover admin functionalities for domain orders including listing, confirming registration, KYC updates, activation, rejection, and refunds. - Implement ClientDomainOrderTest to cover client functionalities for placing domain orders, including validation for balance, duplicate orders, and domain normalization. - Ensure proper handling of domain pricing and service statuses throughout the tests.
This commit is contained in:
@@ -17,12 +17,11 @@ class CustomerFactory extends Factory
|
||||
public function definition()
|
||||
{
|
||||
return [
|
||||
'firstname' => $this->faker->name(),
|
||||
'lastname' => $this->faker->name(),
|
||||
'fullname' => $this->faker->name(),
|
||||
'phone' => $this->faker->unique()->phoneNumber(),
|
||||
'email' => $this->faker->unique()->email(),
|
||||
'email' => $this->faker->unique()->safeEmail(),
|
||||
'email_verify' => now(),
|
||||
'password' => '$2y$10$2Tcx805kaC.Ug/IUD34pW.R.D0l0Gpy.IEeXkrjhBr1uu048pEFp.'
|
||||
'password' => '$2y$10$2Tcx805kaC.Ug/IUD34pW.R.D0l0Gpy.IEeXkrjhBr1uu048pEFp.',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Product>
|
||||
*/
|
||||
class ProductFactory extends Factory
|
||||
{
|
||||
public function definition(): array
|
||||
{
|
||||
$slug = Str::slug($this->faker->unique()->words(3, true));
|
||||
|
||||
$categoryId = DB::table('product_categories')->insertGetId([
|
||||
'name' => 'Test Category ' . $this->faker->unique()->numberBetween(1, 9999),
|
||||
'slug' => 'cat-' . $slug,
|
||||
'type' => null, // null so boot() hook does not override product_type
|
||||
'is_active' => true,
|
||||
'sort_order' => 0,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
return [
|
||||
'product_categories_id' => $categoryId,
|
||||
'product_type' => 'domain',
|
||||
'name' => $this->faker->unique()->words(2, true),
|
||||
'slug' => $slug,
|
||||
'is_active' => true,
|
||||
'is_unlimited' => true,
|
||||
'setup_fee' => 0,
|
||||
'trial_days' => 0,
|
||||
'sort_order' => 0,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -7,9 +7,11 @@ use Illuminate\Support\Facades\Schema;
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('ip_pools', function (Blueprint $table) {
|
||||
$table->softDeletes();
|
||||
});
|
||||
if (!Schema::hasColumn('ip_pools', 'deleted_at')) {
|
||||
Schema::table('ip_pools', function (Blueprint $table) {
|
||||
$table->softDeletes();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
|
||||
@@ -9,9 +9,11 @@ use Illuminate\Support\Str;
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('ip_pools', function (Blueprint $table) {
|
||||
$table->uuid('uuid')->nullable()->unique()->after('id');
|
||||
});
|
||||
if (!Schema::hasColumn('ip_pools', 'uuid')) {
|
||||
Schema::table('ip_pools', function (Blueprint $table) {
|
||||
$table->uuid('uuid')->nullable()->unique()->after('id');
|
||||
});
|
||||
}
|
||||
|
||||
// Backfill existing rows
|
||||
DB::table('ip_pools')->whereNull('uuid')->orderBy('id')->each(function ($row) {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
@@ -11,18 +12,25 @@ return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('ip_pools', function (Blueprint $table) {
|
||||
$table->string('gateway')->nullable()->after('ip_address');
|
||||
$table->unsignedTinyInteger('cidr')->default(24)->after('gateway');
|
||||
|
||||
$table
|
||||
->enum('status', ['free', 'assigned', 'reserved'])
|
||||
->default('free')
|
||||
->after('module_network_id');
|
||||
|
||||
$table->foreignId('service_id')->nullable()->after('status')->constrained('services')->nullOnDelete();
|
||||
|
||||
$table->index(['status', 'module_network_id']);
|
||||
if (!Schema::hasColumn('ip_pools', 'gateway')) {
|
||||
$table->string('gateway')->nullable()->after('ip_address');
|
||||
}
|
||||
if (!Schema::hasColumn('ip_pools', 'cidr')) {
|
||||
$table->unsignedTinyInteger('cidr')->default(24)->after('gateway');
|
||||
}
|
||||
if (!Schema::hasColumn('ip_pools', 'status')) {
|
||||
$table->enum('status', ['free', 'assigned', 'reserved'])->default('free')->after('module_network_id');
|
||||
}
|
||||
if (!Schema::hasColumn('ip_pools', 'service_id')) {
|
||||
$table->foreignId('service_id')->nullable()->after('status')->constrained('services')->nullOnDelete();
|
||||
}
|
||||
});
|
||||
|
||||
if (!collect(\DB::select("SHOW INDEX FROM ip_pools WHERE Key_name = 'ip_pools_status_module_network_id_index'"))->count()) {
|
||||
Schema::table('ip_pools', function (Blueprint $table) {
|
||||
$table->index(['status', 'module_network_id']);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('order_items', function (Blueprint $table) {
|
||||
$table->string('domain_name', 255)->nullable()->after('server_name')->index();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('order_items', function (Blueprint $table) {
|
||||
$table->dropIndex(['domain_name']);
|
||||
$table->dropColumn('domain_name');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('services', function (Blueprint $table) {
|
||||
$table->string('registrar_reference', 255)->nullable()->after('note');
|
||||
$table->text('kyc_url')->nullable()->after('registrar_reference');
|
||||
$table->text('rejection_reason')->nullable()->after('kyc_url');
|
||||
$table->timestamp('refunded_at')->nullable()->after('rejection_reason');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('services', function (Blueprint $table) {
|
||||
$table->dropColumn(['registrar_reference', 'kyc_url', 'rejection_reason', 'refunded_at']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('customers', function (Blueprint $table) {
|
||||
$table->string('email_verify_code', 6)->nullable()->after('email_verify');
|
||||
$table->timestamp('email_verify_code_expired_at')->nullable()->after('email_verify_code');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('customers', function (Blueprint $table) {
|
||||
$table->dropColumn(['email_verify_code', 'email_verify_code_expired_at']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -125,6 +125,13 @@ class RolesAndPermissions extends Seeder
|
||||
'auto-banking.manager',
|
||||
'system-log.manager',
|
||||
|
||||
// Tên miền
|
||||
'domain.view',
|
||||
'domain.create',
|
||||
'domain.edit',
|
||||
'domain.destroy',
|
||||
'domain.manager',
|
||||
|
||||
// Vai trò & Phân quyền
|
||||
'role.view',
|
||||
'role.create',
|
||||
|
||||
@@ -60,6 +60,37 @@ class Setting extends Seeder
|
||||
'password' => '',
|
||||
'status' => 0
|
||||
], JSON_THROW_ON_ERROR)
|
||||
],
|
||||
[
|
||||
'key' => 'domain_pricing',
|
||||
'value' => json_encode([
|
||||
'domains' => [
|
||||
['tld' => '.com', 'setupFee' => 'Miễn phí', 'firstYear' => 320000, 'renewal' => 340000],
|
||||
['tld' => '.net', 'setupFee' => 'Miễn phí', 'firstYear' => 369000, 'renewal' => 409000],
|
||||
['tld' => '.org', 'setupFee' => 'Miễn phí', 'firstYear' => 339000, 'renewal' => 369000],
|
||||
['tld' => '.biz', 'setupFee' => 'Miễn phí', 'firstYear' => 399000, 'renewal' => 409000],
|
||||
['tld' => '.info', 'setupFee' => 'Miễn phí', 'firstYear' => 490000, 'renewal' => 509000],
|
||||
['tld' => '.Pro', 'setupFee' => 'Miễn phí', 'firstYear' => 469000, 'renewal' => 489000],
|
||||
['tld' => '.name', 'setupFee' => 'Miễn phí', 'firstYear' => 259000, 'renewal' => 259000],
|
||||
['tld' => '.ws', 'setupFee' => 'Miễn phí', 'firstYear' => 909000, 'renewal' => 909000],
|
||||
['tld' => '.tv', 'setupFee' => 'Miễn phí', 'firstYear' => 765000, 'renewal' => 819000],
|
||||
['tld' => '.mobi', 'setupFee' => 'Miễn phí', 'firstYear' => 589000, 'renewal' => 589000],
|
||||
['tld' => '.uk', 'setupFee' => 'Miễn phí', 'firstYear' => 537000, 'renewal' => 537000],
|
||||
['tld' => '.asia', 'setupFee' => 'Miễn phí', 'firstYear' => 380000, 'renewal' => 452000],
|
||||
['tld' => '.me', 'setupFee' => 'Miễn phí', 'firstYear' => 1029000, 'renewal' => 1029000],
|
||||
['tld' => '.us', 'setupFee' => 'Miễn phí', 'firstYear' => 323000, 'renewal' => 323000],
|
||||
],
|
||||
'services' => [
|
||||
['name' => 'Chứng thực Rapid SSL', 'setupFee' => 230000, 'maintenance' => 750000, 'note' => '01 Domain'],
|
||||
['name' => 'Chứng thực Quick SSL', 'setupFee' => 230000, 'maintenance' => 2300000, 'note' => '01 Domain'],
|
||||
['name' => 'Chứng thực Wildcard SSL', 'setupFee' => 230000, 'maintenance' => 3300000, 'note' => 'Unlimited Subdomain'],
|
||||
['name' => 'Chứng thực True BusinessID', 'setupFee' => 540000, 'maintenance' => 3900000, 'note' => '01 Domain'],
|
||||
['name' => 'Chứng thực True BusinessID WildCard', 'setupFee' => 900000, 'maintenance' => 15000000, 'note' => 'Unlimited Subdomain'],
|
||||
['name' => 'Đăng ký DNS riêng', 'setupFee' => 200000, 'maintenance' => 'Miễn phí', 'note' => "dns1.tenmiencuaban.com\ndns2.tenmiencuaban.com"],
|
||||
['name' => 'Bảo mật thông tin tên miền', 'setupFee' => 'Miễn phí', 'maintenance' => 90000, 'note' => 'Toàn bộ thông tin Whois tên miền của bạn sẽ được giấu kín hoàn toàn với mọi công cụ Whois'],
|
||||
['name' => 'Khóa tên miền', 'setupFee' => 'Miễn phí', 'maintenance' => 'Miễn phí', 'note' => 'Tăng cường bảo mật cho tên miền'],
|
||||
],
|
||||
], JSON_THROW_ON_ERROR)
|
||||
]
|
||||
]);
|
||||
|
||||
|
||||
@@ -51,6 +51,27 @@ class TemplateSeeder extends Seeder
|
||||
'type_id' => 0,
|
||||
'status' => 1
|
||||
],
|
||||
[
|
||||
'name' => 'Domain Order New',
|
||||
'subject' => '[ADMIN] Đơn đăng ký tên miền mới - {$domain_name}',
|
||||
'message' => '<table cellpadding=0 cellspacing=0 role=presentation style=width:100%;font-family:Montserrat,-apple-system><tr><td style=background-color:#eceff1;font-family:Montserrat,-apple-system,sans-serif align=center><table cellpadding=0 cellspacing=0 role=presentation style=width:600px><tr><td align=center><table cellpadding=0 cellspacing=0 role=presentation style=width:100%><tr><td style=padding:48px;text-align:center> <tr style=background-color:#fff><td style="text-align:center;padding:20px;border-bottom:.5px solid #ececec"><img alt={$company_name} src={$company_logo} style=max-height:100px><tr><td style=border-radius:4px;background-color:#fff;padding:48px;text-align:left;font-family:Montserrat,-apple-system,sans-serif;font-size:16px;line-height:24px;color:#626262><p style=margin-bottom:10px;font-size:20px;font-weight:600>Thông báo đơn đăng ký tên miền mới<p>Có một đơn đăng ký tên miền mới vừa được đặt tại <strong>{$company_name}</strong>.<p><strong>Thông tin đơn hàng:</strong><ul><li>Tên miền: <strong>{$domain_name}</strong><li>Mã đơn hàng: <strong>{$order_num}</strong><li>Tổng tiền: <strong>{$order_total}</strong><li>Khách hàng: <strong>{$customer_name}</strong><li>Email: <strong>{$customer_email}</strong><li>Thời gian: <strong>{$created_at}</strong></ul><table cellpadding=0 cellspacing=0 role=presentation><tr><td style=border-radius:4px;background-color:#f16723;text-align:center;font-family:Montserrat,-apple-system,sans-serif;padding-left:48px;padding-right:48px><a href={$admin_url} style="font-family:sans-serif;display:block;font-size:16px;font-weight:600;line-height:100%;color:#fff;text-decoration:none;padding:16px 24px 16px 24px">XEM ĐƠN HÀNG</a></table><p>Lưu ý:<ul><li>Đây là tin nhắn tự động. Vui lòng không trả lời tin nhắn này!</ul><p> </table><tr><td style=padding:48px;text-align:center> </table></table>',
|
||||
'type_id' => 0,
|
||||
'status' => 1
|
||||
],
|
||||
[
|
||||
'name' => 'Domain Order Approved',
|
||||
'subject' => 'Tên miền {$domain_name} đã được duyệt đăng ký',
|
||||
'message' => '<table cellpadding=0 cellspacing=0 role=presentation style=width:100%;font-family:Montserrat,-apple-system><tr><td style=background-color:#eceff1;font-family:Montserrat,-apple-system,sans-serif align=center><table cellpadding=0 cellspacing=0 role=presentation style=width:600px><tr><td align=center><table cellpadding=0 cellspacing=0 role=presentation style=width:100%><tr><td style=padding:48px;text-align:center> <tr style=background-color:#fff><td style="text-align:center;padding:20px;border-bottom:.5px solid #ececec"><img alt={$company_name} src={$company_logo} style=max-height:100px><tr><td style=border-radius:4px;background-color:#fff;padding:48px;text-align:left;font-family:Montserrat,-apple-system,sans-serif;font-size:16px;line-height:24px;color:#626262><p style=margin-bottom:10px;font-size:20px;font-weight:600>Kính chào {$client_name},<p>Chúng tôi xin thông báo rằng đơn đăng ký tên miền của bạn đã được <strong style=color:#22c55e>duyệt và kích hoạt thành công</strong>.<p><strong>Thông tin tên miền:</strong><ul><li>Tên miền: <strong>{$domain_name}</strong><li>Mã đơn hàng: <strong>{$order_num}</strong></ul><p>Tên miền của bạn hiện đã hoạt động. Nếu bạn có bất kỳ câu hỏi nào, vui lòng liên hệ với chúng tôi để được hỗ trợ.<p>Lưu ý:<ul><li>Đây là tin nhắn tự động. Vui lòng không trả lời tin nhắn này!</ul><p> </table><tr><td style=padding:48px;text-align:center> </table></table>',
|
||||
'type_id' => 0,
|
||||
'status' => 1
|
||||
],
|
||||
[
|
||||
'name' => 'Domain Order Rejected',
|
||||
'subject' => 'Tên miền {$domain_name} đã bị từ chối đăng ký',
|
||||
'message' => '<table cellpadding=0 cellspacing=0 role=presentation style=width:100%;font-family:Montserrat,-apple-system><tr><td style=background-color:#eceff1;font-family:Montserrat,-apple-system,sans-serif align=center><table cellpadding=0 cellspacing=0 role=presentation style=width:600px><tr><td align=center><table cellpadding=0 cellspacing=0 role=presentation style=width:100%><tr><td style=padding:48px;text-align:center> <tr style=background-color:#fff><td style="text-align:center;padding:20px;border-bottom:.5px solid #ececec"><img alt={$company_name} src={$company_logo} style=max-height:100px><tr><td style=border-radius:4px;background-color:#fff;padding:48px;text-align:left;font-family:Montserrat,-apple-system,sans-serif;font-size:16px;line-height:24px;color:#626262><p style=margin-bottom:10px;font-size:20px;font-weight:600>Kính chào {$client_name},<p>Chúng tôi xin thông báo rằng đơn đăng ký tên miền của bạn đã bị <strong style=color:#ef4444>từ chối</strong>.<p><strong>Thông tin đơn hàng:</strong><ul><li>Tên miền: <strong>{$domain_name}</strong><li>Mã đơn hàng: <strong>{$order_num}</strong><li>Lý do từ chối: <strong>{$reason}</strong></ul><p>Số dư đã thanh toán sẽ được hoàn lại vào tài khoản của bạn. Nếu bạn có bất kỳ câu hỏi nào, vui lòng liên hệ với chúng tôi để được hỗ trợ.<p>Lưu ý:<ul><li>Đây là tin nhắn tự động. Vui lòng không trả lời tin nhắn này!</ul><p> </table><tr><td style=padding:48px;text-align:center> </table></table>',
|
||||
'type_id' => 0,
|
||||
'status' => 1
|
||||
],
|
||||
[
|
||||
'name' => 'Service Expiry',
|
||||
'subject' => 'Thông báo dịch vụ sắp hết hạn',
|
||||
|
||||
Reference in New Issue
Block a user