Hide fulfilled pending-setup orders from the customer hosting list.
Deploy Ladill Hosting / deploy (push) Successful in 45s

Sales Centre left Active CustomerHostingOrder rows with pending-setup.local alongside real HostingAccounts, so customers saw two hostings.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
isaacclad
2026-07-13 17:10:49 +00:00
co-authored by Cursor
parent bc199ccb43
commit b7f320ca30
5 changed files with 200 additions and 7 deletions
@@ -0,0 +1,130 @@
<?php
namespace App\Console\Commands;
use App\Models\CustomerHostingOrder;
use App\Models\HostingAccount;
use App\Services\Hosting\PlaceholderPrimaryDomainService;
use Illuminate\Console\Command;
/**
* Legacy Sales Centre created Active CustomerHostingOrder rows with
* pending-setup.local while also assigning a real HostingAccount customers
* then saw two hostings. Link/update those orders (or soft-delete duplicates).
*/
class RepairPlaceholderHostingOrdersCommand extends Command
{
protected $signature = 'hosting:repair-placeholder-orders
{--dry-run : Show changes without writing}
{--delete-duplicates : Soft-delete Active placeholder orders that already have a HostingAccount for the same user}';
protected $description = 'Repair CustomerHostingOrders stuck on pending-setup.local after Hosting app assignment.';
public function handle(): int
{
$orders = CustomerHostingOrder::query()
->with(['hostingAccount.sites'])
->where(function ($q) {
$q->where('domain_name', 'pending-setup.local')
->orWhereNull('domain_name')
->orWhere('domain_name', '');
})
->whereIn('status', [
CustomerHostingOrder::STATUS_ACTIVE,
CustomerHostingOrder::STATUS_SUSPENDED,
CustomerHostingOrder::STATUS_PROVISIONING,
])
->orderBy('id')
->get();
if ($orders->isEmpty()) {
$this->info('No placeholder hosting orders found.');
return self::SUCCESS;
}
$changed = 0;
foreach ($orders as $order) {
$account = $order->hostingAccount;
if (! $account && $order->user_id) {
$account = HostingAccount::query()
->with('sites')
->where('user_id', $order->user_id)
->when($order->hosting_product_id, fn ($q) => $q->where('hosting_product_id', $order->hosting_product_id))
->latest('id')
->first();
}
$realDomain = null;
if ($account) {
if (! $account->relationLoaded('sites')) {
$account->load('sites');
}
$realDomain = $account->linkedDomainLabel();
if (PlaceholderPrimaryDomainService::isPlaceholderDomain($realDomain)) {
$realDomain = null;
}
}
$this->line(sprintf(
'[order #%d] user=%s domain=%s account=%s → %s',
$order->id,
$order->user_id ?: '—',
$order->domain_name ?: '(empty)',
$account?->id ?: 'none',
$realDomain ?: '(no real domain yet)'
));
if ($this->option('dry-run')) {
continue;
}
if ($account && $this->option('delete-duplicates') && (int) $order->hosting_account_id !== (int) $account->id) {
// Separate Active order + separate HostingAccount = duplicate UX.
$order->update([
'hosting_account_id' => $order->hosting_account_id ?: $account->id,
'domain_name' => $realDomain ?: $order->domain_name,
'server_username' => $order->server_username ?: $account->username,
'status' => CustomerHostingOrder::STATUS_CANCELLED,
'cancelled_at' => now(),
'approval_notes' => trim(($order->approval_notes ? $order->approval_notes."\n" : '').
'Auto-cancelled: superseded by Hosting account #'.$account->id),
]);
$order->delete();
$this->info(' soft-deleted duplicate order');
$changed++;
continue;
}
$updates = [];
if ($account && ! $order->hosting_account_id) {
$updates['hosting_account_id'] = $account->id;
}
if ($realDomain) {
$updates['domain_name'] = $realDomain;
}
if ($account && ! $order->server_username) {
$updates['server_username'] = $account->username;
}
if ($account?->node?->ip_address && ! $order->server_ip) {
$account->loadMissing('node');
$updates['server_ip'] = $account->node?->ip_address;
}
if ($updates !== []) {
$order->update($updates);
$this->info(' updated: '.json_encode($updates));
$changed++;
}
}
if ($this->option('dry-run')) {
$this->warn('Dry run: no changes applied.');
} else {
$this->info("Updated {$changed} order(s).");
}
return self::SUCCESS;
}
}