refactor(backend): reorganize domains into Core, Commerce, Ticketing and Shared
This commit is contained in:
160
app/Domains/Commerce/Purchase/Resources/PurchaseResource.php
Normal file
160
app/Domains/Commerce/Purchase/Resources/PurchaseResource.php
Normal file
@@ -0,0 +1,160 @@
|
||||
<?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
|
||||
{
|
||||
$serverTime = now();
|
||||
$expiresAt = $this->stockReservation?->expires_at;
|
||||
$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' => $expiresAt,
|
||||
'expires_in_seconds' => $expiresAt === null
|
||||
? null
|
||||
: max(0, $expiresAt->getTimestamp() - $serverTime->getTimestamp()),
|
||||
'server_time' => $serverTime,
|
||||
'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,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user