155 lines
5.6 KiB
PHP
155 lines
5.6 KiB
PHP
<?php
|
|
|
|
namespace App\Domains\Purchase\Resources;
|
|
|
|
use App\Domains\Purchase\Models\Purchase;
|
|
use App\Domains\Purchase\Models\PurchaseItem;
|
|
use App\Domains\Purchase\Models\TelepagosPaymentCandidate;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Http\Resources\Json\JsonResource;
|
|
|
|
/**
|
|
* @mixin Purchase
|
|
*/
|
|
class PurchaseResource extends JsonResource
|
|
{
|
|
/**
|
|
* @return array<string, mixed>
|
|
*/
|
|
public function toArray(Request $request): array
|
|
{
|
|
$items = $this->resource->relationLoaded('items')
|
|
? $this->resource->getRelation('items')
|
|
: collect();
|
|
$ticketsCount = array_key_exists('tickets_count', $this->resource->getAttributes())
|
|
? (int) $this->resource->getAttribute('tickets_count')
|
|
: null;
|
|
$paymentVerification = $this->resolvePaymentVerification();
|
|
|
|
$subtotal = $items->isNotEmpty()
|
|
? $items->reduce(
|
|
fn (float $carry, PurchaseItem $item): float => $carry + $this->resolveItemSubtotal($item),
|
|
0.0,
|
|
)
|
|
: (float) ($this->total ?? 0);
|
|
|
|
$total = $this->status === Purchase::STATUS_PAID && $this->total !== null
|
|
? (float) $this->total
|
|
: ($items->isNotEmpty()
|
|
? $items->reduce(
|
|
fn (float $carry, PurchaseItem $item): float => $carry + $this->resolveItemTotal($item),
|
|
0.0,
|
|
)
|
|
: (float) ($this->total ?? 0));
|
|
|
|
return [
|
|
'id' => $this->id,
|
|
'cart_id' => $this->cart_id,
|
|
'tenant_codigo' => $this->tenant_codigo,
|
|
'user_id' => $this->user_id,
|
|
'created_at' => $this->created_at,
|
|
'status' => $this->status,
|
|
'payment_method' => $this->payment_method,
|
|
'expires_at' => $this->expires_at,
|
|
'dni' => $this->dni,
|
|
'transfer_payer_dni' => $this->transfer_payer_dni,
|
|
'telefono' => $this->telefono,
|
|
'nombre_apellido' => $this->nombre_apellido,
|
|
'email' => $this->email,
|
|
'items_source' => $items->isNotEmpty() ? 'purchase' : null,
|
|
'items' => PurchaseItemResource::collection($items),
|
|
'tickets_count' => $this->when($ticketsCount !== null, $ticketsCount),
|
|
'has_generated_tickets' => $this->when($ticketsCount !== null, $ticketsCount > 0),
|
|
'payment_verification' => $this->when($paymentVerification !== null, $paymentVerification),
|
|
'subtotal' => $this->formatMoney($subtotal),
|
|
'total' => $this->formatMoney($total),
|
|
];
|
|
}
|
|
|
|
protected function resolveItemSubtotal(PurchaseItem $item): float
|
|
{
|
|
return (float) $item->precio_unitario * $item->cantidad;
|
|
}
|
|
|
|
protected function resolveItemTotal(PurchaseItem $item): float
|
|
{
|
|
return (float) ($item->total ?? 0);
|
|
}
|
|
|
|
protected function formatMoney(float|int|string|null $amount): string
|
|
{
|
|
return number_format((float) ($amount ?? 0), 2, '.', '');
|
|
}
|
|
|
|
/** @return array<string, mixed>|null */
|
|
private function resolvePaymentVerification(): ?array
|
|
{
|
|
if (
|
|
$this->status !== Purchase::STATUS_IN_REVIEW
|
|
|| $this->payment_method !== 'transfer'
|
|
|| ! $this->resource->relationLoaded('telepagosPaymentCandidates')
|
|
) {
|
|
return null;
|
|
}
|
|
|
|
$candidates = $this->resource
|
|
->getRelation('telepagosPaymentCandidates')
|
|
->sort(fn (TelepagosPaymentCandidate $left, TelepagosPaymentCandidate $right): int => $this->comparePaymentCandidates($left, $right))
|
|
->values();
|
|
/** @var TelepagosPaymentCandidate|null $primary */
|
|
$primary = $candidates->first();
|
|
|
|
return [
|
|
'status' => $primary === null ? 'pending' : 'candidate',
|
|
'candidate_count' => $candidates->count(),
|
|
'primary' => $primary === null ? null : [
|
|
'reason' => $primary->match_reason,
|
|
'dni_distance' => $primary->dni_distance,
|
|
'payment_amount' => $this->formatMoney($primary->payment_amount),
|
|
'purchase_amount' => $this->formatMoney($primary->purchase_amount),
|
|
'amount_difference' => $this->formatMoney($primary->amount_difference),
|
|
'confidence' => $primary->confidence,
|
|
'detected_at' => $primary->payment?->created_at?->toIso8601String(),
|
|
],
|
|
'reasons' => $candidates
|
|
->pluck('match_reason')
|
|
->unique()
|
|
->values()
|
|
->all(),
|
|
];
|
|
}
|
|
|
|
private function comparePaymentCandidates(
|
|
TelepagosPaymentCandidate $left,
|
|
TelepagosPaymentCandidate $right,
|
|
): int {
|
|
$reasonComparison = $this->paymentCandidateRank($left->match_reason)
|
|
<=> $this->paymentCandidateRank($right->match_reason);
|
|
|
|
if ($reasonComparison !== 0) {
|
|
return $reasonComparison;
|
|
}
|
|
|
|
$differenceComparison = (float) $left->amount_difference <=> (float) $right->amount_difference;
|
|
|
|
if ($differenceComparison !== 0) {
|
|
return $differenceComparison;
|
|
}
|
|
|
|
$leftTimestamp = $left->payment?->created_at?->getTimestamp() ?? 0;
|
|
$rightTimestamp = $right->payment?->created_at?->getTimestamp() ?? 0;
|
|
|
|
return ($rightTimestamp <=> $leftTimestamp) ?: ($right->id <=> $left->id);
|
|
}
|
|
|
|
private function paymentCandidateRank(string $reason): int
|
|
{
|
|
return match ($reason) {
|
|
'ambiguous_exact_match' => 0,
|
|
'exact_dni_near_amount' => 1,
|
|
'exact_amount_near_dni' => 2,
|
|
default => 3,
|
|
};
|
|
}
|
|
}
|