feat(tickets): add source_purchase_id to tickets and update related logic
This commit is contained in:
@@ -55,7 +55,7 @@ class PurchaseController extends Controller
|
|||||||
{
|
{
|
||||||
$compra = $this->resolveScopedPurchase($tenant, $request->user()->id, $compra);
|
$compra = $this->resolveScopedPurchase($tenant, $request->user()->id, $compra);
|
||||||
|
|
||||||
$compra->loadMissing('items');
|
$compra->loadMissing('items')->loadCount('tickets');
|
||||||
$compra->items->load('imageAttachment');
|
$compra->items->load('imageAttachment');
|
||||||
|
|
||||||
return PurchaseResource::make($compra);
|
return PurchaseResource::make($compra);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ use App\Domains\Auth\Models\User;
|
|||||||
use App\Domains\Cart\Models\Cart;
|
use App\Domains\Cart\Models\Cart;
|
||||||
use App\Domains\Purchase\Events\PurchasePaid;
|
use App\Domains\Purchase\Events\PurchasePaid;
|
||||||
use App\Domains\Tenant\Models\Tenant;
|
use App\Domains\Tenant\Models\Tenant;
|
||||||
|
use App\Domains\Ticket\Models\Ticket;
|
||||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
@@ -88,6 +89,14 @@ class Purchase extends Model
|
|||||||
return $this->hasMany(PurchaseItem::class, 'compra_id');
|
return $this->hasMany(PurchaseItem::class, 'compra_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return HasMany<Ticket, $this>
|
||||||
|
*/
|
||||||
|
public function tickets(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(Ticket::class, 'source_purchase_id');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return HasOne<TelepagosQr, $this>
|
* @return HasOne<TelepagosQr, $this>
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -20,6 +20,9 @@ class PurchaseResource extends JsonResource
|
|||||||
$items = $this->resource->relationLoaded('items')
|
$items = $this->resource->relationLoaded('items')
|
||||||
? $this->resource->getRelation('items')
|
? $this->resource->getRelation('items')
|
||||||
: collect();
|
: collect();
|
||||||
|
$ticketsCount = array_key_exists('tickets_count', $this->resource->getAttributes())
|
||||||
|
? (int) $this->resource->getAttribute('tickets_count')
|
||||||
|
: null;
|
||||||
|
|
||||||
$subtotal = $items->isNotEmpty()
|
$subtotal = $items->isNotEmpty()
|
||||||
? $items->reduce(
|
? $items->reduce(
|
||||||
@@ -51,6 +54,8 @@ class PurchaseResource extends JsonResource
|
|||||||
'email' => $this->email,
|
'email' => $this->email,
|
||||||
'items_source' => $items->isNotEmpty() ? 'purchase' : null,
|
'items_source' => $items->isNotEmpty() ? 'purchase' : null,
|
||||||
'items' => PurchaseItemResource::collection($items),
|
'items' => PurchaseItemResource::collection($items),
|
||||||
|
'tickets_count' => $this->when($ticketsCount !== null, $ticketsCount),
|
||||||
|
'has_generated_tickets' => $this->when($ticketsCount !== null, $ticketsCount > 0),
|
||||||
'subtotal' => $this->formatMoney($subtotal),
|
'subtotal' => $this->formatMoney($subtotal),
|
||||||
'total' => $this->formatMoney($total),
|
'total' => $this->formatMoney($total),
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ class GenerateTicketsForPaidPurchase
|
|||||||
$user,
|
$user,
|
||||||
$purchaseItem->cantidad,
|
$purchaseItem->cantidad,
|
||||||
$purchaseItem->source_variant_id,
|
$purchaseItem->source_variant_id,
|
||||||
|
$purchase->getKey(),
|
||||||
);
|
);
|
||||||
|
|
||||||
array_push($ticketIds, ...$generatedTickets->pluck('id')->all());
|
array_push($ticketIds, ...$generatedTickets->pluck('id')->all());
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Domains\Ticket\Models;
|
namespace App\Domains\Ticket\Models;
|
||||||
|
|
||||||
use App\Domains\Auth\Models\User;
|
use App\Domains\Auth\Models\User;
|
||||||
|
use App\Domains\Purchase\Models\Purchase;
|
||||||
use App\Domains\Tenant\Models\Tenant;
|
use App\Domains\Tenant\Models\Tenant;
|
||||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
@@ -14,6 +15,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|||||||
'ticket',
|
'ticket',
|
||||||
'name',
|
'name',
|
||||||
'description',
|
'description',
|
||||||
|
'source_purchase_id',
|
||||||
'source_catalog_item_id',
|
'source_catalog_item_id',
|
||||||
'source_variant_id',
|
'source_variant_id',
|
||||||
'starts_at',
|
'starts_at',
|
||||||
@@ -38,6 +40,7 @@ class Ticket extends Model
|
|||||||
return [
|
return [
|
||||||
'source_catalog_item_id' => 'integer',
|
'source_catalog_item_id' => 'integer',
|
||||||
'source_variant_id' => 'integer',
|
'source_variant_id' => 'integer',
|
||||||
|
'source_purchase_id' => 'integer',
|
||||||
'starts_at' => 'datetime',
|
'starts_at' => 'datetime',
|
||||||
'expires_at' => 'datetime',
|
'expires_at' => 'datetime',
|
||||||
'used_at' => 'datetime',
|
'used_at' => 'datetime',
|
||||||
@@ -57,6 +60,12 @@ class Ticket extends Model
|
|||||||
return $this->belongsTo(User::class);
|
return $this->belongsTo(User::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @return BelongsTo<Purchase, $this> */
|
||||||
|
public function sourcePurchase(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Purchase::class, 'source_purchase_id');
|
||||||
|
}
|
||||||
|
|
||||||
public function isValid(): bool
|
public function isValid(): bool
|
||||||
{
|
{
|
||||||
$now = now();
|
$now = now();
|
||||||
|
|||||||
@@ -21,12 +21,13 @@ class TicketGeneratorService
|
|||||||
User $user,
|
User $user,
|
||||||
int $quantity = 1,
|
int $quantity = 1,
|
||||||
?int $sourceVariantId = null,
|
?int $sourceVariantId = null,
|
||||||
|
?int $sourcePurchaseId = null,
|
||||||
): Collection {
|
): Collection {
|
||||||
if ($quantity < 1) {
|
if ($quantity < 1) {
|
||||||
throw TicketGenerationException::invalidQuantity();
|
throw TicketGenerationException::invalidQuantity();
|
||||||
}
|
}
|
||||||
|
|
||||||
return DB::transaction(function () use ($catalogItem, $user, $quantity, $sourceVariantId): Collection {
|
return DB::transaction(function () use ($catalogItem, $user, $quantity, $sourceVariantId, $sourcePurchaseId): Collection {
|
||||||
$targets = $this->resolveTargets(
|
$targets = $this->resolveTargets(
|
||||||
$catalogItem,
|
$catalogItem,
|
||||||
$quantity,
|
$quantity,
|
||||||
@@ -36,6 +37,7 @@ class TicketGeneratorService
|
|||||||
return $targets->map(function (array $target) use (
|
return $targets->map(function (array $target) use (
|
||||||
$catalogItem,
|
$catalogItem,
|
||||||
$sourceVariantId,
|
$sourceVariantId,
|
||||||
|
$sourcePurchaseId,
|
||||||
$user,
|
$user,
|
||||||
): Ticket {
|
): Ticket {
|
||||||
$item = $target['catalog_item'];
|
$item = $target['catalog_item'];
|
||||||
@@ -46,6 +48,7 @@ class TicketGeneratorService
|
|||||||
'ticket' => (string) Str::uuid(),
|
'ticket' => (string) Str::uuid(),
|
||||||
'name' => $item->nombre,
|
'name' => $item->nombre,
|
||||||
'description' => (string) ($item->descripcion ?? ''),
|
'description' => (string) ($item->descripcion ?? ''),
|
||||||
|
'source_purchase_id' => $sourcePurchaseId,
|
||||||
'source_catalog_item_id' => $catalogItem->getKey(),
|
'source_catalog_item_id' => $catalogItem->getKey(),
|
||||||
'source_variant_id' => $sourceVariantId,
|
'source_variant_id' => $sourceVariantId,
|
||||||
'starts_at' => $selectedItem->getMinimumUseDate(),
|
'starts_at' => $selectedItem->getMinimumUseDate(),
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('tickets', function (Blueprint $table): void {
|
||||||
|
$table->foreignId('source_purchase_id')
|
||||||
|
->nullable()
|
||||||
|
->after('description')
|
||||||
|
->constrained('compras')
|
||||||
|
->cascadeOnUpdate()
|
||||||
|
->nullOnDelete();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('tickets', function (Blueprint $table): void {
|
||||||
|
$table->dropConstrainedForeignId('source_purchase_id');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -193,6 +193,12 @@ class TicketGeneratorServiceTest extends TestCase
|
|||||||
$this->assertDatabaseCount('tickets', 2);
|
$this->assertDatabaseCount('tickets', 2);
|
||||||
Event::assertDispatchedTimes(TicketsAvailable::class, 1);
|
Event::assertDispatchedTimes(TicketsAvailable::class, 1);
|
||||||
|
|
||||||
|
$this->actingAs($this->user, 'sanctum')
|
||||||
|
->getJson("/api/tenants/{$this->tenant->codigo}/compras/{$purchase->id}")
|
||||||
|
->assertOk()
|
||||||
|
->assertJsonPath('data.tickets_count', 2)
|
||||||
|
->assertJsonPath('data.has_generated_tickets', true);
|
||||||
|
|
||||||
$purchase->markAsPaid();
|
$purchase->markAsPaid();
|
||||||
|
|
||||||
$this->assertDatabaseCount('tickets', 2);
|
$this->assertDatabaseCount('tickets', 2);
|
||||||
@@ -209,6 +215,7 @@ class TicketGeneratorServiceTest extends TestCase
|
|||||||
$purchase->markAsPaid();
|
$purchase->markAsPaid();
|
||||||
|
|
||||||
$this->assertDatabaseHas('tickets', [
|
$this->assertDatabaseHas('tickets', [
|
||||||
|
'source_purchase_id' => $purchase->id,
|
||||||
'source_catalog_item_id' => $item->id,
|
'source_catalog_item_id' => $item->id,
|
||||||
'source_variant_id' => $variant->id,
|
'source_variant_id' => $variant->id,
|
||||||
]);
|
]);
|
||||||
@@ -226,6 +233,12 @@ class TicketGeneratorServiceTest extends TestCase
|
|||||||
$this->assertSame(Purchase::STATUS_PAID, $purchase->status);
|
$this->assertSame(Purchase::STATUS_PAID, $purchase->status);
|
||||||
$this->assertDatabaseCount('tickets', 0);
|
$this->assertDatabaseCount('tickets', 0);
|
||||||
Event::assertNotDispatched(TicketsAvailable::class);
|
Event::assertNotDispatched(TicketsAvailable::class);
|
||||||
|
|
||||||
|
$this->actingAs($this->user, 'sanctum')
|
||||||
|
->getJson("/api/tenants/{$this->tenant->codigo}/compras/{$purchase->id}")
|
||||||
|
->assertOk()
|
||||||
|
->assertJsonPath('data.tickets_count', 0)
|
||||||
|
->assertJsonPath('data.has_generated_tickets', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_paid_status_is_confirmed_when_ticket_maximum_use_date_was_reached(): void
|
public function test_paid_status_is_confirmed_when_ticket_maximum_use_date_was_reached(): void
|
||||||
|
|||||||
Reference in New Issue
Block a user