File: /home/xedaptot/ai.naniguide.com/database/seeders/Demo/Stage03_CustomersSeeder.php
<?php
namespace Database\Seeders\Demo;
use App\Model\Customer;
use App\Model\Language;
use App\Model\Role;
use App\Model\User;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
/**
* Creates demo customer accounts via AccountProvisioningService::createCustomer
* (the same flow `/admin/customers/create` uses).
*
* Special-case: when a customer entry has `reuse_admin` set, we skip user
* creation and instead link the existing admin user to a freshly-created
* Customer record. This is how the primary customer shares the user account
* with the default admin so the same login works for both /admin and /rui routes.
*/
class Stage03_CustomersSeeder extends AbstractDemoSeeder
{
protected int $fakerOffset = 3;
/** key → Customer */
public static array $created = [];
/** customer key → list of additional User team-member rows (besides primary owner). */
public static array $teamUsers = [];
public function run(): void
{
$language = Language::getFirstLanguageByCode('en') ?? Language::first();
// Use the super admin as the "creator" admin for all customers
$superAdminEntry = Stage01_AdminsSeeder::$created['super'] ?? null;
if (!$superAdminEntry) {
throw new \RuntimeException('Stage03 needs Stage01 to have run first.');
}
$creatorAdmin = $superAdminEntry['admin'];
$defaultRole = Role::getDefaultAdminRole();
foreach (DemoConfig::customers() as $cfg) {
$this->time("create customer {$cfg['email']}", function () use ($cfg, $creatorAdmin, $language, $defaultRole) {
if (!empty($cfg['reuse_admin'])) {
$this->createCustomerReusingAdminUser($cfg, $creatorAdmin, $language);
} else {
$this->createCustomerWithNewUser($cfg, $creatorAdmin, $language, $defaultRole);
}
if (!empty($cfg['team_users'])) {
$this->createTeamUsers($cfg, $defaultRole);
}
});
}
$this->log('seeded ' . count(self::$created) . ' customers');
$teamCount = array_sum(array_map('count', self::$teamUsers));
if ($teamCount > 0) {
$this->log("seeded {$teamCount} additional team users");
}
}
/**
* Create a Customer row + link it to the existing admin's user.
* This is for the "primary" customer that shares the admin user account.
*/
protected function createCustomerReusingAdminUser(array $cfg, $creatorAdmin, $language): void
{
$adminEntry = Stage01_AdminsSeeder::$created[$cfg['reuse_admin']] ?? null;
if (!$adminEntry) {
throw new \RuntimeException("reuse_admin=[{$cfg['reuse_admin']}] not found");
}
[$validator, $customer] = Customer::createCustomer(
$cfg['company'],
$cfg['timezone'],
$language ? $language->id : null,
$creatorAdmin
);
if ($validator->fails()) {
throw new \RuntimeException('createCustomer failed: ' . $validator->errors()->first());
}
$customer->addUser($adminEntry['user']);
self::$created[$cfg['key']] = $customer;
}
/**
* Standard flow — same steps as the admin "create customer" form path, but
* inlined so we can detect success by exception (the wrapper service
* re-evaluates a stale unique-email validator after the transaction commits
* which produces a false-positive failure).
*/
protected function createCustomerWithNewUser(array $cfg, $creatorAdmin, $language, $defaultRole): void
{
if (!$defaultRole) {
throw new \RuntimeException('No default admin role exists.');
}
DB::transaction(function () use ($cfg, $creatorAdmin, $language, $defaultRole) {
// 1. user
$user = User::newDefault();
[$ok, $errors] = $user->saveUser(
$cfg['email'],
$cfg['password'],
$cfg['password'],
$cfg['first_name'],
$cfg['last_name'],
null,
$defaultRole->uid
);
if (!$ok) {
throw new \RuntimeException("saveUser failed for {$cfg['email']}: " . implode(', ', $errors->all()));
}
// 2. customer
[$validator, $customer] = Customer::createCustomer(
$cfg['company'],
$cfg['timezone'],
$language ? $language->id : null,
$creatorAdmin
);
if ($validator->fails()) {
throw new \RuntimeException("createCustomer failed for {$cfg['email']}: " . $validator->errors()->first());
}
$customer->addUser($user);
self::$created[$cfg['key']] = $customer;
// 3. avatar
if (!empty($cfg['avatar'])) {
Stage01_AdminsSeeder::copyAvatar($user, $cfg['avatar']);
}
// 4. backdate created_at for Customer Growth chart
if (!empty($cfg['created_months_ago'])) {
$date = Carbon::now()->subMonths($cfg['created_months_ago'])
->startOfMonth()->addDays(rand(0, 27));
$customer->created_at = $date;
$customer->save();
$user->created_at = $date;
$user->save();
}
});
}
/**
* Add additional team-member User rows to a customer that already exists.
* Same flow as `/rui/account/users/create` (AccountController::userStore):
* Customer::newUser() → User::saveUser() → Customer::addUser() to wire FK.
* All members receive the global organization_admin role for demo simplicity.
*/
protected function createTeamUsers(array $cfg, $defaultRole): void
{
$customer = self::$created[$cfg['key']] ?? null;
if (!$customer) {
throw new \RuntimeException("createTeamUsers: customer [{$cfg['key']}] not in \$created map.");
}
if (!$defaultRole) {
throw new \RuntimeException('No default admin role exists — cannot seed team users.');
}
foreach ($cfg['team_users'] as $member) {
DB::transaction(function () use ($customer, $member, $defaultRole, $cfg) {
$user = $customer->newUser();
[$ok, $errors] = $user->saveUser(
$member['email'],
$member['password'],
$member['password'],
$member['first_name'],
$member['last_name'],
null,
$defaultRole->uid
);
if (!$ok) {
throw new \RuntimeException("team-user saveUser failed for {$member['email']}: " . implode(', ', $errors->all()));
}
$customer->addUser($user);
if (!empty($member['avatar'])) {
Stage01_AdminsSeeder::copyAvatar($user, $member['avatar']);
}
self::$teamUsers[$cfg['key']][] = $user;
});
}
}
}