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/demo.sports-crowd.com/app/Http/Controllers/ReportInternalController.php
<?php

namespace App\Http\Controllers;

use App\Core\Ticket\ConfirmStadiumEnum;
use App\Core\Ticket\TicketStatusEnum;
use App\Core\Ticket\TicketTypesEnum;
use App\MatchEvent;
use App\Services\TicketParametersService;
use Carbon\Carbon;
use PdfReport;
use CSVReport;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\DB;
use Webklex\PDFMerger\Facades\PDFMergerFacade as PDFMerger;

class ReportInternalController extends Controller
{

    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        $events = MatchEvent::select('id', 'name', 'event_start', 'active')->orderBy('active', 'desc')->orderBy('created_at', 'desc')->get();
        return view("reports_internal.reports_internal", compact('events'));
    }

    public function generateReport($type, $event_id)
    {
        ini_set('memory_limit', '2048M');
        switch ($type) {
            case 'anulada':
                return $this->reportReversedAndReject($event_id);
                break;
            case 'cortesia':
                return $this->reportCourtesy($event_id);
                break;
            case 'vendida':
                return $this->reportTicketsSold($event_id);
                break;
            case 'consolidado':
                return $this->reportTicketsConsolidated($event_id);
                break;
            case 'consolidadoPDF':
                return $this->reportTicketsConsolidatedPDF($event_id);
                break;
            case 'venta-libre-y-credito':
                return $this->reportTicketsOverTheCounterAndCredit($event_id);
                break;
            case 'abonado':
                return $this->reportTicketsSubscriber(['event_id' => $event_id, 'group_by_special_text' => false]);
                break;
            case 'ingreso-tribuna':
                return $this->reportTicketsByGrandstand($event_id);
                break;
            case 'abonado-por-etiqueta':
                return $this->reportTicketsSubscriber(['event_id' => $event_id, 'group_by_special_text' => true]);
                break;
        }
    }

    public function reportReversedAndReject($event_id)
    {
        $title = "Ticket reversados y anulados";
        $event = MatchEvent::find($event_id);
        $meta = [ // For displaying filters description on header
            'Evento' => $title . ' - ' . $event->name,
            'Fecha impresión reporte' => Carbon::now()->format('d/m/Y H:i:s'),
            'Tipo' => 'Tickets Reversados y anulados'
        ];

        // Query a mostrar en el reporte
        $queryBuilder = DB::table('tickets')
            ->select([
                'tickets.id',
                'match_events.name',
                'match_events.code',
                'tickets.zone',
                'tickets.code_ticket',
                'tickets.special_text',
                'tickets.code_seat',
                'tickets.price',
                'tickets.created_at AS ticket_issue_date',
                'users.first_name',
                'users.last_name',
                'users.document',
                'ticket_types.name AS type_ticket',
                's.zone_id',
                'tickets.letter_seat as letter',
                'z.zone_id as id_sub_zone',
                'zsub.name as tribune',
                'ticket_mains.payment_reference'
            ])
            ->join('ticket_mains', 'tickets.ticket_main_id', '=', 'ticket_mains.id')
            ->join('match_events', 'tickets.match_event_id', '=', 'match_events.id')
            ->join('users', 'tickets.user_id', '=', 'users.id')
            ->join('ticket_types', 'tickets.ticket_type_id', '=', 'ticket_types.id')
            ->join('seats as s', 'tickets.seat_id', '=', 's.id')
            ->join('zones as z', 's.zone_id', '=', 'z.id')
            ->join('zones as zsub', 'z.zone_id', '=', 'zsub.id')
            ->where('tickets.match_event_id', $event_id)
            ->whereIn('tickets.ticket_status_id', [TicketStatusEnum::CANCELLED, TicketStatusEnum::REVERSED]);

        $columns = [
            'ID'                    => 'id',
            'Nombre Evento'         => 'name',
            'Codigo evento'         => 'code',
            'Tribuna'               => 'tribune',
            'Sector'                => 'zone',
            'Silla'                 => 'code_seat',
            'Fila'                  => 'letter',
            'Tipo de boleta'        => 'type_ticket',
            'Código ticket'         => 'code_ticket',
            'Boleta Especial'       => 'special_text',
            'Ref. de pago'          => 'payment_reference',
            'Precio'                => 'price',
            'Fecha emisión boleta'  => 'ticket_issue_date',
            'Nombres Usuario'       => function ($obj) {
                return $this->clean($obj->first_name);
            },
            'Apellidos Usuario'     => function ($obj) {
                return $this->clean($obj->last_name);
            },
            'Documento'             => 'document',
        ];

        $name = stripslashes(str_replace('/', '', $event->name));
        return CSVReport::of($title, $meta, $queryBuilder, $columns)->download($name . ' - Interno - Reversados y anulados');
    }

    public function reportCourtesy($event_id)
    {
        $title = "Tickets de cortesía";
        $event = MatchEvent::find($event_id);
        $meta = [ // For displaying filters description on header
            'Evento' => $title . ' - ' . $event->name,
            'Fecha impresión reporte' => Carbon::now()->format('d/m/Y H:i:s'),
            'Tipo' => 'Tickets cortesia'
        ];

        // Query a mostrar en el reporte
        $queryBuilder = DB::table('tickets')
            ->select([
                'tickets.id',
                'match_events.name',
                'match_events.code',
                'tickets.zone',
                'tickets.code_ticket',
                'tickets.special_text',
                'tickets.code_seat',
                'tickets.price',
                'tickets.confirm_stadium_ticket',
                'tickets.created_at AS ticket_issue_date',
                'users.first_name',
                'users.last_name',
                'users.document',
                'users.email',
                'ticket_types.name AS type_ticket',
                's.zone_id',
                'tickets.letter_seat as letter',
                'z.zone_id as id_sub_zone',
                'zsub.name as tribune',
                'tm.invoice_number',
                'tm.cufe',
                'tm.credit_note_invoice_number',
                'tm.cude',
                DB::raw('CONCAT(billingUser.first_name, " ", billingUser.last_name) AS billingUserFullName'),
                'billingUser.email AS billingUserEmail',
                'billingUser.document AS billingUserDocument',
            ])
            ->join('ticket_mains as tm', 'tickets.ticket_main_id', '=', 'tm.id')
            ->leftJoin('users as billingUser', 'tm.user_id_log', '=', 'billingUser.id')
            ->join('match_events', 'tickets.match_event_id', '=', 'match_events.id')
            ->join('users', 'tickets.user_id', '=', 'users.id')
            ->join('ticket_types', 'tickets.ticket_type_id', '=', 'ticket_types.id')
            ->join('seats as s', 'tickets.seat_id', '=', 's.id')
            ->join('zones as z', 's.zone_id', '=', 'z.id')
            ->join('zones as zsub', 'z.zone_id', '=', 'zsub.id')
            ->where('tickets.match_event_id', $event_id)
            ->whereIn('tickets.ticket_status_id', [TicketStatusEnum::PURCHASED, TicketStatusEnum::CREATED])
            ->whereIn('tickets.ticket_type_id', [TicketTypesEnum::COMPLIMENTARY])
            ->groupBy('tickets.id')
            ->orderBy('ticket_issue_date', 'DESC');

        $columns = [
            'ID'                    => 'id',
            'Nombre Evento'         => 'name',
            'Codigo evento'         => 'code',
            'Tribuna'               => 'tribune',
            'Sector'                => 'zone',
            'Silla'                 => 'code_seat',
            'Fila'                  => 'letter',
            'Tipo de boleta'        => 'type_ticket',
            'Código ticket'         => 'code_ticket',
            'Boleta Especial'       => 'special_text',
            'Precio'                => 'price',
            'Ingreso al estadio'    => 'confirm_stadium_ticket',
            'Fecha emisión boleta'  => 'ticket_issue_date',
            'Nombres Usuario'       => function ($obj) {
                return $this->clean($obj->first_name);
            },
            'Apellidos Usuario'     => function ($obj) {
                return $this->clean($obj->last_name);
            },
            'Documento'             => 'document',
            'Email'                 => 'email',
        ];

        $ticketParametersService = new TicketParametersService;
        $enableElectronicInvoiceCreation = $ticketParametersService->validateElectronicInvoiceCreation();
        if ($enableElectronicInvoiceCreation) {
            $columns['Usuario de facturación'] = 'billingUserFullName';
            $columns['Correo de facturación'] = 'billingUserEmail';
            $columns['Documento de facturación'] = 'billingUserDocument';
            $columns['Número de factura electrónica'] = 'invoice_number';
            $columns['CUFE'] = 'cufe';
            $columns['Número de nota de crédito'] = 'credit_note_invoice_number';
            $columns['CUDE'] = 'cude';
        }

        $name = stripslashes(str_replace('/', '', $event->name));
        return CSVReport::of($title, $meta, $queryBuilder, $columns)->download($name . ' - Interno - Cortesias');
    }

    public function reportTicketsSold($event_id)
    {
        $title = "Ticket vendidos";
        $event = MatchEvent::find($event_id);

        $meta = [ // For displaying filters description on header
            'Evento' => $title . ' - ' . $event->name,
            'Fecha impresión reporte' => Carbon::now()->format('d/m/Y H:i:s'),
            'Tipo' => 'Tickets vendidos'
        ];

        $ticketParametersService = new TicketParametersService;
        $iva = 0;
        if ($ticketParametersService->enableElectronicInvoiceServiceCharge()) {
            $iva = $ticketParametersService->ivaPercentageServiceCharge();
        }

        // Query a mostrar en el reporte
        $queryBuilder = DB::table('tickets')
            ->select([
                'tickets.id',
                'match_events.name',
                'match_events.code',
                'tickets.zone',
                'tickets.code_ticket',
                'tickets.special_text',
                'tickets.code_seat',
                'tickets.price',
                DB::raw('DATE(tickets.created_at) AS ticket_issue_date'),
                DB::raw('TIME(tickets.created_at) AS ticket_issue_time'),
                'tickets.confirm_stadium_ticket',
                'users.first_name',
                'users.last_name',
                'users.document',
                'dt.name AS document_type',
                'ticket_types.name AS type_ticket',
                's.zone_id',
                'tickets.letter_seat as letter',
                'z.zone_id as id_sub_zone',
                'zsub.name as tribune',
                DB::raw('IF(tm.pin_tercero IS NOT NULL AND tm.reference_tercero IS NOT NULL, "Tercero", "Pasarela") as f_pago'),
                DB::raw('IF(tm.payment_reference IS NOT NULL, tm.payment_reference, tm.reference_tercero) as payment_reference'),
                'users.email',
                'users.phone',
                DB::raw('CONCAT(sellerUser.first_name, " ", sellerUser.last_name) AS sellerFullName'),
                'sellerUser.email AS sellerEmail',
                'tm.total',
                DB::raw('GROUP_CONCAT(DISTINCT(tags.name)) AS segmentation'),
                'gp.name as gateway',
                'tm.invoice_number',
                'tm.cufe',
                'tm.credit_note_invoice_number',
                'tm.cude',
                DB::raw('ROUND(IF(tickets.service_charge > 0, tickets.service_charge, ROUND(tm.service_charge / (SELECT COUNT(*) FROM tickets t2 WHERE t2.ticket_main_id = tm.id), 0))/(1 + ' . $iva . '/100), 2) as service_charge'),
                DB::raw('ROUND(
                            (
                                IF(
                                    tickets.service_charge > 0,
                                    tickets.service_charge,
                                    ROUND(tm.service_charge / (SELECT COUNT(*) FROM tickets t2 WHERE t2.ticket_main_id = tm.id), 0)
                                )
                                - (
                                    IF(
                                        tickets.service_charge > 0,
                                        tickets.service_charge,
                                        ROUND(tm.service_charge / (SELECT COUNT(*) FROM tickets t2 WHERE t2.ticket_main_id = tm.id), 0)
                                    ) / (1 + ' . $iva . '/100)
                                )
                            ),
                        2) as iva_service_charge'),
                DB::raw('CONCAT(billingUser.first_name, " ", billingUser.last_name) AS billingUserFullName'),
                'billingUser.email AS billingUserEmail',
                'billingUser.document AS billingUserDocument',
            ])
            ->selectRaw('MAX(ui.dob) as dob')
            ->join('ticket_mains as tm', 'tickets.ticket_main_id', '=', 'tm.id')
            ->leftJoin('users as billingUser', 'tm.user_id_log', '=', 'billingUser.id')
            ->join('match_events', 'tickets.match_event_id', '=', 'match_events.id')
            ->join('users', 'tickets.user_id', '=', 'users.id')
            ->leftJoin('document_types as dt', 'users.document_type_id', '=', 'dt.id')
            ->leftJoin('user_informations as ui', 'users.id', '=', 'ui.user_id')
            ->leftJoin('users as sellerUser', 'tm.seller_user_id', '=', 'sellerUser.id')
            ->leftJoin('user_tags', 'users.id', '=', 'user_tags.user_id')
            ->leftjoin('tags', function ($join) {
                $join->on('tags.id', '=', 'user_tags.tag_id')->where('tags.active', 1);
            })
            ->join('ticket_types', 'tickets.ticket_type_id', '=', 'ticket_types.id')
            ->join('seats as s', 'tickets.seat_id', '=', 's.id')
            ->join('zones as z', 's.zone_id', '=', 'z.id')
            ->join('zones as zsub', 'z.zone_id', '=', 'zsub.id')
            ->leftJoin('gateway_payments as gp', 'tm.gateway_payments_id', '=', 'gp.id')
            ->where('tickets.match_event_id', $event_id)
            ->whereIn('tickets.ticket_status_id', [TicketStatusEnum::PURCHASED, TicketStatusEnum::CREATED])
            ->whereIn('tickets.ticket_type_id', [TicketTypesEnum::FREE_SALE, TicketTypesEnum::SUBSCRIBER, TicketTypesEnum::CREDIT])
            ->groupBy('tickets.id')
            ->orderBy('ticket_issue_date', 'DESC');

        $columns = [
            'ID'                    => 'id',
            'Nombre Evento'         => 'name',
            'Codigo evento'         => 'code',
            'Tribuna'               => 'tribune',
            'Sector'                => 'zone',
            'Silla'                 => 'code_seat',
            'Fila'                  => 'letter',
            'Tipo de boleta'        => 'type_ticket',
            'Código ticket'         => 'code_ticket',
            'Boleta Especial'       => 'special_text',
            'Ref. de pago'          => 'payment_reference',
            'Precio'                => 'price',
            'Cobro de servicio'     => 'service_charge',
            'IVA de servicio'       => 'iva_service_charge',            
            'Fecha compra'          => 'ticket_issue_date',
            'Hora compra'           => 'ticket_issue_time',
            'Forma pago'            => 'f_pago',
            'Nombres Usuario' => function ($obj) {
                return $this->clean($obj->first_name);
            },
            'Apellidos Usuario' => function ($obj) {
                return $this->clean($obj->last_name);
            },
            'Documento'             => 'document',
            'Tipo Documento'        => 'document_type',
            'Email'                 => 'email',
            'Fecha Nacimiento'      => 'dob',
            'Telefono'              => 'phone',
            'Segmentación'          => 'segmentation',
            'Nombres Vendedor'      => 'sellerFullName',
            'Email Vendedor'        => 'sellerEmail',
            'Ingreso al estadio'    => 'confirm_stadium_ticket',
            'Precio total compra'   => 'total',
            'Pasarela de pago'      => 'gateway',
        ];

        $enableElectronicInvoiceCreation = $ticketParametersService->validateElectronicInvoiceCreation();
        if ($enableElectronicInvoiceCreation) {
            $columns['Usuario de facturación'] = 'billingUserFullName';
            $columns['Correo de facturación'] = 'billingUserEmail';
            $columns['Documento de facturación'] = 'billingUserDocument';
            $columns['Número de factura electrónica'] = 'invoice_number';
            $columns['CUFE'] = 'cufe';
            $columns['Número de nota de crédito'] = 'credit_note_invoice_number';
            $columns['CUDE'] = 'cude';
        }

        $name = stripslashes(str_replace('/', '', $event->name));
        return CSVReport::of($title, $meta, $queryBuilder, $columns)->download($name . ' - Interno - Vendidas');
    }

    public function reportTicketsConsolidated($event_id)
    {
        $title = "Reporte consolidado";
        $event = MatchEvent::find($event_id);

        $meta = [ // For displaying filters description on header
            'Evento' => $title . ' - ' . $event->name . '(' . $event->id . ')',
            'Fecha evento' => $event->event_start,
            'Código evento' => $event->code,
            'Fecha impresión reporte' => Carbon::now()->format('d/m/Y H:i:s'),
            'Tipo' => 'Tickets vendidos'
        ];

        $queryBuilder = DB::table('tickets')
            ->select(
                'match_events.name',
                'match_events.code',
                DB::raw("SUBSTRING_INDEX(z.name, '_', 1) as name_sub"),
                'zsub.name as zone',
                DB::raw('SUM(tickets.price) as total'),
                DB::raw("IF(ticket_mains.pin_tercero IS NOT NULL || ticket_mains.pin_internal IS NOT NULL, 'Efectivo', IF(tickets.ticket_type_id = 4, 'Crédito', 'Pasarela')) as f_pago"),
                DB::raw("IF(ticket_mains.payment_reference IS NOT NULL, 'Aplicación', 'Taquilla') as way_payment"),
                DB::raw('COUNT(tickets.id) as t_tickets')
            )
            ->join('ticket_mains', 'tickets.ticket_main_id', '=', 'ticket_mains.id')
            ->join('match_events', 'tickets.match_event_id', '=', 'match_events.id')
            ->join('seats as s', 'tickets.seat_id', '=', 's.id')
            ->join('zones as z', 's.zone_id', '=', 'z.id')
            ->join('zones as zsub', 'z.zone_id', '=', 'zsub.id')
            ->where('tickets.match_event_id', $event_id)
            ->whereIn('tickets.ticket_status_id', [TicketStatusEnum::PURCHASED, TicketStatusEnum::CREATED])
            ->whereIn('tickets.ticket_type_id', [TicketTypesEnum::FREE_SALE, TicketTypesEnum::SUBSCRIBER, TicketTypesEnum::CREDIT])
            ->groupBy('zsub.id', 'match_events.name', 'match_events.code', 'zsub.name', DB::raw("SUBSTRING_INDEX(z.name, '_', 1)"), 'f_pago', 'way_payment')
            ->orderBy('zsub.name', 'ASC')
            ->orderBy(DB::raw("SUBSTRING_INDEX(z.name, '_', 1)"), 'ASC');

        $columns = [
            'Nombre Evento' => 'name',
            'Codigo evento' => 'code',
            'Tribuna' => 'zone',
            'Sector' => 'name_sub',
            'Forma pago' => 'f_pago',
            'Medio de Compra' => 'way_payment',
            'Cant. Tickets' => 't_tickets',
            'Total' => 'total'
        ];

        return PdfReport::of($title, $meta, $queryBuilder, $columns)
            ->editColumns(['Total'], [ // Mass edit column
                'class' => 'right bold padding'
            ])
            ->editColumns(['Cant. Tickets'], [ // Mass edit column
                'class' => 'right bold'
            ])
            ->setCss([
                '.padding' => 'padding: 0px 0px 0px 85px;',
            ])
            ->showTotal([ // Used to sum all value on specified column on the last table (except using groupBy method). 'point' is a type for displaying total with a thousand separator
                'Total' => 'point', // if you want to show dollar sign ($) then use 'Total Balance' => '$'
                'Cant. Tickets' => 'point'
            ])
            ->download('Reporte consolidado - ' . $event->name);
    }

    public function reportTicketsConsolidatedPDF($event_id)
    {
        $title = "Reporte consolidado";
        $event = $this->getMatchEvent($event_id);
        $event['event_start'] = Carbon::parse($event['event_start']);
        $event['printed_at'] = Carbon::now();
        $event['free_sales'] = $this->getFreeSales($event_id);
        $event['credit_sales'] = $this->getCreditSales($event_id);

        $pdf = App::make('snappy.pdf.wrapper');
        $htmlContent = view('reports_internal.consolidated_pdf.consolidated_pdf_body', compact('event'))->render();
        $htmlHeader = view('reports_internal.consolidated_pdf.consolidated_pdf_header', compact('event'))->render();
        $htmlFooter = view('reports_internal.consolidated_pdf.consolidated_pdf_footer', compact('event'))->render();

        $pdf->setOption('enable-local-file-access', true)
            ->setOption('header-html', $htmlHeader)
            ->setOption('footer-html', $htmlFooter)
            ->loadHTML($htmlContent);

        return $pdf->inline();
    }

    public function reportTicketsOverTheCounterAndCredit($event_id)
    {
        $title = "Reporte Venta Libre Y Crédito";
        $event = MatchEvent::find($event_id);

        $meta = [ // For displaying filters description on header
            'Evento' => $title . ' - ' . $event->name . '(' . $event->id . ')',
            'Fecha evento' => $event->event_start,
            'Código evento' => $event->code,
            'Fecha impresión reporte' => Carbon::now()->format('d/m/Y H:i:s'),
            'Tipo' => 'Tickets vendidos'
        ];

        $queryBuilder = DB::table('tickets')
            ->select(
                'match_events.name',
                'match_events.code',
                DB::raw("SUBSTRING_INDEX(z.name, '_', 1) as name_sub"),
                'zsub.name as zone',
                DB::raw('SUM(tickets.price) as total'),
                DB::raw("IF(ticket_mains.pin_tercero IS NOT NULL || ticket_mains.pin_internal IS NOT NULL, 'Efectivo', IF(tickets.ticket_type_id = 4, 'Crédito', 'Pasarela')) as f_pago"),
                DB::raw("IF(ticket_mains.payment_reference IS NOT NULL, 'Aplicación', 'Taquilla') as way_payment"),
                DB::raw('COUNT(tickets.id) as t_tickets')
            )
            ->join('ticket_mains', 'tickets.ticket_main_id', '=', 'ticket_mains.id')
            ->join('match_events', 'tickets.match_event_id', '=', 'match_events.id')
            ->join('seats as s', 'tickets.seat_id', '=', 's.id')
            ->join('zones as z', 's.zone_id', '=', 'z.id')
            ->join('zones as zsub', 'z.zone_id', '=', 'zsub.id')
            ->where('tickets.match_event_id', $event_id)        // Del evento
            ->whereIn('tickets.ticket_status_id', [TicketStatusEnum::PURCHASED, TicketStatusEnum::CREATED])
            ->whereIn('tickets.ticket_type_id', [TicketTypesEnum::FREE_SALE, TicketTypesEnum::CREDIT])
            ->groupBy('zsub.id', 'match_events.name', 'match_events.code', 'zsub.name', DB::raw("SUBSTRING_INDEX(z.name, '_', 1)"), 'f_pago', 'way_payment')
            ->orderBy('zsub.name', 'ASC')
            ->orderBy(DB::raw("SUBSTRING_INDEX(z.name, '_', 1)"), 'ASC');

        $columns = [
            'Nombre Evento' => 'name',
            'Codigo evento' => 'code',
            'Tribuna' => 'zone',
            'Sector' => 'name_sub',
            'Forma pago' => 'f_pago',
            'Medio de Compra' => 'way_payment',
            'Cant. Tickets' => 't_tickets',
            'Total' => 'total'
        ];

        return PdfReport::of($title, $meta, $queryBuilder, $columns)
            ->editColumns(['Total'], [ // Mass edit column
                'class' => 'right bold padding'
            ])
            ->editColumns(['Cant. Tickets'], [ // Mass edit column
                'class' => 'right bold'
            ])
            ->setCss([
                '.padding' => 'padding: 0px 0px 0px 85px;',
            ])
            ->showTotal([ // Used to sum all value on specified column on the last table (except using groupBy method). 'point' is a type for displaying total with a thousand separator
                'Total' => 'point', // if you want to show dollar sign ($) then use 'Total Balance' => '$'
                'Cant. Tickets' => 'point'
            ])
            ->download('Reporte Venta Libre Y Crédito - ' . $event->name);
    }

    public function reportTicketsSubscriber(array $params)
    {
        $event_id = $params['event_id'];
        $group_by_special_text = $params['group_by_special_text'] ?? false;

        $title = "Reporte Abonados" . ($group_by_special_text ? ' por promoción' : '');
        $event = MatchEvent::find($event_id);

        $meta = [ // For displaying filters description on header
            'Evento' => $title . ' - ' . $event->name . '(' . $event->id . ')',
            'Fecha evento' => $event->event_start,
            'Código evento' => $event->code,
            'Fecha impresión reporte' => Carbon::now()->format('d/m/Y H:i:s'),
            'Tipo' => 'Tickets vendidos'
        ];

        $queryBuilder = DB::table('tickets')
            ->select(
                'match_events.name',
                'match_events.code',
                DB::raw("SUBSTRING_INDEX(z.name, '_', 1) as name_sub"),
                'zsub.name as zone',
                DB::raw('SUM(tickets.price) as total'),
                DB::raw("IF(ticket_mains.pin_tercero IS NOT NULL || ticket_mains.pin_internal IS NOT NULL, 'Efectivo', IF(tickets.ticket_type_id = 4, 'Crédito', 'Pasarela')) as f_pago"),
                DB::raw("IF(ticket_mains.payment_reference IS NOT NULL, 'Aplicación', 'Taquilla') as way_payment"),
                DB::raw('COUNT(tickets.id) as t_tickets')
            )
            ->join('ticket_mains', 'tickets.ticket_main_id', '=', 'ticket_mains.id')
            ->join('match_events', 'tickets.match_event_id', '=', 'match_events.id')
            ->join('seats as s', 'tickets.seat_id', '=', 's.id')
            ->join('zones as z', 's.zone_id', '=', 'z.id')
            ->join('zones as zsub', 'z.zone_id', '=', 'zsub.id')
            ->where('tickets.match_event_id', $event_id)                // Del evento
            ->whereIn('tickets.ticket_status_id', [TicketStatusEnum::PURCHASED, TicketStatusEnum::CREATED])
            ->whereIn('tickets.ticket_type_id', [TicketTypesEnum::SUBSCRIBER])
            ->groupBy('zsub.id', 'match_events.name', 'match_events.code', 'zsub.name', DB::raw("SUBSTRING_INDEX(z.name, '_', 1)"), 'f_pago', 'way_payment')
            ->when($group_by_special_text, function ($query) {
                return $query->addSelect('tickets.special_text')->groupBy('tickets.special_text');
            })
            ->orderBy('zsub.name', 'ASC')
            ->orderBy(DB::raw("SUBSTRING_INDEX(z.name, '_', 1)"), 'ASC');

        $columns = [
            'Nombre Evento'     => 'name',
            'Codigo evento'     => 'code',
            'Tribuna'           => 'zone',
            'Sector'            => 'name_sub',
            'Forma pago'        => 'f_pago',
            'Medio de Compra'   => 'way_payment',
            'Cant. Tickets'     => 't_tickets',
            'Total'             => 'total'
        ];

        if ($group_by_special_text) {
            $keys = array_keys($columns);
            $pos = array_search('Sector', $keys);

            $before = array_slice($columns, 0, $pos + 1, true);
            $after  = array_slice($columns, $pos + 1, null, true);

            $columns = $before + ['Promoción' => 'special_text'] + $after;
        }

        return PdfReport::of($title, $meta, $queryBuilder, $columns)
            ->editColumns(['Total'], [ // Mass edit column
                'class' => 'right bold padding'
            ])
            ->editColumns(['Cant. Tickets'], [ // Mass edit column
                'class' => 'right bold'
            ])
            ->setCss([
                '.padding' => 'padding: 0px 0px 0px 85px;',
            ])
            ->showTotal([ // Used to sum all value on specified column on the last table (except using groupBy method). 'point' is a type for displaying total with a thousand separator
                'Total' => 'point', // if you want to show dollar sign ($) then use 'Total Balance' => '$'
                'Cant. Tickets' => 'point'
            ])
            ->download('Reporte Abonados ' . ($group_by_special_text ? 'por promoción' : '') . ' - ' . $event->name);
    }

    public function reportTicketsByGrandstand($event_id)
    {
        $title = "Reporte ingresos por tribuna";
        $event = MatchEvent::find($event_id);
        $date = Carbon::now()->format('Y-m-d H:i:s');

        $meta = [
            'Evento' => $event->name . '(' . $event->id . ')',
            'Fecha evento' => $event->event_start,
            'Código evento' => $event->code,
            'Fecha impresión reporte' => $date,
            'Descripcion' => 'Porcentaje ingresos por tribuna'
        ];

        $pathReportPercentage = '/public/tickets_tmp/' . time() . '-p.pdf';
        $queryBuilderPercentage = DB::table('tickets')
            ->select(DB::raw('CONCAT("Tribuna ",zsub.name) as grandstand'), DB::raw('ROUND((IFNULL(SUM(tv.validation_state), SUM(IF(tickets.confirm_stadium_ticket = "' . ConfirmStadiumEnum::CONFIRMED . '", 1, 0)))/(COUNT(*))*100), 2) AS percentage'))
            ->leftJoin('ticket_validation_histories AS tv', function ($join) {
                $join->on('tickets.id', '=', 'tv.ticket_id')->where('tv.validation_state', '=', 1);
            })
            ->join('ticket_types as tt', 'tickets.ticket_type_id', '=', 'tt.id')
            ->join('seats as s', 'tickets.seat_id', '=', 's.id')
            ->join('zones as z', 's.zone_id', '=', 'z.id')
            ->join('zones as zsub', 'z.zone_id', '=', 'zsub.id')
            ->where('tickets.match_event_id', $event_id)
            ->whereIn('tickets.ticket_status_id', [TicketStatusEnum::PURCHASED, TicketStatusEnum::CREATED])
            ->groupBy('zsub.name')
            ->orderBy('zsub.name', 'ASC');

        $columnsPercentage = [
            'Tribuna' => 'grandstand',
            'Porcentaje' => 'percentage'
        ];

        PdfReport::of($title, $meta, $queryBuilderPercentage, $columnsPercentage)
            ->showNumColumn(false)
            ->editColumn('Porcentaje', [
                'class' => 'right bold',
                'displayAs' => function ($result) {
                    return $result->percentage . ' %';
                }
            ])
            ->store($pathReportPercentage);

        $meta = [
            'Evento' => $event->name . '(' . $event->id . ')',
            'Fecha evento' => $event->event_start,
            'Código evento' => $event->code,
            'Fecha impresión reporte' => $date,
            'Descripcion' => 'Total ingresos por tribuna y tipo'
        ];

        $queryBuilder = DB::table('tickets')
            ->select(DB::raw('CONCAT("Tribuna ",zsub.name) as grandstand'), 'tt.name as type', DB::raw('COUNT(*) as sales'), DB::raw('IFNULL(SUM(tv.validation_state), SUM(IF(tickets.confirm_stadium_ticket = "' . ConfirmStadiumEnum::CONFIRMED . '", 1, 0))) AS receipts'))
            ->leftJoin('ticket_validation_histories AS tv', function ($join) {
                $join->on('tickets.id', '=', 'tv.ticket_id')->where('tv.validation_state', '=', 1);
            })
            ->join('ticket_types as tt', 'tickets.ticket_type_id', '=', 'tt.id')
            ->join('seats as s', 'tickets.seat_id', '=', 's.id')
            ->join('zones as z', 's.zone_id', '=', 'z.id')
            ->join('zones as zsub', 'z.zone_id', '=', 'zsub.id')
            ->where('tickets.match_event_id', $event_id)
            ->whereIn('tickets.ticket_status_id', [TicketStatusEnum::PURCHASED, TicketStatusEnum::CREATED])
            ->groupBy('zsub.name', 'tt.name')
            ->orderBy('zsub.name', 'ASC');

        $columns = [
            'Tribuna' => 'grandstand',
            'Tipo' => 'type',
            'Total Boletas' => 'sales',
            'Total Ingresos' => 'receipts'
        ];

        $pathReport = '/public/tickets_tmp/' . time() . '.pdf';
        PdfReport::of($title, $meta, $queryBuilder, $columns)
            ->showNumColumn(false)
            ->editColumn('Total Boletas', [
                'class' => 'right bold',
            ])
            ->editColumn('Total Ingresos', [
                'class' => 'right bold',
            ])
            ->showTotal([
                'Total Boletas' => 'point',
                'Total Ingresos' => 'point',
            ])
            ->store($pathReport);

        $pdf = PDFMerger::init();
        $pdf->addPDF(storage_path() . '/app' . $pathReport, 'all');
        $pdf->addPDF(storage_path() . '/app' . $pathReportPercentage, 'all');
        $fileName = $title . ' - ' . $event->name . '.pdf';
        $pdf->merge();
        $pdf->save(storage_path() . '/app/public/tickets_tmp/' . $fileName);
        return response()->download(storage_path() . '/app/public/tickets_tmp/' . $fileName);
    }

    public static function clean($string)
    {
        if (!$string) {
            return "";
        }
        $string = str_replace(' ', '-', $string); // Replaces all spaces with hyphens.
        return preg_replace('/[^A-Za-z0-9\-]/', '', $string); // Removes special chars.
    }

    private function getMatchEvent($event_id)
    {
        return MatchEvent::find($event_id);
    }

    private function getFreeSales($event_id)
    {
        return $this->getSalesByTicketType($event_id, [TicketTypesEnum::FREE_SALE, TicketTypesEnum::SUBSCRIBER]);
    }

    private function getCreditSales($event_id)
    {
        return $this->getSalesByTicketType($event_id, [TicketTypesEnum::CREDIT]);
    }

    private function getSalesByTicketType(int $event_id, array $ticketTypesIds)
    {
        $summary = DB::table('tickets')
            ->select([
                'sellerUser.id',
                'sellerUser.email',
                DB::raw('COUNT(tickets.id) as num_tickets'),
                DB::raw('SUM(tickets.price) as total'),

            ])
            ->join('ticket_mains', 'tickets.ticket_main_id', '=', 'ticket_mains.id')
            ->join('users as sellerUser', 'ticket_mains.seller_user_id', '=', 'sellerUser.id')
            ->where('tickets.match_event_id', $event_id)
            ->whereIn('tickets.ticket_status_id', [TicketStatusEnum::PURCHASED, TicketStatusEnum::CREATED])
            ->whereIn('tickets.ticket_type_id', $ticketTypesIds)
            ->groupBy(['sellerUser.id', 'sellerUser.email'])
            ->orderBy('sellerUser.email', 'ASC')
            ->get();

        $details = $this->getSalesDetailsByTicketType($event_id, $ticketTypesIds)->toArray();

        foreach ($summary->toArray() as &$s) {
            $s->details = array_filter($details, function ($d) use ($s) {
                return $d->id == $s->id;
            });
        }
        unset($s);

        return $summary;
    }

    private function getSalesDetailsByTicketType(int $event_id, array $ticketTypesIds)
    {
        return DB::table('tickets')
            ->select([
                'sellerUser.id',
                'zsub.name as tribune',
                DB::raw('COUNT(tickets.id) as num_tickets'),
                DB::raw('SUM(tickets.price) as total'),

            ])
            ->join('ticket_mains', 'tickets.ticket_main_id', '=', 'ticket_mains.id')
            ->join('users as sellerUser', 'ticket_mains.seller_user_id', '=', 'sellerUser.id')
            ->join('seats as s', 'tickets.seat_id', '=', 's.id')
            ->join('zones as z', 's.zone_id', '=', 'z.id')
            ->join('zones as zsub', 'z.zone_id', '=', 'zsub.id')
            ->where('tickets.match_event_id', $event_id)
            ->whereIn('tickets.ticket_status_id', [TicketStatusEnum::PURCHASED, TicketStatusEnum::CREATED])
            ->whereIn('tickets.ticket_type_id', $ticketTypesIds)
            ->groupBy(['sellerUser.id', 'tribune'])
            ->orderBy('sellerUser.email', 'ASC')
            ->get();
    }
}