shopit-back/app/Domains/Ticket/Services/AdminAppTicketRowService.php

218 lines
8.4 KiB
PHP

<?php
namespace App\Domains\Ticket\Services;
use App\Domains\Catalog\Models\ItemAttribute;
use App\Domains\Purchase\Models\PurchaseItem;
use App\Domains\Ticket\Models\Ticket;
use Carbon\Carbon;
use Carbon\CarbonInterface;
use Illuminate\Support\Collection;
class AdminAppTicketRowService
{
private const FIESTA_FUTBOL_INFANTIL = 'fiesta_futbol_infantil';
private const CATEGORY_PRESENTATIONS = [
'alojamientos' => ['category' => 'Camping', 'product' => 'tipo_alojamiento', 'type' => null],
'camping' => ['category' => null, 'product' => 'tipo_alojamiento', 'type' => null],
'entradas' => ['category' => null, 'product' => 'product', 'type' => null],
'comidas' => ['category' => 'Comida', 'product' => 'event_date', 'type' => 'horario'],
'comida' => ['category' => null, 'product' => 'event_date', 'type' => 'horario'],
'merchandising' => ['category' => null, 'product' => 'product', 'type' => 'color'],
];
/** @return array<string, mixed> */
public function details(Ticket $ticket): array
{
$purchaseItem = $this->sourcePurchaseItem($ticket);
return [
'source_purchase_id' => $ticket->source_purchase_id,
'order_number' => $ticket->source_purchase_id,
'product' => $purchaseItem?->item_nombre
?? $ticket->sourceCatalogItem?->nombre
?? $ticket->name,
'amount' => $purchaseItem?->precio_unitario,
'client' => $ticket->sourcePurchase?->nombre_apellido ?? $ticket->user?->nombre_apellido,
'date' => $ticket->sourcePurchase?->created_at,
'status' => $ticket->status,
'scanned_by' => $ticket->scannerUser?->nombre_apellido,
'variant_properties' => $this->variantProperties($ticket),
];
}
/** @param array<string, mixed>|null $details */
public function values(Ticket $ticket, ?array $details = null): array
{
$details ??= $this->details($ticket);
$presentation = $this->presentation($ticket, $details);
return [
'order_number' => $details['order_number'],
'category' => $presentation['category'],
'product' => $presentation['product'],
'type' => $presentation['type'],
'amount' => $details['amount'] === null ? null : (float) $details['amount'],
'client' => $details['client'] ?? 'Sin nombre',
'ticket' => $ticket->ticket,
'date' => $details['date'],
'status' => $details['status'],
'scanned_by' => $details['scanned_by'] ?? '-',
];
}
/**
* @param Collection<int, Ticket> $tickets
* @return Collection<int, array<string, mixed>>
*/
public function rows(Collection $tickets): Collection
{
return $tickets->values()->map(fn (Ticket $ticket): array => $this->values($ticket));
}
/**
* @param Collection<int, array<string, mixed>> $rows
* @param list<array<string, mixed>> $columns
* @return Collection<int, array<string, string>>
*/
public function displayRows(Collection $rows, array $columns, string $timeZone): Collection
{
return $rows->map(fn (array $row): array => collect($columns)
->mapWithKeys(fn (array $column): array => [
$column['key'] => $this->displayValue(
$row[$column['key']] ?? null,
$column['type'],
$timeZone,
),
])
->all());
}
public function displayValue(mixed $value, string $type, string $timeZone): string
{
if ($value === null || $value === '') {
return '-';
}
return match ($type) {
'order_number' => '#'.$value,
'currency' => '$'.number_format((float) $value, 2, ',', '.'),
'date' => ($value instanceof CarbonInterface ? $value : Carbon::parse((string) $value))
->copy()->timezone($timeZone)->format('d/m/Y H:i'),
'status' => match ((string) $value) {
Ticket::STATUS_USED => 'Usado',
Ticket::STATUS_EXPIRED => 'Vencido',
default => 'Activo',
},
default => (string) $value,
};
}
/** @param array<string, mixed> $details */
private function presentation(Ticket $ticket, array $details): array
{
$sourceCategory = trim((string) ($ticket->sourceCatalogItem?->category?->nombre ?? '')) ?: '-';
if ($ticket->tenant_code !== self::FIESTA_FUTBOL_INFANTIL) {
return [
'category' => $sourceCategory,
'product' => (string) ($details['product'] ?: $ticket->name ?: '-'),
'type' => $this->allPropertyLabels($details) ?: '-',
];
}
$configuration = self::CATEGORY_PRESENTATIONS[mb_strtolower($sourceCategory)] ?? null;
if ($configuration === null) {
return [
'category' => $sourceCategory,
'product' => (string) ($details['product'] ?: $ticket->name ?: '-'),
'type' => $this->allPropertyLabels($details) ?: '-',
];
}
return [
'category' => $configuration['category'] ?? $sourceCategory,
'product' => $configuration['product'] === 'product'
? (string) ($details['product'] ?: $ticket->name ?: '-')
: ($this->propertyLabels($details, $configuration['product']) ?: '-'),
'type' => $configuration['type'] === null
? '-'
: ($this->propertyLabels($details, $configuration['type']) ?: '-'),
];
}
/** @param array<string, mixed> $details */
private function propertyLabels(array $details, string $code): string
{
$property = collect($details['variant_properties'] ?? [])->firstWhere('code', $code);
$labels = collect($property['values'] ?? [])->pluck('label')->filter();
if ($code === 'event_date') {
$labels = $labels->map(function (string $label): string {
[$day, $month] = array_pad(explode('/', $label), 2, null);
return $day !== null && $month !== null ? "{$day}/{$month}" : $label;
});
}
return $labels->implode(', ');
}
/** @param array<string, mixed> $details */
private function allPropertyLabels(array $details): string
{
return collect($details['variant_properties'] ?? [])
->flatMap(fn (array $property): array => $property['values'] ?? [])
->pluck('label')
->filter()
->implode(', ');
}
private function sourcePurchaseItem(Ticket $ticket): ?PurchaseItem
{
return $ticket->sourcePurchase?->items->first(function (PurchaseItem $item) use ($ticket): bool {
if ($ticket->source_variant_id !== null) {
return $item->source_variant_id === $ticket->source_variant_id;
}
return $item->source_catalog_item_id === $ticket->source_catalog_item_id
&& $item->source_variant_id === null;
});
}
/** @return list<array{code: string, label: string, values: list<array{value: string, label: string}>}> */
private function variantProperties(Ticket $ticket): array
{
$variant = $ticket->sourceVariant;
if ($variant === null) {
return [];
}
$itemAttributes = $variant->definitions
->map(fn ($definition) => $definition->itemAttribute)
->filter()
->merge($variant->catalogItem?->itemAttributes ?? collect())
->unique('id')
->values();
return $variant->selectionOptions($itemAttributes)
->map(function (array $selection, string $attributeCode) use ($itemAttributes): array {
$itemAttribute = $itemAttributes->first(
fn (ItemAttribute $itemAttribute): bool => $itemAttribute->attribute?->codigo
=== $attributeCode,
);
$values = array_is_list($selection) ? $selection : [$selection];
return [
'code' => $attributeCode,
'label' => $itemAttribute?->attribute?->nombre
?? ($attributeCode === 'event_date' ? 'Fecha' : $attributeCode),
'values' => array_values($values),
];
})
->values()
->all();
}
}