update email template

This commit is contained in:
baodev0
2026-09-09 16:56:22 +07:00
parent d3d9f76ba0
commit 2365ef72e9
210 changed files with 6335 additions and 4688 deletions
@@ -0,0 +1,37 @@
<?php
namespace App\Integrations\Whmcs\Support;
use Illuminate\Support\Facades\DB;
/**
* Resolves a WHMCS `tblclients.id` to the matching local `customers.id`,
* bridged through the uuid both sides already share (see ClientWriter).
*/
class CustomerIdResolver
{
/** @var array<int, int>|null whmcs client id => local customer id */
private static ?array $map = null;
public function resolve(int $whmcsClientId): ?int
{
self::$map ??= $this->build();
return self::$map[$whmcsClientId] ?? null;
}
private function build(): array
{
$uuidsByWhmcsId = DB::connection('whmcs')->table('tblclients')->pluck('uuid', 'id');
$customerIdsByUuid = DB::table('customers')->pluck('id', 'uuid');
$map = [];
foreach ($uuidsByWhmcsId as $whmcsId => $uuid) {
if (isset($customerIdsByUuid[$uuid])) {
$map[$whmcsId] = $customerIdsByUuid[$uuid];
}
}
return $map;
}
}
@@ -0,0 +1,53 @@
<?php
namespace App\Integrations\Whmcs\Support;
use Illuminate\Support\Facades\DB;
/**
* Ensures a WHMCS customer domain also exists in the DNS manager (dns_manager_domain) and is
* linked to the customer (dns_domain_user) — that pivot is what the customer's "Domains" panel
* in the admin actually reads (fetchCustomerDnsDomains), not module_domain.
*/
class DnsDomainLinker
{
private static ?int $canonicalServerId = null;
public function link(string $domainName, int $customerId, ?int $registrarId = null): void
{
$domainId = DB::table('dns_manager_domain')->whereRaw('LOWER(domain_name) = ?', [mb_strtolower($domainName)])->value('id');
if (! $domainId) {
$serverId = $this->canonicalServerId();
if (! $serverId) {
return;
}
$domainId = DB::table('dns_manager_domain')->insertGetId([
'domain_name' => $domainName,
'dns_manager_server_id' => $serverId,
// not a real provisioned DNS zone, just a billing placeholder created from WHMCS
'is_created' => 0,
'registrar_id' => $registrarId,
'created_at' => now(),
'updated_at' => now(),
]);
} elseif ($registrarId !== null) {
// don't clobber an existing registrar with an unresolved (null) one from a later re-sync
DB::table('dns_manager_domain')->where('id', $domainId)->update(['registrar_id' => $registrarId]);
}
DB::table('dns_domain_user')->updateOrInsert(
['domain_id' => $domainId, 'user_id' => $customerId],
['created_at' => now(), 'updated_at' => now()]
);
}
private function canonicalServerId(): ?int
{
return self::$canonicalServerId ??= DB::table('dns_manager_servers')
->orderByRaw('CASE WHEN id = 1 THEN 0 ELSE 1 END')
->orderBy('id')
->value('id');
}
}
@@ -0,0 +1,22 @@
<?php
namespace App\Integrations\Whmcs\Support;
use Illuminate\Support\Facades\DB;
/**
* Resolves a WHMCS `tblorders.id` to the matching local `orders.id` via `orders.legacy_id`.
* Orders must be synced (whmcs:sync-orders) before invoices for this to find anything.
*/
class OrderIdResolver
{
/** @var array<int, int>|null legacy order id => local order id */
private static ?array $map = null;
public function resolve(int $whmcsOrderId): ?int
{
self::$map ??= DB::table('orders')->whereNotNull('legacy_id')->pluck('id', 'legacy_id')->all();
return self::$map[$whmcsOrderId] ?? null;
}
}
@@ -0,0 +1,19 @@
<?php
namespace App\Integrations\Whmcs\Support;
use Illuminate\Support\Facades\DB;
/** Resolves a WHMCS `tblhosting.id` to the local `order_items.id` via `legacy_id` (1:1, same source row). */
class OrderItemIdResolver
{
/** @var array<int, int>|null */
private static ?array $map = null;
public function resolve(int $legacyHostingId): ?int
{
self::$map ??= DB::table('order_items')->whereNotNull('legacy_id')->pluck('id', 'legacy_id')->all();
return self::$map[$legacyHostingId] ?? null;
}
}
@@ -0,0 +1,24 @@
<?php
namespace App\Integrations\Whmcs\Support;
use Illuminate\Support\Facades\DB;
/** Resolves a WHMCS `tblproductgroups.id` to the local `product_categories.id` via `legacy_id`. */
class ProductCategoryIdResolver
{
/** @var array<int, int>|null */
private static ?array $map = null;
public function resolve(int $legacyGroupId): ?int
{
self::$map ??= DB::table('product_categories')->whereNotNull('legacy_id')->pluck('id', 'legacy_id')->all();
return self::$map[$legacyGroupId] ?? null;
}
public function type(int $localCategoryId): ?string
{
return DB::table('product_categories')->where('id', $localCategoryId)->value('type');
}
}
@@ -0,0 +1,27 @@
<?php
namespace App\Integrations\Whmcs\Support;
use Illuminate\Support\Facades\DB;
/** Resolves a WHMCS `tblproducts.id` to the local `products.id` via `legacy_id`. */
class ProductIdResolver
{
/** @var array<int, int>|null */
private static ?array $map = null;
public function resolve(int $legacyProductId): ?int
{
self::$map ??= DB::table('products')->whereNotNull('legacy_id')->pluck('id', 'legacy_id')->all();
return self::$map[$legacyProductId] ?? null;
}
public function categoryTypeOf(int $localProductId): ?string
{
return DB::table('products')
->join('product_categories', 'product_categories.id', '=', 'products.product_categories_id')
->where('products.id', $localProductId)
->value('product_categories.type');
}
}
@@ -0,0 +1,26 @@
<?php
namespace App\Integrations\Whmcs\Support;
use Illuminate\Support\Facades\DB;
/** Resolves a WHMCS registrar module slug (e.g. 'nhanhoa', 'PA') to the local `registrars.id`, matched case-insensitively on short_name. */
class RegistrarIdResolver
{
/** @var array<string, int>|null lowercase short_name => registrar id */
private static ?array $map = null;
public function resolve(string $whmcsRegistrar): ?int
{
$whmcsRegistrar = trim($whmcsRegistrar);
if ($whmcsRegistrar === '') {
return null;
}
self::$map ??= DB::table('registrars')->pluck('id', 'short_name')
->mapWithKeys(fn ($id, $shortName) => [mb_strtolower($shortName) => $id])
->all();
return self::$map[mb_strtolower($whmcsRegistrar)] ?? null;
}
}
@@ -0,0 +1,23 @@
<?php
namespace App\Integrations\Whmcs\Support;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
/** Slugifies a candidate and appends -N until it's unique in the given column, excluding the row's own legacy_id. */
class SlugUniquifier
{
public function unique(string $table, string $column, string $candidate, int $legacyId): string
{
$base = Str::slug($candidate) ?: 'item';
$slug = $base;
$suffix = 1;
while (DB::table($table)->where($column, $slug)->where('legacy_id', '!=', $legacyId)->exists()) {
$slug = "{$base}-" . ++$suffix;
}
return $slug;
}
}