HEX
Server: Apache/2.4.41 (Ubuntu)
System: Linux ip-172-31-42-149 5.15.0-1084-aws #91~20.04.1-Ubuntu SMP Fri May 2 07:00:04 UTC 2025 aarch64
User: ubuntu (1000)
PHP: 7.4.33
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
Upload Files
File: /var/www/vhost/disk-apps/qas.sports-crowd.com/app/Core/Ticket/Application/TransferService.php
<?php

declare(strict_types=1);

namespace App\Core\Ticket\Application;

use App\Core\Ticket\Entities\TransferStatusEnum;
use App\Core\Ticket\TicketStatusEnum;
use App\Mail\TransferTicket;
use App\Services\TicketParametersService;
use App\Ticket;
use App\TicketUserLog;
use App\User;
use Carbon\Carbon;
use Exception;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Str;

class TransferService
{
    private $ticketParameterService;

    public function __construct()
    {
        $this->ticketParameterService = new TicketParametersService();
    }

    public function batchTransfer(
        $userId,
        $ticketCount,
        $recipientEmail,
        $matchEventId,
        $zone,
        $ticketIds = []
    ) {
        $recipient = User::where('email', $recipientEmail)->first();

        $availableTickets = Ticket::where('user_id', $userId)
            ->where('match_event_id', $matchEventId)
            ->where('zone', $zone)
            ->whereIn('ticket_status_id', [TicketStatusEnum::PURCHASED, TicketStatusEnum::CREATED])
            ->whereNotIn('id', TicketUserLog::where('status', TransferStatusEnum::PENDING)->pluck('ticket_id'))
            ->limit($ticketCount);

        if (!empty($ticketIds)) {
            $availableTickets = $availableTickets->whereIn('id', $ticketIds);
        }

        $availableTickets = $availableTickets->get();

        if ($availableTickets->count() < $ticketCount) {
            throw new Exception(__('transfer_tickets.not_enough_tickets'), 400);
        }

        $transferGroup = Str::uuid();

        $timeToAcceptComplimentaryTickets = $this->ticketParameterService->timeToAcceptComplimentaryTickets();
        DB::transaction(function () use ($availableTickets, $userId, $recipient, $transferGroup, $recipientEmail, $timeToAcceptComplimentaryTickets) {
            foreach ($availableTickets as $ticket) {
                TicketUserLog::create([
                    'ticket_id' => $ticket->id,
                    'previous_user_id' => $userId,
                    'new_user_id' => $recipient->id ?? null,
                    'email' => $recipientEmail,
                    'expires_at' => Carbon::now()->addMinutes($timeToAcceptComplimentaryTickets),
                    'status' => TransferStatusEnum::PENDING,
                    'transfer_group' => $transferGroup,
                    'number_transfers' => ++$ticket->number_transfers ?? 1,
                    'payment_reference' => $ticket->ticket_main->payment_reference
                ]);
            }
        });

        try {
            Mail::send(new TransferTicket($transferGroup));
        } catch (Exception $e) {
            throw new Exception(__('transfer_tickets.email_not_sent'), 500);
        }

        return ['transfer_group' => $transferGroup];
    }

    public function acceptTransfer($user, $transferGroup)
    {
        $transfers = TicketUserLog::where('transfer_group', $transferGroup)
            ->where(function ($query) use ($user) {
                $query->where('new_user_id', $user->id)->orWhere('email', $user->email);
            })
            ->where('status', TransferStatusEnum::PENDING)
            ->where('expires_at', '>', Carbon::now())
            ->get();

        if ($transfers->isEmpty()) {
            throw new Exception(__('transfer_tickets.transfer_not_found_expired'), 404);
        }

        DB::transaction(function () use ($user, $transfers) {
            foreach ($transfers as $transfer) {
                if (in_array($transfer->ticket->ticket_status_id, [TicketStatusEnum::PURCHASED, TicketStatusEnum::CREATED])) {
                    $transfer->ticket->update(['user_id' => $user->id, 'number_transfers' => $transfer->number_transfers]);
                    $transfer->update(['status' => TransferStatusEnum::ACCEPTED, 'new_user_id' => $user->id]);
                } else {
                    $transfer->update(['status' => TransferStatusEnum::DECLINED]);
                }
            }
        });
    }

