$businessData * @param array $ownerData */ public function provision(array $businessData, array $ownerData, ?User $invitedBy = null): BusinessProvisioningResult { $hostname = $this->domains->normalize((string) $businessData['domain']); return DB::transaction(function () use ($businessData, $ownerData, $hostname, $invitedBy): BusinessProvisioningResult { $slug = strtolower(trim((string) $businessData['slug'])); if (Business::query()->where('slug', $slug)->exists()) { throw new \InvalidArgumentException('That business slug is already in use.'); } if (BusinessDomain::query()->where('hostname', $hostname)->exists()) { throw new \InvalidArgumentException('That hostname is already assigned to another business.'); } $business = Business::create([ 'slug' => $slug, 'name' => trim((string) $businessData['name']), 'shortName' => filled($businessData['shortName'] ?? null) ? trim((string) $businessData['shortName']) : null, 'description' => filled($businessData['description'] ?? null) ? trim((string) $businessData['description']) : null, 'phone' => filled($businessData['phone'] ?? null) ? trim((string) $businessData['phone']) : null, 'email' => strtolower(trim((string) ($businessData['email'] ?? ''))), 'timezone' => $businessData['timezone'] ?? 'Europe/London', 'currency' => strtoupper((string) ($businessData['currency'] ?? 'GBP')), 'locale' => $businessData['locale'] ?? 'en-GB', ]); BusinessDomain::create([ 'businessId' => $business->id, 'hostname' => $hostname, 'isPrimary' => true, 'isActive' => true, ]); BusinessSettings::create([ 'businessId' => $business->id, 'cancellationPolicy' => 'Please give us 24 hours notice if you need to cancel.', ]); BookingRule::create([ 'businessId' => $business->id, 'minNoticeMinutes' => 120, 'maxAdvanceDays' => 60, 'cancellationWindowHours' => 24, 'rescheduleWindowHours' => 24, 'slotIntervalMinutes' => 30, 'bufferAfterMinutes' => 5, 'customerCanChooseStaff' => true, 'allowGuestBooking' => true, ]); foreach ([ 'MONDAY' => [600, 1140], 'TUESDAY' => [600, 1140], 'WEDNESDAY' => [600, 1140], 'THURSDAY' => [600, 1140], 'FRIDAY' => [600, 1200], 'SATURDAY' => [540, 1020], 'SUNDAY' => [null, null], ] as $day => [$opens, $closes]) { BusinessHours::create([ 'businessId' => $business->id, 'dayOfWeek' => $day, 'isClosed' => $opens === null, 'opensAtMinutes' => $opens, 'closesAtMinutes' => $closes, ]); } $ownerRole = $this->createRole($business, 'Owner', Permission::cases()); $barberRole = $this->createRole($business, 'Barber', [ Permission::ManageOwnAppointments, Permission::CheckInCustomers, Permission::ManageOwnAvailability, Permission::BookOnBehalfOfCustomer, Permission::ManageLoyaltyStamps, ]); $owner = User::create([ 'name' => trim((string) $ownerData['name']), 'email' => strtolower(trim((string) $ownerData['email'])), 'passwordHash' => null, 'isSuperAdmin' => false, ]); $staff = StaffProfile::create([ 'userId' => $owner->id, 'businessId' => $business->id, 'roleId' => $ownerRole->id, 'status' => 'ACTIVE', 'isBookableOnline' => true, ]); $invitation = StaffInvitation::create([ 'businessId' => $business->id, 'staffId' => $staff->id, 'email' => $owner->email, 'token' => Str::random(64), 'invitedByUserId' => $invitedBy?->id, 'expiresAt' => now()->addDays(7), ]); // Keep the local variable explicit so static analysers understand // that the default staff role is intentionally provisioned too. unset($barberRole); return new BusinessProvisioningResult($business, $invitation); }); } /** * @param array $permissions */ private function createRole(Business $business, string $name, array $permissions): Role { $role = Role::create(['businessId' => $business->id, 'name' => $name, 'isSystem' => true]); foreach ($permissions as $permission) { RolePermission::create(['roleId' => $role->id, 'permission' => $permission->value]); } return $role; } }