Add Queue demo:seed for ticket walkthroughs.
Deploy Ladill Queue / deploy (push) Successful in 1m53s
Deploy Ladill Queue / deploy (push) Successful in 1m53s
Seed branches, queues, counters, and tickets with plan-aware limits and reset. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,632 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Qms;
|
||||
|
||||
use App\Models\AuditLog;
|
||||
use App\Models\Branch;
|
||||
use App\Models\Counter;
|
||||
use App\Models\CustomerFeedback;
|
||||
use App\Models\Department;
|
||||
use App\Models\Device;
|
||||
use App\Models\DisplayScreen;
|
||||
use App\Models\Member;
|
||||
use App\Models\Organization;
|
||||
use App\Models\QueueAppointment;
|
||||
use App\Models\QueueRule;
|
||||
use App\Models\ServiceQueue;
|
||||
use App\Models\ServiceSession;
|
||||
use App\Models\Ticket;
|
||||
use App\Models\TicketEvent;
|
||||
use App\Models\TicketTransfer;
|
||||
use App\Models\User;
|
||||
use App\Models\VoiceAnnouncement;
|
||||
use App\Models\Workflow;
|
||||
use App\Models\WorkflowStep;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class DemoSeeder
|
||||
{
|
||||
/** @var array<string, array{branches: int, queues_per_branch: int, counters_per_branch: int, tickets_per_queue: int, history_tickets: int}> */
|
||||
private const VOLUMES = [
|
||||
'free' => [
|
||||
'branches' => 1,
|
||||
'queues_per_branch' => 5,
|
||||
'counters_per_branch' => 3,
|
||||
'tickets_per_queue' => 8,
|
||||
'history_tickets' => 12,
|
||||
],
|
||||
'pro' => [
|
||||
'branches' => 3,
|
||||
'queues_per_branch' => 4,
|
||||
'counters_per_branch' => 4,
|
||||
'tickets_per_queue' => 10,
|
||||
'history_tickets' => 40,
|
||||
],
|
||||
'enterprise' => [
|
||||
'branches' => 4,
|
||||
'queues_per_branch' => 5,
|
||||
'counters_per_branch' => 5,
|
||||
'tickets_per_queue' => 12,
|
||||
'history_tickets' => 80,
|
||||
],
|
||||
];
|
||||
|
||||
private const QUEUE_NAMES = [
|
||||
['Reception', 'A'],
|
||||
['Customer Service', 'B'],
|
||||
['Payments', 'C'],
|
||||
['Collections', 'D'],
|
||||
['Support', 'E'],
|
||||
];
|
||||
|
||||
public function __construct(private readonly OrganizationResolver $orgs) {}
|
||||
|
||||
/**
|
||||
* @return array{user: User, organization: Organization, plan: string, counts: array<string, int>}
|
||||
*/
|
||||
public function seed(string $identity, string $plan, bool $reset = false): array
|
||||
{
|
||||
$plan = $this->normalizePlan($plan);
|
||||
$user = $this->resolveUser($identity);
|
||||
|
||||
if ($reset) {
|
||||
$this->resetForOwner($user->ownerRef());
|
||||
}
|
||||
|
||||
$organization = $this->ensureOrganization($user);
|
||||
$this->assignPlan($organization, $plan);
|
||||
|
||||
$volumes = self::VOLUMES[$plan];
|
||||
$branches = $this->seedBranches($user->ownerRef(), $organization, $volumes['branches']);
|
||||
$queues = [];
|
||||
$counters = [];
|
||||
|
||||
foreach ($branches as $branchIndex => $branch) {
|
||||
$dept = $this->ensureDepartment($user->ownerRef(), $branch);
|
||||
$branchQueues = $this->seedQueues(
|
||||
$user->ownerRef(),
|
||||
$organization,
|
||||
$branch,
|
||||
$dept,
|
||||
$volumes['queues_per_branch'],
|
||||
$plan,
|
||||
);
|
||||
$branchCounters = $this->seedCounters(
|
||||
$user->ownerRef(),
|
||||
$organization,
|
||||
$branch,
|
||||
$dept,
|
||||
$branchQueues,
|
||||
$volumes['counters_per_branch'],
|
||||
);
|
||||
$this->seedDisplay($user->ownerRef(), $organization, $branch, $branchQueues);
|
||||
$this->seedDevice($user->ownerRef(), $organization, $branch);
|
||||
|
||||
foreach ($branchQueues as $qi => $queue) {
|
||||
$this->seedLiveTickets(
|
||||
$user->ownerRef(),
|
||||
$organization,
|
||||
$branch,
|
||||
$queue,
|
||||
$branchCounters[$qi % max(1, count($branchCounters))] ?? null,
|
||||
$volumes['tickets_per_queue'],
|
||||
);
|
||||
}
|
||||
|
||||
if ($plan !== 'free' && count($branchQueues) >= 2) {
|
||||
$this->seedRouting($user->ownerRef(), $branchQueues);
|
||||
$this->seedTransfers($user->ownerRef(), $organization, $branch, $branchQueues, $branchCounters);
|
||||
}
|
||||
|
||||
if ($branchIndex === 0 && $plan !== 'free') {
|
||||
$this->seedWorkflow($user->ownerRef(), $organization, $branch, $branchQueues);
|
||||
}
|
||||
|
||||
$queues = array_merge($queues, $branchQueues);
|
||||
$counters = array_merge($counters, $branchCounters);
|
||||
}
|
||||
|
||||
$this->seedHistoryTickets(
|
||||
$user->ownerRef(),
|
||||
$organization,
|
||||
$branches[0],
|
||||
$queues[0] ?? null,
|
||||
$counters[0] ?? null,
|
||||
$volumes['history_tickets'],
|
||||
);
|
||||
|
||||
$organization->refresh();
|
||||
|
||||
return [
|
||||
'user' => $user,
|
||||
'organization' => $organization,
|
||||
'plan' => $plan,
|
||||
'counts' => [
|
||||
'branches' => Branch::query()->where('organization_id', $organization->id)->whereNull('deleted_at')->count(),
|
||||
'queues' => ServiceQueue::query()->where('organization_id', $organization->id)->whereNull('deleted_at')->count(),
|
||||
'counters' => Counter::query()->where('organization_id', $organization->id)->whereNull('deleted_at')->count(),
|
||||
'tickets' => Ticket::query()->where('organization_id', $organization->id)->whereNull('deleted_at')->count(),
|
||||
'displays' => DisplayScreen::query()->where('organization_id', $organization->id)->whereNull('deleted_at')->count(),
|
||||
'rules' => QueueRule::query()->where('owner_ref', $user->ownerRef())->count(),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function resolveUser(string $identity): User
|
||||
{
|
||||
$identity = trim($identity);
|
||||
$user = User::query()
|
||||
->where(function ($q) use ($identity) {
|
||||
$q->where('email', $identity)->orWhere('public_id', $identity);
|
||||
})
|
||||
->first();
|
||||
|
||||
if ($user) {
|
||||
return $user;
|
||||
}
|
||||
|
||||
$email = str_contains($identity, '@') ? $identity : $identity.'@demo.ladill.local';
|
||||
$publicId = str_contains($identity, '@') ? (string) Str::uuid() : $identity;
|
||||
|
||||
return User::query()->create([
|
||||
'public_id' => $publicId,
|
||||
'name' => 'Demo User',
|
||||
'email' => $email,
|
||||
'password' => bcrypt('password'),
|
||||
'email_verified_at' => now(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function assignPlan(Organization $organization, string $plan): void
|
||||
{
|
||||
$settings = $organization->settings ?? [];
|
||||
$settings['onboarded'] = true;
|
||||
$settings['plan'] = $plan;
|
||||
$settings['plan_expires_at'] = $plan === 'free'
|
||||
? null
|
||||
: now()->addYear()->toIso8601String();
|
||||
$settings['billed_branches'] = max(1, Branch::query()
|
||||
->where('organization_id', $organization->id)
|
||||
->where('is_active', true)
|
||||
->count());
|
||||
$settings['demo'] = true;
|
||||
$organization->forceFill(['settings' => $settings])->save();
|
||||
}
|
||||
|
||||
public function resetForOwner(string $ownerRef): void
|
||||
{
|
||||
DB::transaction(function () use ($ownerRef) {
|
||||
$orgIds = Organization::withTrashed()->where('owner_ref', $ownerRef)->pluck('id');
|
||||
if ($orgIds->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$ticketIds = Ticket::withTrashed()->where('owner_ref', $ownerRef)->pluck('id');
|
||||
$queueIds = ServiceQueue::withTrashed()->where('owner_ref', $ownerRef)->pluck('id');
|
||||
$counterIds = Counter::withTrashed()->where('owner_ref', $ownerRef)->pluck('id');
|
||||
$workflowIds = Workflow::withTrashed()->where('owner_ref', $ownerRef)->pluck('id');
|
||||
|
||||
CustomerFeedback::query()->where('owner_ref', $ownerRef)->delete();
|
||||
VoiceAnnouncement::query()->where('owner_ref', $ownerRef)->delete();
|
||||
ServiceSession::query()->where('owner_ref', $ownerRef)->delete();
|
||||
TicketTransfer::query()->where('owner_ref', $ownerRef)->delete();
|
||||
TicketEvent::query()->where('owner_ref', $ownerRef)->delete();
|
||||
QueueAppointment::withTrashed()->where('owner_ref', $ownerRef)->forceDelete();
|
||||
|
||||
if ($ticketIds->isNotEmpty()) {
|
||||
Ticket::withTrashed()->whereIn('id', $ticketIds)->forceDelete();
|
||||
}
|
||||
|
||||
QueueRule::query()->where('owner_ref', $ownerRef)->delete();
|
||||
|
||||
if ($workflowIds->isNotEmpty()) {
|
||||
WorkflowStep::query()->whereIn('workflow_id', $workflowIds)->delete();
|
||||
Workflow::withTrashed()->whereIn('id', $workflowIds)->forceDelete();
|
||||
}
|
||||
|
||||
DisplayScreen::withTrashed()->where('owner_ref', $ownerRef)->forceDelete();
|
||||
Device::withTrashed()->where('owner_ref', $ownerRef)->forceDelete();
|
||||
|
||||
DB::table('queue_staff_sessions')->where('owner_ref', $ownerRef)->delete();
|
||||
DB::table('queue_counter_assignments')->where('owner_ref', $ownerRef)->delete();
|
||||
|
||||
if ($counterIds->isNotEmpty()) {
|
||||
DB::table('queue_counter_service_queue')->whereIn('counter_id', $counterIds)->delete();
|
||||
Counter::withTrashed()->whereIn('id', $counterIds)->forceDelete();
|
||||
}
|
||||
|
||||
if ($queueIds->isNotEmpty()) {
|
||||
ServiceQueue::withTrashed()->whereIn('id', $queueIds)->forceDelete();
|
||||
}
|
||||
|
||||
Department::withTrashed()->where('owner_ref', $ownerRef)->forceDelete();
|
||||
Member::query()->where('owner_ref', $ownerRef)->delete();
|
||||
AuditLog::query()->where('owner_ref', $ownerRef)->delete();
|
||||
Branch::withTrashed()->where('owner_ref', $ownerRef)->forceDelete();
|
||||
Organization::withTrashed()->where('owner_ref', $ownerRef)->forceDelete();
|
||||
});
|
||||
}
|
||||
|
||||
private function ensureOrganization(User $user): Organization
|
||||
{
|
||||
$existing = $this->orgs->resolveForUser($user);
|
||||
if ($existing) {
|
||||
$settings = $existing->settings ?? [];
|
||||
$settings['onboarded'] = true;
|
||||
$existing->forceFill(['settings' => $settings])->save();
|
||||
$this->orgs->ensureOwnerMember($user, $existing);
|
||||
|
||||
return $existing;
|
||||
}
|
||||
|
||||
return $this->orgs->completeOnboarding($user, [
|
||||
'organization_name' => 'Demo Queue Org',
|
||||
'industry' => 'retail',
|
||||
'appointment_mode' => 'hybrid',
|
||||
'branch_name' => 'Main Branch',
|
||||
'timezone' => 'Africa/Accra',
|
||||
]);
|
||||
}
|
||||
|
||||
/** @return list<Branch> */
|
||||
private function seedBranches(string $ownerRef, Organization $organization, int $count): array
|
||||
{
|
||||
$branches = Branch::query()
|
||||
->where('organization_id', $organization->id)
|
||||
->whereNull('deleted_at')
|
||||
->orderBy('id')
|
||||
->get()
|
||||
->all();
|
||||
|
||||
$names = ['Main Branch', 'East Legon', 'Kumasi Central', 'Tema Harbour', 'Takoradi Mall'];
|
||||
for ($i = count($branches); $i < $count; $i++) {
|
||||
$branches[] = Branch::query()->create([
|
||||
'owner_ref' => $ownerRef,
|
||||
'organization_id' => $organization->id,
|
||||
'name' => $names[$i] ?? ('Branch '.($i + 1)),
|
||||
'code' => 'BR'.($i + 1),
|
||||
'address' => 'Demo address '.($i + 1).', Accra',
|
||||
'phone' => '0302'.str_pad((string) (100000 + $i), 6, '0', STR_PAD_LEFT),
|
||||
'is_active' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
return array_slice($branches, 0, $count);
|
||||
}
|
||||
|
||||
private function ensureDepartment(string $ownerRef, Branch $branch): Department
|
||||
{
|
||||
return Department::query()->firstOrCreate(
|
||||
['branch_id' => $branch->id, 'name' => 'General Reception'],
|
||||
[
|
||||
'owner_ref' => $ownerRef,
|
||||
'type' => 'reception',
|
||||
'is_active' => true,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<ServiceQueue>
|
||||
*/
|
||||
private function seedQueues(
|
||||
string $ownerRef,
|
||||
Organization $organization,
|
||||
Branch $branch,
|
||||
Department $department,
|
||||
int $count,
|
||||
string $plan,
|
||||
): array {
|
||||
$queues = ServiceQueue::query()
|
||||
->where('branch_id', $branch->id)
|
||||
->whereNull('deleted_at')
|
||||
->orderBy('id')
|
||||
->get()
|
||||
->all();
|
||||
|
||||
for ($i = count($queues); $i < $count; $i++) {
|
||||
[$name, $prefix] = self::QUEUE_NAMES[$i % count(self::QUEUE_NAMES)];
|
||||
$strategy = $plan === 'free'
|
||||
? 'fifo'
|
||||
: ['fifo', 'priority', 'vip', 'overflow', 'dynamic'][$i % 5];
|
||||
|
||||
$queues[] = ServiceQueue::query()->create([
|
||||
'owner_ref' => $ownerRef,
|
||||
'organization_id' => $organization->id,
|
||||
'branch_id' => $branch->id,
|
||||
'department_id' => $department->id,
|
||||
'name' => $name.($i >= count(self::QUEUE_NAMES) ? ' '.($i + 1) : ''),
|
||||
'prefix' => $prefix.($i >= count(self::QUEUE_NAMES) ? (string) ($i + 1) : ''),
|
||||
'strategy' => $strategy,
|
||||
'avg_service_seconds' => 240 + ($i * 30),
|
||||
'is_active' => true,
|
||||
'ticket_sequence' => 0,
|
||||
]);
|
||||
}
|
||||
|
||||
return array_slice($queues, 0, $count);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<ServiceQueue> $queues
|
||||
* @return list<Counter>
|
||||
*/
|
||||
private function seedCounters(
|
||||
string $ownerRef,
|
||||
Organization $organization,
|
||||
Branch $branch,
|
||||
Department $department,
|
||||
array $queues,
|
||||
int $count,
|
||||
): array {
|
||||
$counters = Counter::query()
|
||||
->where('branch_id', $branch->id)
|
||||
->whereNull('deleted_at')
|
||||
->orderBy('id')
|
||||
->get()
|
||||
->all();
|
||||
|
||||
for ($i = count($counters); $i < $count; $i++) {
|
||||
$counter = Counter::query()->create([
|
||||
'owner_ref' => $ownerRef,
|
||||
'organization_id' => $organization->id,
|
||||
'branch_id' => $branch->id,
|
||||
'department_id' => $department->id,
|
||||
'name' => 'Counter '.($i + 1),
|
||||
'code' => 'C'.($i + 1),
|
||||
'status' => $i === 0 ? 'available' : 'busy',
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
if ($queues !== []) {
|
||||
$queue = $queues[$i % count($queues)];
|
||||
$counter->serviceQueues()->syncWithoutDetaching([$queue->id => ['priority' => 0]]);
|
||||
}
|
||||
|
||||
$counters[] = $counter;
|
||||
}
|
||||
|
||||
return array_slice($counters, 0, $count);
|
||||
}
|
||||
|
||||
/** @param list<ServiceQueue> $queues */
|
||||
private function seedDisplay(string $ownerRef, Organization $organization, Branch $branch, array $queues): void
|
||||
{
|
||||
if (DisplayScreen::query()->where('branch_id', $branch->id)->whereNull('deleted_at')->exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayScreen::query()->create([
|
||||
'owner_ref' => $ownerRef,
|
||||
'organization_id' => $organization->id,
|
||||
'branch_id' => $branch->id,
|
||||
'name' => $branch->name.' Display',
|
||||
'layout' => 'standard',
|
||||
'service_queue_ids' => array_map(fn (ServiceQueue $q) => $q->id, $queues),
|
||||
'is_active' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
private function seedDevice(string $ownerRef, Organization $organization, Branch $branch): void
|
||||
{
|
||||
if (Device::query()->where('branch_id', $branch->id)->whereNull('deleted_at')->exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Device::query()->create([
|
||||
'owner_ref' => $ownerRef,
|
||||
'organization_id' => $organization->id,
|
||||
'branch_id' => $branch->id,
|
||||
'name' => $branch->name.' Kiosk',
|
||||
'type' => 'kiosk',
|
||||
'status' => 'online',
|
||||
'device_token' => Str::random(40),
|
||||
'last_online_at' => now(),
|
||||
]);
|
||||
}
|
||||
|
||||
private function seedLiveTickets(
|
||||
string $ownerRef,
|
||||
Organization $organization,
|
||||
Branch $branch,
|
||||
ServiceQueue $queue,
|
||||
?Counter $counter,
|
||||
int $count,
|
||||
): void {
|
||||
$existing = Ticket::query()
|
||||
->where('service_queue_id', $queue->id)
|
||||
->whereIn('status', ['waiting', 'called', 'serving'])
|
||||
->count();
|
||||
|
||||
for ($i = $existing; $i < $count; $i++) {
|
||||
$seq = $queue->ticket_sequence + 1;
|
||||
$queue->forceFill(['ticket_sequence' => $seq])->save();
|
||||
$status = ['waiting', 'waiting', 'called', 'serving'][$i % 4];
|
||||
|
||||
Ticket::query()->create([
|
||||
'owner_ref' => $ownerRef,
|
||||
'organization_id' => $organization->id,
|
||||
'branch_id' => $branch->id,
|
||||
'service_queue_id' => $queue->id,
|
||||
'counter_id' => in_array($status, ['called', 'serving'], true) ? $counter?->id : null,
|
||||
'ticket_number' => $queue->prefix.str_pad((string) $seq, 3, '0', STR_PAD_LEFT),
|
||||
'status' => $status,
|
||||
'priority' => ['walk_in', 'vip', 'elderly', 'appointment'][$i % 4],
|
||||
'position' => $i + 1,
|
||||
'customer_name' => 'Guest '.($i + 1),
|
||||
'customer_phone' => '024'.str_pad((string) (3000000 + $i), 7, '0', STR_PAD_LEFT),
|
||||
'source' => ['kiosk', 'reception', 'web'][$i % 3],
|
||||
'issued_at' => now()->subMinutes(30 - $i),
|
||||
'called_at' => in_array($status, ['called', 'serving'], true) ? now()->subMinutes(5) : null,
|
||||
'serving_started_at' => $status === 'serving' ? now()->subMinutes(2) : null,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
private function seedHistoryTickets(
|
||||
string $ownerRef,
|
||||
Organization $organization,
|
||||
Branch $branch,
|
||||
?ServiceQueue $queue,
|
||||
?Counter $counter,
|
||||
int $count,
|
||||
): void {
|
||||
if (! $queue) {
|
||||
return;
|
||||
}
|
||||
|
||||
$existing = Ticket::query()
|
||||
->where('service_queue_id', $queue->id)
|
||||
->whereIn('status', ['completed', 'no_show', 'cancelled'])
|
||||
->count();
|
||||
|
||||
for ($i = $existing; $i < $count; $i++) {
|
||||
$seq = $queue->ticket_sequence + 1;
|
||||
$queue->forceFill(['ticket_sequence' => $seq])->save();
|
||||
$status = ['completed', 'completed', 'completed', 'no_show', 'cancelled'][$i % 5];
|
||||
|
||||
$ticket = Ticket::query()->create([
|
||||
'owner_ref' => $ownerRef,
|
||||
'organization_id' => $organization->id,
|
||||
'branch_id' => $branch->id,
|
||||
'service_queue_id' => $queue->id,
|
||||
'counter_id' => $counter?->id,
|
||||
'ticket_number' => $queue->prefix.str_pad((string) $seq, 3, '0', STR_PAD_LEFT),
|
||||
'status' => $status,
|
||||
'priority' => 'walk_in',
|
||||
'customer_name' => 'History Guest '.($i + 1),
|
||||
'source' => 'kiosk',
|
||||
'issued_at' => now()->subDays(($i % 14) + 1)->subMinutes(40),
|
||||
'called_at' => now()->subDays(($i % 14) + 1)->subMinutes(20),
|
||||
'serving_started_at' => $status === 'completed' ? now()->subDays(($i % 14) + 1)->subMinutes(15) : null,
|
||||
'completed_at' => $status === 'completed' ? now()->subDays(($i % 14) + 1)->subMinutes(5) : null,
|
||||
]);
|
||||
|
||||
TicketEvent::query()->create([
|
||||
'owner_ref' => $ownerRef,
|
||||
'ticket_id' => $ticket->id,
|
||||
'event' => 'issued',
|
||||
'actor_ref' => $ownerRef,
|
||||
'created_at' => $ticket->issued_at,
|
||||
]);
|
||||
|
||||
if ($status === 'completed') {
|
||||
TicketEvent::query()->create([
|
||||
'owner_ref' => $ownerRef,
|
||||
'ticket_id' => $ticket->id,
|
||||
'event' => 'completed',
|
||||
'actor_ref' => $ownerRef,
|
||||
'counter_id' => $counter?->id,
|
||||
'created_at' => $ticket->completed_at,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @param list<ServiceQueue> $queues */
|
||||
private function seedRouting(string $ownerRef, array $queues): void
|
||||
{
|
||||
$from = $queues[0];
|
||||
if (QueueRule::query()->where('service_queue_id', $from->id)->exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QueueRule::query()->create([
|
||||
'owner_ref' => $ownerRef,
|
||||
'service_queue_id' => $from->id,
|
||||
'rule_type' => 'overflow',
|
||||
'conditions' => ['waiting_above' => 15],
|
||||
'actions' => ['route_to_queue_id' => $queues[1]->id],
|
||||
'sort_order' => 1,
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
QueueRule::query()->create([
|
||||
'owner_ref' => $ownerRef,
|
||||
'service_queue_id' => $from->id,
|
||||
'rule_type' => 'routing',
|
||||
'conditions' => ['priority' => 'vip'],
|
||||
'actions' => ['prefer_counter' => true],
|
||||
'sort_order' => 2,
|
||||
'is_active' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<ServiceQueue> $queues
|
||||
* @param list<Counter> $counters
|
||||
*/
|
||||
private function seedTransfers(
|
||||
string $ownerRef,
|
||||
Organization $organization,
|
||||
Branch $branch,
|
||||
array $queues,
|
||||
array $counters,
|
||||
): void {
|
||||
if (count($queues) < 2 || TicketTransfer::query()->where('owner_ref', $ownerRef)->exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$ticket = Ticket::query()->create([
|
||||
'owner_ref' => $ownerRef,
|
||||
'organization_id' => $organization->id,
|
||||
'branch_id' => $branch->id,
|
||||
'service_queue_id' => $queues[1]->id,
|
||||
'counter_id' => $counters[0]->id ?? null,
|
||||
'ticket_number' => $queues[1]->prefix.'T01',
|
||||
'status' => 'transferred',
|
||||
'priority' => 'walk_in',
|
||||
'customer_name' => 'Transferred Guest',
|
||||
'source' => 'reception',
|
||||
'issued_at' => now()->subHour(),
|
||||
]);
|
||||
|
||||
TicketTransfer::query()->create([
|
||||
'owner_ref' => $ownerRef,
|
||||
'ticket_id' => $ticket->id,
|
||||
'from_service_queue_id' => $queues[0]->id,
|
||||
'to_service_queue_id' => $queues[1]->id,
|
||||
'from_counter_id' => $counters[0]->id ?? null,
|
||||
'reason' => 'Needs specialist desk',
|
||||
'transferred_by' => $ownerRef,
|
||||
'transferred_at' => now()->subMinutes(30),
|
||||
]);
|
||||
}
|
||||
|
||||
/** @param list<ServiceQueue> $queues */
|
||||
private function seedWorkflow(string $ownerRef, Organization $organization, Branch $branch, array $queues): void
|
||||
{
|
||||
if (Workflow::query()->where('organization_id', $organization->id)->whereNull('deleted_at')->exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$workflow = Workflow::query()->create([
|
||||
'owner_ref' => $ownerRef,
|
||||
'organization_id' => $organization->id,
|
||||
'branch_id' => $branch->id,
|
||||
'name' => 'Demo service journey',
|
||||
'industry_template' => 'retail',
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
foreach (array_slice($queues, 0, min(3, count($queues))) as $i => $queue) {
|
||||
WorkflowStep::query()->create([
|
||||
'workflow_id' => $workflow->id,
|
||||
'service_queue_id' => $queue->id,
|
||||
'name' => $queue->name,
|
||||
'sort_order' => $i + 1,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
private function normalizePlan(string $plan): string
|
||||
{
|
||||
$plan = strtolower(trim($plan));
|
||||
if (! in_array($plan, ['free', 'pro', 'enterprise'], true)) {
|
||||
throw new \InvalidArgumentException('Plan must be free, pro, or enterprise.');
|
||||
}
|
||||
|
||||
return $plan;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user