update email template
This commit is contained in:
@@ -36,7 +36,9 @@ class CheckOverdueInvoices extends Command
|
||||
}
|
||||
|
||||
$invoices = Invoice::query()
|
||||
->with('order:id,status')
|
||||
// customer_id must be selected too: OrderObserver::updated() reads it when
|
||||
// logging the status change below, and a NULL there violates log.customer_id NOT NULL.
|
||||
->with('order:id,status,customer_id')
|
||||
->whereNotNull('due_date')
|
||||
->whereDate('due_date', '<', $today)
|
||||
->whereIn('status', [InvoiceStatus::UNPAID->value, InvoiceStatus::OVERDUE->value])
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands\Domain;
|
||||
|
||||
use App\Integrations\Domain\Extractors\DnsDomainExtractor;
|
||||
use App\Integrations\Domain\Extractors\DnsDomainUserExtractor;
|
||||
use App\Integrations\Domain\Extractors\DnsRecordExtractor;
|
||||
use App\Integrations\Domain\Extractors\DnsServerExtractor;
|
||||
use App\Integrations\Domain\Transformers\DnsDomainTransformer;
|
||||
use App\Integrations\Domain\Transformers\DnsDomainUserTransformer;
|
||||
use App\Integrations\Domain\Transformers\DnsRecordTransformer;
|
||||
use App\Integrations\Domain\Transformers\DnsServerTransformer;
|
||||
use App\Integrations\Domain\Writers\DnsDomainUserWriter;
|
||||
use App\Integrations\Domain\Writers\DnsDomainWriter;
|
||||
use App\Integrations\Domain\Writers\DnsRecordWriter;
|
||||
use App\Integrations\Domain\Writers\DnsServerWriter;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class SyncDomainsCommand extends Command
|
||||
{
|
||||
protected $signature = 'domain:sync';
|
||||
|
||||
protected $description = 'Extract DNS servers/domains/records/owners from the domain admin (tgs_domain_prod) database and upsert them locally';
|
||||
|
||||
public function handle(
|
||||
DnsServerExtractor $serverExtractor,
|
||||
DnsServerTransformer $serverTransformer,
|
||||
DnsServerWriter $serverWriter,
|
||||
DnsDomainExtractor $domainExtractor,
|
||||
DnsDomainTransformer $domainTransformer,
|
||||
DnsDomainWriter $domainWriter,
|
||||
DnsRecordExtractor $recordExtractor,
|
||||
DnsRecordTransformer $recordTransformer,
|
||||
DnsRecordWriter $recordWriter,
|
||||
DnsDomainUserExtractor $domainUserExtractor,
|
||||
DnsDomainUserTransformer $domainUserTransformer,
|
||||
DnsDomainUserWriter $domainUserWriter,
|
||||
): int {
|
||||
// Order matters: servers before domains (FK), domains before records/owners (FK).
|
||||
$this->info('Syncing servers...');
|
||||
$bar = $this->output->createProgressBar(DB::connection('domain')->table('dns_manager_servers')->count());
|
||||
$bar->start();
|
||||
foreach ($serverExtractor->extract() as $row) {
|
||||
$serverWriter->write($serverTransformer->transform($row));
|
||||
$bar->advance();
|
||||
}
|
||||
$bar->finish();
|
||||
$this->newLine();
|
||||
|
||||
$this->info('Syncing domains...');
|
||||
$bar = $this->output->createProgressBar(DB::connection('domain')->table('dns_manager_domain')->count());
|
||||
$bar->start();
|
||||
$skippedDomains = 0;
|
||||
foreach ($domainExtractor->extract() as $row) {
|
||||
if (! $domainWriter->write($domainTransformer->transform($row))) {
|
||||
$skippedDomains++;
|
||||
}
|
||||
$bar->advance();
|
||||
}
|
||||
$bar->finish();
|
||||
$this->newLine();
|
||||
|
||||
$this->info('Syncing records...');
|
||||
$bar = $this->output->createProgressBar(DB::connection('domain')->table('dns_manager_ns')->count());
|
||||
$bar->start();
|
||||
$skippedRecords = 0;
|
||||
foreach ($recordExtractor->extract() as $row) {
|
||||
if (! $recordWriter->write($recordTransformer->transform($row))) {
|
||||
$skippedRecords++;
|
||||
}
|
||||
$bar->advance();
|
||||
}
|
||||
$bar->finish();
|
||||
$this->newLine();
|
||||
|
||||
$this->info('Syncing domain owners...');
|
||||
$bar = $this->output->createProgressBar(DB::connection('domain')->table('dns_domain_user')->count());
|
||||
$bar->start();
|
||||
$skippedOwners = 0;
|
||||
foreach ($domainUserExtractor->extract() as $row) {
|
||||
if (! $domainUserWriter->write($domainUserTransformer->transform($row))) {
|
||||
$skippedOwners++;
|
||||
}
|
||||
$bar->advance();
|
||||
}
|
||||
$bar->finish();
|
||||
$this->newLine();
|
||||
|
||||
$this->info("Skipped: {$skippedDomains} domain(s), {$skippedRecords} record(s), {$skippedOwners} owner link(s) (unresolved FK).");
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -3,13 +3,10 @@
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Enums\Invoice\InvoiceStatus;
|
||||
use App\Enums\Order\OrderStatus;
|
||||
use App\Helpers\OrderHelper;
|
||||
use App\Jobs\VirtualMachine\Action\VMJobs;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\InvoiceItem;
|
||||
use App\Models\Order;
|
||||
use App\Models\OrderItem;
|
||||
use App\Models\Service;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
@@ -142,38 +139,22 @@ class ServiceExpiry extends Command
|
||||
}
|
||||
|
||||
$price = (int) $orderItem->price;
|
||||
$months = $orderItem->months ?? 1;
|
||||
|
||||
if ($dryRun) {
|
||||
$this->line(" [dry-run:invoice] Would create renewal order + invoice for Service #{$service->id}, amount " . number_format($price) . ' VND.');
|
||||
$this->line(" [dry-run:invoice] Would create renewal invoice for Service #{$service->id} on Order #{$orderItem->order_id}, amount " . number_format($price) . ' VND.');
|
||||
return;
|
||||
}
|
||||
|
||||
$order = Order::create([
|
||||
'order_num' => OrderHelper::generateOrderNumber(),
|
||||
'customer_id' => $service->customer_id,
|
||||
'sub_total' => $price,
|
||||
'discount_amount' => 0,
|
||||
'total' => $price,
|
||||
'status' => OrderStatus::PENDING->value,
|
||||
'client_ip' => null,
|
||||
'note' => 'Đơn hàng gia hạn dịch vụ #' . $service->id,
|
||||
]);
|
||||
|
||||
OrderItem::create([
|
||||
'order_id' => $order->id,
|
||||
'product_id' => $service->product_id,
|
||||
'billing_cycle' => $orderItem->billing_cycle,
|
||||
'months' => $orderItem->months ?? 1,
|
||||
'os_id' => $orderItem->os_id,
|
||||
'quantity' => 1,
|
||||
'server_name' => $service->server_name,
|
||||
'price' => $price,
|
||||
]);
|
||||
// Gia hạn là một billing cycle mới của CÙNG một Order/Service — không tạo Order mới.
|
||||
// Order gốc chính là order đã tạo ra service này (qua order_item -> order_id).
|
||||
$periodStart = $service->expires_at && $service->expires_at->isFuture() ? $service->expires_at->copy() : now();
|
||||
$periodEnd = $periodStart->copy()->addMonths($months);
|
||||
|
||||
$invoice = Invoice::create([
|
||||
'invoice_number' => OrderHelper::generateOrderNumber('INV'),
|
||||
'customer_id' => $service->customer_id,
|
||||
'order_id' => $order->id,
|
||||
'order_id' => $orderItem->order_id,
|
||||
'invoice_type' => 2, // renewal
|
||||
'sub_total' => $price,
|
||||
'discount_amount' => 0,
|
||||
@@ -183,6 +164,8 @@ class ServiceExpiry extends Command
|
||||
'due_date' => $service->expires_at && $service->expires_at->isFuture()
|
||||
? $service->expires_at->toDateString()
|
||||
: now()->addDays(7)->toDateString(),
|
||||
'billing_period_start' => $periodStart->toDateString(),
|
||||
'billing_period_end' => $periodEnd->toDateString(),
|
||||
]);
|
||||
|
||||
InvoiceItem::create([
|
||||
@@ -199,11 +182,11 @@ class ServiceExpiry extends Command
|
||||
// Update service to point to the new renewal invoice
|
||||
$service->update(['invoice_id' => $invoice->id]);
|
||||
|
||||
Log::info('Renewal order and invoice created', [
|
||||
Log::info('Renewal invoice created', [
|
||||
'service_id' => $service->id,
|
||||
'order_num' => $order->order_num,
|
||||
'order_id' => $orderItem->order_id,
|
||||
'invoice_number' => $invoice->invoice_number,
|
||||
]);
|
||||
$this->line(" [invoice] Renewal {$order->order_num} / {$invoice->invoice_number} for Service #{$service->id}.");
|
||||
$this->line(" [invoice] Renewal invoice {$invoice->invoice_number} on Order #{$orderItem->order_id} for Service #{$service->id}.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\OrderItem;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class UpdateExpiryDate extends Command
|
||||
{
|
||||
protected $signature = 'app:update-expiry-date {--dry-run : Show what would be updated without making changes}';
|
||||
|
||||
protected $description = 'Update expiry_date for existing order items based on created_at + months';
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$dryRun = $this->option('dry-run');
|
||||
|
||||
$query = OrderItem::whereNull('expiry_date')
|
||||
->whereNotNull('months')
|
||||
->where('months', '>', 0);
|
||||
|
||||
$count = $query->count();
|
||||
|
||||
if ($count === 0) {
|
||||
$this->info('No order items need to be updated.');
|
||||
return 0;
|
||||
}
|
||||
|
||||
$this->info("Found {$count} order items to update.");
|
||||
|
||||
if ($dryRun) {
|
||||
$this->warn('DRY RUN - No changes will be made.');
|
||||
$query->chunk(100, function ($items) {
|
||||
foreach ($items as $item) {
|
||||
$expiryDate = Carbon::parse($item->created_at)->addMonths($item->months);
|
||||
$this->line("Order #{$item->order_id}, Item #{$item->id}: would set expiry_date to {$expiryDate}");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$updated = 0;
|
||||
$query->chunk(100, function ($items) use (&$updated) {
|
||||
foreach ($items as $item) {
|
||||
$expiryDate = Carbon::parse($item->created_at)->addMonths($item->months);
|
||||
$item->update(['expiry_date' => $expiryDate]);
|
||||
$updated++;
|
||||
}
|
||||
});
|
||||
$this->info("Updated {$updated} order items.");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands\Whmcs;
|
||||
|
||||
use App\Integrations\Whmcs\Extractors\ClientExtractor;
|
||||
use App\Jobs\Whmcs\SyncClientJob;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class SyncClientsCommand extends Command
|
||||
{
|
||||
protected $signature = 'whmcs:sync-clients {--sync : Run each record inline instead of queueing it}';
|
||||
|
||||
protected $description = 'Extract clients from the WHMCS database and upsert them into customers';
|
||||
|
||||
public function handle(ClientExtractor $extractor): int
|
||||
{
|
||||
$sync = (bool) $this->option('sync');
|
||||
$total = DB::connection('whmcs')->table('tblclients')->count();
|
||||
|
||||
$bar = $this->output->createProgressBar($total);
|
||||
$bar->start();
|
||||
|
||||
foreach ($extractor->extract() as $row) {
|
||||
$job = new SyncClientJob((array) $row);
|
||||
$sync ? dispatch_sync($job) : dispatch($job);
|
||||
$bar->advance();
|
||||
}
|
||||
|
||||
$bar->finish();
|
||||
$this->newLine();
|
||||
$this->info("Dispatched {$total} client(s) for sync.");
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands\Whmcs;
|
||||
|
||||
use App\Integrations\Whmcs\Extractors\CustomerDomainExtractor;
|
||||
use App\Integrations\Whmcs\Transformers\CustomerDomainTransformer;
|
||||
use App\Integrations\Whmcs\Writers\CustomerDomainWriter;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class SyncCustomerDomainsCommand extends Command
|
||||
{
|
||||
protected $signature = 'whmcs:sync-domains';
|
||||
|
||||
protected $description = 'Extract customer domain registrations (tbldomains) from WHMCS and upsert them into module_domain (run after whmcs:sync-clients, whmcs:sync-orders, domain:sync)';
|
||||
|
||||
public function handle(CustomerDomainExtractor $extractor, CustomerDomainTransformer $transformer, CustomerDomainWriter $writer): int
|
||||
{
|
||||
$total = DB::connection('whmcs')->table('tbldomains')->count();
|
||||
|
||||
$bar = $this->output->createProgressBar($total);
|
||||
$bar->start();
|
||||
|
||||
$skipped = 0;
|
||||
foreach ($extractor->extract() as $row) {
|
||||
if (! $writer->write($transformer->transform($row))) {
|
||||
$skipped++;
|
||||
}
|
||||
$bar->advance();
|
||||
}
|
||||
|
||||
$bar->finish();
|
||||
$this->newLine();
|
||||
$this->info("Processed {$total} domain(s), skipped {$skipped} (unresolved customer).");
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands\Whmcs;
|
||||
|
||||
use App\Integrations\Whmcs\Extractors\InvoiceExtractor;
|
||||
use App\Jobs\Whmcs\SyncInvoiceJob;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class SyncInvoicesCommand extends Command
|
||||
{
|
||||
protected $signature = 'whmcs:sync-invoices {--sync : Run each record inline instead of queueing it}';
|
||||
|
||||
protected $description = 'Extract invoices (with their line items) from WHMCS and upsert them into invoices/invoice_items';
|
||||
|
||||
public function handle(InvoiceExtractor $extractor): int
|
||||
{
|
||||
$sync = (bool) $this->option('sync');
|
||||
$total = DB::connection('whmcs')->table('tblinvoices')->count();
|
||||
|
||||
$bar = $this->output->createProgressBar($total);
|
||||
$bar->start();
|
||||
|
||||
foreach ($extractor->extract() as $row) {
|
||||
$job = new SyncInvoiceJob((array) $row);
|
||||
$sync ? dispatch_sync($job) : dispatch($job);
|
||||
$bar->advance();
|
||||
}
|
||||
|
||||
$bar->finish();
|
||||
$this->newLine();
|
||||
$this->info("Dispatched {$total} invoice(s) for sync.");
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands\Whmcs;
|
||||
|
||||
use App\Integrations\Whmcs\Extractors\OrderItemExtractor;
|
||||
use App\Integrations\Whmcs\Transformers\OrderItemTransformer;
|
||||
use App\Integrations\Whmcs\Writers\OrderItemWriter;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class SyncOrderItemsCommand extends Command
|
||||
{
|
||||
protected $signature = 'whmcs:sync-order-items';
|
||||
|
||||
protected $description = 'Extract provisioned services (tblhosting) from WHMCS and upsert them into order_items (run after whmcs:sync-orders)';
|
||||
|
||||
public function handle(OrderItemExtractor $extractor, OrderItemTransformer $transformer, OrderItemWriter $writer): int
|
||||
{
|
||||
$total = DB::connection('whmcs')->table('tblhosting')->count();
|
||||
|
||||
$bar = $this->output->createProgressBar($total);
|
||||
$bar->start();
|
||||
|
||||
$skipped = 0;
|
||||
foreach ($extractor->extract() as $row) {
|
||||
if (! $writer->write($transformer->transform($row))) {
|
||||
$skipped++;
|
||||
}
|
||||
$bar->advance();
|
||||
}
|
||||
|
||||
$bar->finish();
|
||||
$this->newLine();
|
||||
$this->info("Processed {$total} order item(s), skipped {$skipped} (unresolved order).");
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands\Whmcs;
|
||||
|
||||
use App\Integrations\Whmcs\Extractors\OrderExtractor;
|
||||
use App\Jobs\Whmcs\SyncOrderJob;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class SyncOrdersCommand extends Command
|
||||
{
|
||||
protected $signature = 'whmcs:sync-orders {--sync : Run each record inline instead of queueing it}';
|
||||
|
||||
protected $description = 'Extract orders from the WHMCS database and upsert them into orders (run before whmcs:sync-invoices)';
|
||||
|
||||
public function handle(OrderExtractor $extractor): int
|
||||
{
|
||||
$sync = (bool) $this->option('sync');
|
||||
$total = DB::connection('whmcs')->table('tblorders')->count();
|
||||
|
||||
$bar = $this->output->createProgressBar($total);
|
||||
$bar->start();
|
||||
|
||||
foreach ($extractor->extract() as $row) {
|
||||
$job = new SyncOrderJob((array) $row);
|
||||
$sync ? dispatch_sync($job) : dispatch($job);
|
||||
$bar->advance();
|
||||
}
|
||||
|
||||
$bar->finish();
|
||||
$this->newLine();
|
||||
$this->info("Dispatched {$total} order(s) for sync.");
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands\Whmcs;
|
||||
|
||||
use App\Integrations\Whmcs\Extractors\AddonExtractor;
|
||||
use App\Integrations\Whmcs\Extractors\ProductExtractor;
|
||||
use App\Integrations\Whmcs\Extractors\ProductGroupExtractor;
|
||||
use App\Integrations\Whmcs\Transformers\ProductAddonTransformer;
|
||||
use App\Integrations\Whmcs\Transformers\ProductCategoryTransformer;
|
||||
use App\Integrations\Whmcs\Transformers\ProductTransformer;
|
||||
use App\Integrations\Whmcs\Writers\ProductAddonWriter;
|
||||
use App\Integrations\Whmcs\Writers\ProductCategoryWriter;
|
||||
use App\Integrations\Whmcs\Writers\ProductWriter;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class SyncProductsCommand extends Command
|
||||
{
|
||||
protected $signature = 'whmcs:sync-products';
|
||||
|
||||
protected $description = 'Extract product groups, products (with pricing) and addons from WHMCS and upsert them into product_categories/products/product_pricings/product_addon';
|
||||
|
||||
public function handle(
|
||||
ProductGroupExtractor $groupExtractor,
|
||||
ProductCategoryTransformer $groupTransformer,
|
||||
ProductCategoryWriter $groupWriter,
|
||||
ProductExtractor $productExtractor,
|
||||
ProductTransformer $productTransformer,
|
||||
ProductWriter $productWriter,
|
||||
AddonExtractor $addonExtractor,
|
||||
ProductAddonTransformer $addonTransformer,
|
||||
ProductAddonWriter $addonWriter,
|
||||
): int {
|
||||
// Order matters: categories before products (FK), products before addons (type guess looks at linked products).
|
||||
$this->info('Syncing product categories...');
|
||||
$bar = $this->output->createProgressBar(DB::connection('whmcs')->table('tblproductgroups')->count());
|
||||
$bar->start();
|
||||
foreach ($groupExtractor->extract() as $row) {
|
||||
$groupWriter->write($groupTransformer->transform($row));
|
||||
$bar->advance();
|
||||
}
|
||||
$bar->finish();
|
||||
$this->newLine();
|
||||
|
||||
$this->info('Syncing products...');
|
||||
$bar = $this->output->createProgressBar(DB::connection('whmcs')->table('tblproducts')->count());
|
||||
$bar->start();
|
||||
$skippedProducts = 0;
|
||||
foreach ($productExtractor->extract() as $row) {
|
||||
if (! $productWriter->write($productTransformer->transform($row))) {
|
||||
$skippedProducts++;
|
||||
}
|
||||
$bar->advance();
|
||||
}
|
||||
$bar->finish();
|
||||
$this->newLine();
|
||||
|
||||
$this->info('Syncing addons...');
|
||||
$bar = $this->output->createProgressBar(DB::connection('whmcs')->table('tbladdons')->count());
|
||||
$bar->start();
|
||||
foreach ($addonExtractor->extract() as $row) {
|
||||
$addonWriter->write($addonTransformer->transform($row));
|
||||
$bar->advance();
|
||||
}
|
||||
$bar->finish();
|
||||
$this->newLine();
|
||||
|
||||
$this->info("Skipped: {$skippedProducts} product(s) (unresolved category).");
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands\Whmcs;
|
||||
|
||||
use App\Integrations\Whmcs\Extractors\ServiceExtractor;
|
||||
use App\Integrations\Whmcs\Transformers\ServiceTransformer;
|
||||
use App\Integrations\Whmcs\Writers\ServiceWriter;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class SyncServicesCommand extends Command
|
||||
{
|
||||
protected $signature = 'whmcs:sync-services';
|
||||
|
||||
protected $description = 'Extract provisioned services (tblhosting) from WHMCS and upsert them into services (run after whmcs:sync-clients, whmcs:sync-products, whmcs:sync-order-items)';
|
||||
|
||||
public function handle(ServiceExtractor $extractor, ServiceTransformer $transformer, ServiceWriter $writer): int
|
||||
{
|
||||
$total = DB::connection('whmcs')->table('tblhosting')->count();
|
||||
|
||||
$bar = $this->output->createProgressBar($total);
|
||||
$bar->start();
|
||||
|
||||
$skipped = 0;
|
||||
foreach ($extractor->extract() as $row) {
|
||||
if (! $writer->write($transformer->transform($row))) {
|
||||
$skipped++;
|
||||
}
|
||||
$bar->advance();
|
||||
}
|
||||
|
||||
$bar->finish();
|
||||
$this->newLine();
|
||||
$this->info("Processed {$total} service(s), skipped {$skipped} (unresolved customer).");
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user