    public function declineTransfer($userId, $transferGroup)
    {
        $transfers = TicketUserLog::where('transfer_group', $transferGroup)
            ->where('previous_user_id', $userId)
            ->where('status', TransferStatusEnum::PENDING)
            ->get();

        if ($transfers->isEmpty()) {
            throw new Exception(__('transfer_tickets.transfer_not_found'), 404);
        }

        DB::transaction(function () use ($transfers) {
            foreach ($transfers as $transfer) {
                $transfer->update(['status' => TransferStatusEnum::DECLINED]);
            }
        });
    }

    public function reassignTransfer($userId, $transferGroup, $newUserEmail)
    {
        $transfers = TicketUserLog::where('transfer_group', $transferGroup)
            ->where('previous_user_id', $userId)
            ->where('expires_at', '<', Carbon::now())
            ->whereIn('status', [TransferStatusEnum::PENDING, TransferStatusEnum::DECLINED, TransferStatusEnum::EXPIRED])
            ->get();

        if ($transfers->isEmpty()) {
            throw new Exception(__('transfer_tickets.transfer_cannot_be_reassigned'), 403);
        }

        $newUser = User::where('email', $newUserEmail)->firstOrFail();

        DB::transaction(function () use ($transfers, $newUser, $newUserEmail) {
            foreach ($transfers as $transfer) {
                $transfer->update([
                    'new_user_id' => $newUser->id ?? null,
                    'email' => $newUserEmail,
                    'expires_at' => Carbon::now()->addMinutes(30),
                    'status' => TransferStatusEnum::PENDING
                ]);
            }
        });

        Mail::send(new TransferTicket($transferGroup));
    }

    public function reverseTransfer($userId, $transferGroup)
    {
        $transfers = TicketUserLog::where('transfer_group', $transferGroup)
            ->where('previous_user_id', $userId)
            ->whereIn('status', [TransferStatusEnum::PENDING, TransferStatusEnum::DECLINED, TransferStatusEnum::EXPIRED])
            ->get();

        if ($transfers->isEmpty()) {
            throw new Exception(__('transfer_tickets.transfer_cannot_be_reversed'), 403);
        }

        DB::transaction(function () use ($transfers) {
            foreach ($transfers as $transfer) {
                $transfer->update(['status' => TransferStatusEnum::EXPIRED]);
            }
        });
    }

    public function expireTransfers()
    {
        DB::transaction(function () {
            $expiredTransfers = TicketUserLog::where('status', TransferStatusEnum::PENDING)
                ->where('expires_at', '<', Carbon::now())
                ->get();

            foreach ($expiredTransfers as $transfer) {
                $transfer->update(['status' => TransferStatusEnum::EXPIRED]);
            }
        });
    }

    public function getPendingTransfersByUser($user)
    {
        return TicketUserLog::select([
            'transfer_group',
            'previous_user_id',
            'new_user_id',
            DB::raw('COUNT(ticket_id) as ticket_count'),
            DB::raw('MAX(expires_at) as expires_at'),
        ])
            ->join('tickets', 'tickets.id', '=', 'ticket_user_logs.ticket_id')
            ->whereIn('tickets.ticket_status_id', [TicketStatusEnum::PURCHASED, TicketStatusEnum::CREATED])
            ->where('status', TransferStatusEnum::PENDING)
            ->where(function ($query) use ($user) {
                $query->where('new_user_id', $user->id)->orWhere('email', $user->email);
            })
            ->groupBy('transfer_group', 'previous_user_id', 'new_user_id')
            ->get();
    }

    public function getPendingBatchTransfersByUser($userId)
    {
        return TicketUserLog::select([
            'id',
            'ticket_id',
            'previous_user_id',
            'new_user_id',
            'payment_reference',
            'number_transfers',
            'status',
            'expires_at',
            'transfer_group',
        ])
            ->where('previous_user_id', $userId)
            ->orWhere('new_user_id', $userId)
            ->with('ticket:id,code_ticket,zone,match_event_id')
            ->get();
    }

    public function getMatchEventByTransferGroupId($transferGroupId)
    {
        return TicketUserLog::distinct()->select([
            'match_events.id',
            'match_events.name',
            'match_events.event_start',
            'match_events.stadium_to_play',
            'tickets.zone',
        ])
            ->join('tickets', 'tickets.id', '=', 'ticket_user_logs.ticket_id')
            ->join('match_events', 'match_events.id', '=', 'tickets.match_event_id')
            ->where('transfer_group', $transferGroupId)
            ->groupBy('match_events.id', 'tickets.zone')
            ->first();
    }

    public function countTicketsTransferByTransferGroup($transferGroup)
    {
        return TicketUserLog::where('transfer_group', $transferGroup)->count();
    }
}