Compare commits
8 Commits
feature/ev
...
dev
| Author | SHA1 | Date |
|---|---|---|
|
|
d3182fbd13 | |
|
|
b8eb71896c | |
|
|
cb0fb2899c | |
|
|
615eacea91 | |
|
|
84c9fa4c9d | |
|
|
b3d06431e5 | |
|
|
379c2bdbeb | |
|
|
ca7a2ae55c |
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Domains\Bundle\Models;
|
||||
|
||||
use App\Domains\Catalog\Models\GroupItem;
|
||||
use App\Domains\Shared\Contracts\Buyable;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
|
|
@ -10,6 +11,7 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
||||
|
||||
#[Fillable([
|
||||
'tenant_codigo',
|
||||
|
|
@ -48,6 +50,14 @@ class Bundle extends Model implements Buyable
|
|||
return $this->hasMany(BundleItem::class, 'bundle_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return MorphMany<GroupItem, $this>
|
||||
*/
|
||||
public function groupItems(): MorphMany
|
||||
{
|
||||
return $this->morphMany(GroupItem::class, 'groupable');
|
||||
}
|
||||
|
||||
public function getPrice(): float
|
||||
{
|
||||
return (float) $this->precio;
|
||||
|
|
|
|||
|
|
@ -2,8 +2,11 @@
|
|||
|
||||
namespace App\Domains\Catalog\Controllers;
|
||||
|
||||
use App\Domains\Catalog\Resources\CatalogFeaturedGroupResource;
|
||||
use App\Domains\Bundle\Models\Bundle;
|
||||
use App\Domains\Catalog\Models\FeaturedGroup;
|
||||
use App\Domains\Catalog\Models\ProductVariant;
|
||||
use App\Domains\Catalog\Resources\CatalogFeaturedGroupResource;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Routing\Controller;
|
||||
|
||||
|
|
@ -12,9 +15,15 @@ class CatalogController extends Controller
|
|||
public function index(string $tenant): JsonResponse
|
||||
{
|
||||
$featuredGroups = FeaturedGroup::where('tenant_codigo', $tenant)
|
||||
->with(['featuredVariants' => function ($query) {
|
||||
$query->orderBy('order');
|
||||
}, 'featuredVariants.variant.product', 'featuredVariants.variant.attachments', 'featuredVariants.variant.product.attachments'])
|
||||
->with([
|
||||
'groupItems' => fn ($query) => $query->orderBy('order'),
|
||||
'groupItems.groupable' => function (MorphTo $morphTo): void {
|
||||
$morphTo->morphWith([
|
||||
ProductVariant::class => ['product', 'attachments', 'product.attachments'],
|
||||
Bundle::class => ['items.variant.product', 'items.variant.attachments', 'items.variant.product.attachments'],
|
||||
]);
|
||||
},
|
||||
])
|
||||
->orderBy('group_order')
|
||||
->get();
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ class FeaturedGroupController extends Controller
|
|||
public function show(string $tenant, FeaturedGroup $featuredGroup): FeaturedGroupResource
|
||||
{
|
||||
abort_if($featuredGroup->tenant_codigo !== $tenant, 404);
|
||||
return new FeaturedGroupResource($featuredGroup->load('featuredVariants'));
|
||||
return new FeaturedGroupResource($featuredGroup->load('groupItems'));
|
||||
}
|
||||
|
||||
public function update(UpdateFeaturedGroupRequest $request, string $tenant, FeaturedGroup $featuredGroup): FeaturedGroupResource
|
||||
|
|
|
|||
|
|
@ -1,42 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Domains\Catalog\Controllers;
|
||||
|
||||
use App\Domains\Catalog\Models\FeaturedGroup;
|
||||
use App\Domains\Catalog\Models\FeaturedVariant;
|
||||
use App\Domains\Catalog\Requests\StoreFeaturedVariantRequest;
|
||||
use App\Domains\Catalog\Requests\UpdateFeaturedVariantRequest;
|
||||
use App\Domains\Catalog\Resources\FeaturedVariantResource;
|
||||
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
|
||||
use Illuminate\Routing\Controller;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
class FeaturedVariantController extends Controller
|
||||
{
|
||||
public function index(string $tenant, FeaturedGroup $featuredGroup): AnonymousResourceCollection
|
||||
{
|
||||
abort_if($featuredGroup->tenant_codigo !== $tenant, 404);
|
||||
return FeaturedVariantResource::collection($featuredGroup->featuredVariants);
|
||||
}
|
||||
|
||||
public function store(StoreFeaturedVariantRequest $request, string $tenant, FeaturedGroup $featuredGroup): FeaturedVariantResource
|
||||
{
|
||||
abort_if($featuredGroup->tenant_codigo !== $tenant, 404);
|
||||
$variant = $featuredGroup->featuredVariants()->create($request->validated());
|
||||
return new FeaturedVariantResource($variant);
|
||||
}
|
||||
|
||||
public function update(UpdateFeaturedVariantRequest $request, string $tenant, FeaturedGroup $featuredGroup, FeaturedVariant $featuredVariant): FeaturedVariantResource
|
||||
{
|
||||
abort_if($featuredGroup->tenant_codigo !== $tenant || $featuredVariant->featured_group_id !== $featuredGroup->id, 404);
|
||||
$featuredVariant->update($request->validated());
|
||||
return new FeaturedVariantResource($featuredVariant);
|
||||
}
|
||||
|
||||
public function destroy(string $tenant, FeaturedGroup $featuredGroup, FeaturedVariant $featuredVariant): JsonResponse
|
||||
{
|
||||
abort_if($featuredGroup->tenant_codigo !== $tenant || $featuredVariant->featured_group_id !== $featuredGroup->id, 404);
|
||||
$featuredVariant->delete();
|
||||
return response()->json(null, 204);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
namespace App\Domains\Catalog\Controllers;
|
||||
|
||||
use App\Domains\Bundle\Models\Bundle;
|
||||
use App\Domains\Catalog\Models\FeaturedGroup;
|
||||
use App\Domains\Catalog\Models\GroupItem;
|
||||
use App\Domains\Catalog\Models\ProductVariant;
|
||||
use App\Domains\Catalog\Requests\StoreGroupItemRequest;
|
||||
use App\Domains\Catalog\Requests\UpdateGroupItemRequest;
|
||||
use App\Domains\Catalog\Resources\GroupItemResource;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
|
||||
use Illuminate\Routing\Controller;
|
||||
|
||||
class GroupItemController extends Controller
|
||||
{
|
||||
public function index(string $tenant, FeaturedGroup $featuredGroup): AnonymousResourceCollection
|
||||
{
|
||||
abort_if($featuredGroup->tenant_codigo !== $tenant, 404);
|
||||
|
||||
return GroupItemResource::collection($featuredGroup->groupItems);
|
||||
}
|
||||
|
||||
public function store(StoreGroupItemRequest $request, string $tenant, FeaturedGroup $featuredGroup): GroupItemResource
|
||||
{
|
||||
abort_if($featuredGroup->tenant_codigo !== $tenant, 404);
|
||||
|
||||
$groupableType = $request->mappedGroupableType();
|
||||
$groupable = $groupableType::query()->findOrFail($request->integer('groupable_id'));
|
||||
|
||||
abort_if(
|
||||
($groupable instanceof ProductVariant && $groupable->product()->where('tenant_codigo', $tenant)->doesntExist())
|
||||
|| ($groupable instanceof Bundle && $groupable->tenant_codigo !== $tenant),
|
||||
404
|
||||
);
|
||||
|
||||
$groupItem = $featuredGroup->groupItems()->create([
|
||||
'groupable_type' => $groupableType,
|
||||
'groupable_id' => $groupable->getKey(),
|
||||
'order' => $request->validated('order'),
|
||||
]);
|
||||
|
||||
return new GroupItemResource($groupItem);
|
||||
}
|
||||
|
||||
public function update(UpdateGroupItemRequest $request, string $tenant, FeaturedGroup $featuredGroup, GroupItem $groupItem): GroupItemResource
|
||||
{
|
||||
abort_if($featuredGroup->tenant_codigo !== $tenant || $groupItem->featured_group_id !== $featuredGroup->id, 404);
|
||||
$groupItem->update($request->validated());
|
||||
|
||||
return new GroupItemResource($groupItem);
|
||||
}
|
||||
|
||||
public function destroy(string $tenant, FeaturedGroup $featuredGroup, GroupItem $groupItem): JsonResponse
|
||||
{
|
||||
abort_if($featuredGroup->tenant_codigo !== $tenant || $groupItem->featured_group_id !== $featuredGroup->id, 404);
|
||||
$groupItem->delete();
|
||||
|
||||
return response()->json(null, 204);
|
||||
}
|
||||
}
|
||||
|
|
@ -14,8 +14,8 @@ class FeaturedGroup extends Model
|
|||
'group_order',
|
||||
];
|
||||
|
||||
public function featuredVariants(): HasMany
|
||||
public function groupItems(): HasMany
|
||||
{
|
||||
return $this->hasMany(FeaturedVariant::class);
|
||||
return $this->hasMany(GroupItem::class);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,17 +2,18 @@
|
|||
|
||||
namespace App\Domains\Catalog\Models;
|
||||
|
||||
use App\Domains\Catalog\Models\ProductVariant;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||
|
||||
class FeaturedVariant extends Model
|
||||
class GroupItem extends Model
|
||||
{
|
||||
protected $table = 'featured_variants';
|
||||
protected $table = 'group_items';
|
||||
|
||||
protected $fillable = [
|
||||
'featured_group_id',
|
||||
'product_variant_id',
|
||||
'groupable_type',
|
||||
'groupable_id',
|
||||
'order',
|
||||
];
|
||||
|
||||
|
|
@ -21,8 +22,8 @@ class FeaturedVariant extends Model
|
|||
return $this->belongsTo(FeaturedGroup::class);
|
||||
}
|
||||
|
||||
public function variant(): BelongsTo
|
||||
public function groupable(): MorphTo
|
||||
{
|
||||
return $this->belongsTo(ProductVariant::class, 'product_variant_id');
|
||||
return $this->morphTo();
|
||||
}
|
||||
}
|
||||
|
|
@ -12,6 +12,7 @@ use Illuminate\Database\Eloquent\Model;
|
|||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
||||
|
||||
#[Fillable([
|
||||
'producto_id',
|
||||
|
|
@ -217,4 +218,12 @@ class ProductVariant extends Model implements Buyable
|
|||
'attachment_id'
|
||||
)->withTimestamps();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return MorphMany<GroupItem, $this>
|
||||
*/
|
||||
public function groupItems(): MorphMany
|
||||
{
|
||||
return $this->morphMany(GroupItem::class, 'groupable');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Domains\Catalog\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class StoreFeaturedVariantRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'product_variant_id' => ['required', 'integer'],
|
||||
'order' => ['nullable', 'integer'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
namespace App\Domains\Catalog\Requests;
|
||||
|
||||
use App\Domains\Bundle\Models\Bundle;
|
||||
use App\Domains\Catalog\Models\ProductVariant;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class StoreGroupItemRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'groupable_type' => ['required', 'string', Rule::in(['variant', 'bundle'])],
|
||||
'groupable_id' => [
|
||||
'required',
|
||||
'integer',
|
||||
Rule::exists(match ($this->input('groupable_type')) {
|
||||
'bundle' => 'bundles',
|
||||
default => 'productos_variantes',
|
||||
}, 'id'),
|
||||
],
|
||||
'order' => ['nullable', 'integer'],
|
||||
];
|
||||
}
|
||||
|
||||
public function mappedGroupableType(): string
|
||||
{
|
||||
return match ($this->input('groupable_type')) {
|
||||
'variant' => ProductVariant::class,
|
||||
'bundle' => Bundle::class,
|
||||
default => throw new \InvalidArgumentException('Invalid groupable type'),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ namespace App\Domains\Catalog\Requests;
|
|||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class UpdateFeaturedVariantRequest extends FormRequest
|
||||
class UpdateGroupItemRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
|
|
@ -2,9 +2,11 @@
|
|||
|
||||
namespace App\Domains\Catalog\Resources;
|
||||
|
||||
use App\Domains\Bundle\Models\Bundle;
|
||||
use App\Domains\Catalog\Models\GroupItem;
|
||||
use App\Domains\Catalog\Models\ProductVariant;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
use App\Domains\Catalog\Models\FeaturedVariant;
|
||||
|
||||
class CatalogFeaturedGroupResource extends JsonResource
|
||||
{
|
||||
|
|
@ -15,27 +17,51 @@ class CatalogFeaturedGroupResource extends JsonResource
|
|||
'title' => $this->group_name,
|
||||
'layout' => $this->product_layout,
|
||||
'group_order' => $this->group_order,
|
||||
'items' => $this->whenLoaded('featuredVariants', function () use ($request) {
|
||||
return $this->featuredVariants->map(function (FeaturedVariant $featuredVariant) use ($request) {
|
||||
$variant = $featuredVariant->variant;
|
||||
$variantResource = (new ProductVariantResource($variant))->toArray($request);
|
||||
|
||||
'items' => $this->whenLoaded('groupItems', function () use ($request) {
|
||||
return $this->groupItems->map(function (GroupItem $groupItem) use ($request) {
|
||||
$groupable = $groupItem->groupable;
|
||||
|
||||
if ($groupable instanceof Bundle) {
|
||||
return [
|
||||
'id' => $groupable->id,
|
||||
'groupable_type' => 'bundle',
|
||||
'groupable_id' => $groupable->id,
|
||||
'nombre' => $groupable->nombre,
|
||||
'descripcion' => $groupable->descripcion,
|
||||
'precio' => $groupable->precio,
|
||||
'cantidad_maxima' => $groupable->stock_tecnico,
|
||||
'items' => $groupable->items->map(fn ($item) => [
|
||||
'cantidad' => $item->cantidad,
|
||||
'variant' => (new ProductVariantResource($item->variant))->toArray($request),
|
||||
])->values(),
|
||||
];
|
||||
}
|
||||
|
||||
if (! $groupable instanceof ProductVariant) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$variantResource = (new ProductVariantResource($groupable))->toArray($request);
|
||||
$variantResource['groupable_type'] = 'variant';
|
||||
$variantResource['groupable_id'] = $groupable->id;
|
||||
|
||||
if ($this->product_layout === 'row' || $this->product_layout === 'column_with_cart' || $this->product_layout === 'vertical_with_cart') {
|
||||
// Devuelve el Product Variant Resource completo como pidio el usuario
|
||||
// "Que todo devuelva el product variant resource"
|
||||
return $variantResource;
|
||||
}
|
||||
|
||||
|
||||
// Para column_with_image o vertical_with_image
|
||||
if ($this->product_layout === 'column_with_image' || $this->product_layout === 'vertical_with_image') {
|
||||
if (isset($variantResource['images']) && count($variantResource['images']) > 0) {
|
||||
$variantResource['images'] = [$variantResource['images'][0]];
|
||||
}
|
||||
|
||||
return $variantResource;
|
||||
}
|
||||
|
||||
|
||||
return $variantResource;
|
||||
});
|
||||
})->filter()->values();
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ class FeaturedGroupResource extends JsonResource
|
|||
'group_name' => $this->group_name,
|
||||
'product_layout' => $this->product_layout,
|
||||
'group_order' => $this->group_order,
|
||||
'featured_variants' => FeaturedVariantResource::collection($this->whenLoaded('featuredVariants')),
|
||||
'group_items' => GroupItemResource::collection($this->whenLoaded('groupItems')),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Domains\Catalog\Resources;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class FeaturedVariantResource extends JsonResource
|
||||
{
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'featured_group_id' => $this->featured_group_id,
|
||||
'product_variant_id' => $this->product_variant_id,
|
||||
'order' => $this->order,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace App\Domains\Catalog\Resources;
|
||||
|
||||
use App\Domains\Bundle\Models\Bundle;
|
||||
use App\Domains\Catalog\Models\ProductVariant;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class GroupItemResource extends JsonResource
|
||||
{
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'featured_group_id' => $this->featured_group_id,
|
||||
'groupable_type' => match ($this->groupable_type) {
|
||||
ProductVariant::class => 'variant',
|
||||
Bundle::class => 'bundle',
|
||||
default => 'unknown',
|
||||
},
|
||||
'groupable_id' => $this->groupable_id,
|
||||
'order' => $this->order,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
<?php
|
||||
|
||||
use App\Domains\Catalog\Controllers\AttributeController;
|
||||
use App\Domains\Catalog\Controllers\BrandController;
|
||||
use App\Domains\Catalog\Controllers\CatalogController;
|
||||
use App\Domains\Catalog\Controllers\CategoryController;
|
||||
use App\Domains\Catalog\Controllers\ProductController;
|
||||
use App\Domains\Catalog\Controllers\AttributeController;
|
||||
use App\Domains\Catalog\Controllers\ProductVariantController;
|
||||
use App\Domains\Catalog\Controllers\FeaturedGroupController;
|
||||
use App\Domains\Catalog\Controllers\FeaturedVariantController;
|
||||
use App\Domains\Catalog\Controllers\GroupItemController;
|
||||
use App\Domains\Catalog\Controllers\ProductController;
|
||||
use App\Domains\Catalog\Controllers\ProductVariantController;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
Route::prefix('tenants/{tenant:codigo}')->group(function (): void {
|
||||
|
|
@ -22,10 +22,10 @@ Route::prefix('tenants/{tenant:codigo}')->group(function (): void {
|
|||
'variants' => 'productVariant',
|
||||
]);
|
||||
Route::apiResource('featured-groups', FeaturedGroupController::class);
|
||||
Route::apiResource('featured-groups.variants', FeaturedVariantController::class)
|
||||
Route::apiResource('featured-groups.items', GroupItemController::class)
|
||||
->only(['index', 'store', 'update', 'destroy'])
|
||||
->parameters([
|
||||
'featured-groups' => 'featuredGroup',
|
||||
'variants' => 'featuredVariant',
|
||||
'items' => 'groupItem',
|
||||
]);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ class TelepagosWebhookService
|
|||
$dni = substr($cuit, 2, -1);
|
||||
|
||||
$compra = Purchase::where('tenant_codigo', $tenantCodigo)
|
||||
->whereRaw("REPLACE(dni, '.', '') = ?", [$dni])
|
||||
->where('transfer_payer_dni', $dni)
|
||||
->whereIn('status', [Purchase::STATUS_CREATED, Purchase::STATUS_PENDING_PAYMENT])
|
||||
->where('payment_method', 'transfer')
|
||||
->where('total', $amount)
|
||||
|
|
|
|||
|
|
@ -68,10 +68,17 @@ class PurchaseController extends Controller
|
|||
$method = $request->validated('method');
|
||||
$totalAmount = $compra->calculateCurrentTotalAmount();
|
||||
|
||||
$compra->update([
|
||||
$purchaseUpdate = [
|
||||
'payment_method' => $method,
|
||||
'status' => Purchase::STATUS_PENDING_PAYMENT,
|
||||
'total' => $totalAmount,
|
||||
]);
|
||||
];
|
||||
|
||||
if ($method === 'transfer') {
|
||||
$purchaseUpdate['transfer_payer_dni'] = preg_replace('/\D+/', '', (string) $request->validated('transfer_payer_dni'));
|
||||
}
|
||||
|
||||
$compra->update($purchaseUpdate);
|
||||
|
||||
if ($method === 'transfer') {
|
||||
$telepagosService = new TelepagosIntegrationService;
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ use Illuminate\Database\Eloquent\Relations\HasOne;
|
|||
'payment_method',
|
||||
'total',
|
||||
'dni',
|
||||
'transfer_payer_dni',
|
||||
'telefono',
|
||||
'nombre_apellido',
|
||||
'email',
|
||||
|
|
|
|||
|
|
@ -19,6 +19,11 @@ class PaymentIntentRequest extends FormRequest
|
|||
{
|
||||
return [
|
||||
'method' => ['required', 'string', Rule::in(['qr', 'transfer'])],
|
||||
'transfer_payer_dni' => [
|
||||
Rule::requiredIf(fn (): bool => $this->input('method') === 'transfer'),
|
||||
'string',
|
||||
'regex:/^\d{7,8}$/',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ class PurchaseResource extends JsonResource
|
|||
'status' => $this->status,
|
||||
'payment_method' => $this->payment_method,
|
||||
'dni' => $this->dni,
|
||||
'transfer_payer_dni' => $this->transfer_payer_dni,
|
||||
'telefono' => $this->telefono,
|
||||
'nombre_apellido' => $this->nombre_apellido,
|
||||
'email' => $this->email,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
<?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('compras', function (Blueprint $table) {
|
||||
$table->string('transfer_payer_dni', 8)->nullable()->after('dni');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('compras', function (Blueprint $table) {
|
||||
$table->dropColumn('transfer_payer_dni');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::rename('featured_variants', 'group_items');
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::rename('group_items', 'featured_variants');
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
use App\Domains\Catalog\Models\ProductVariant;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('group_items', function (Blueprint $table) {
|
||||
$table->nullableMorphs('groupable');
|
||||
});
|
||||
|
||||
DB::table('group_items')->update([
|
||||
'groupable_type' => ProductVariant::class,
|
||||
'groupable_id' => DB::raw('product_variant_id'),
|
||||
]);
|
||||
|
||||
$productVariantForeignKey = collect(Schema::getForeignKeys('group_items'))
|
||||
->first(fn (array $foreignKey): bool => $foreignKey['columns'] === ['product_variant_id']);
|
||||
|
||||
Schema::table('group_items', function (Blueprint $table) use ($productVariantForeignKey) {
|
||||
if ($productVariantForeignKey !== null) {
|
||||
$table->dropForeign($productVariantForeignKey['name']);
|
||||
}
|
||||
|
||||
$table->dropColumn('product_variant_id');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
if (DB::table('group_items')->where('groupable_type', '!=', ProductVariant::class)->exists()) {
|
||||
throw new RuntimeException('Cannot roll back groupable group items while bundle items exist.');
|
||||
}
|
||||
|
||||
Schema::table('group_items', function (Blueprint $table) {
|
||||
$table->unsignedBigInteger('product_variant_id')->nullable();
|
||||
});
|
||||
|
||||
DB::table('group_items')->update([
|
||||
'product_variant_id' => DB::raw('groupable_id'),
|
||||
]);
|
||||
|
||||
Schema::table('group_items', function (Blueprint $table) {
|
||||
$table->dropMorphs('groupable');
|
||||
$table->foreign('product_variant_id')
|
||||
->references('id')
|
||||
->on('productos_variantes')
|
||||
->cascadeOnDelete();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
<?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('bundles', function (Blueprint $table) {
|
||||
$table->dropForeign(['tenant_codigo']);
|
||||
});
|
||||
|
||||
Schema::table('bundles', function (Blueprint $table) {
|
||||
$table->string('tenant_codigo')->change();
|
||||
$table->foreign('tenant_codigo')->references('codigo')->on('tenants')->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('bundles', function (Blueprint $table) {
|
||||
$table->dropForeign(['tenant_codigo']);
|
||||
});
|
||||
|
||||
Schema::table('bundles', function (Blueprint $table) {
|
||||
$table->string('tenant_codigo', 10)->change();
|
||||
$table->foreign('tenant_codigo')->references('codigo')->on('tenants')->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -18,7 +18,7 @@ class AttributeSeeder extends Seeder
|
|||
$tenants = Tenant::all();
|
||||
|
||||
foreach ($tenants as $tenant) {
|
||||
// Seed Color attribute
|
||||
// Fiesta Futbol Infantil only uses the Fecha attribute.
|
||||
$existingColor = Attribute::query()
|
||||
->where('tenant_codigo', $tenant->codigo)
|
||||
->where('codigo', 'color')
|
||||
|
|
@ -28,41 +28,43 @@ class AttributeSeeder extends Seeder
|
|||
Product::deleteAttribute($existingColor);
|
||||
}
|
||||
|
||||
Product::createAttribute($tenant, [
|
||||
'codigo' => 'color',
|
||||
'nombre' => 'Color',
|
||||
'type' => FieldType::Select->value,
|
||||
'is_required' => true,
|
||||
'metadata_schema' => [
|
||||
'hex' => ['type' => 'string'],
|
||||
],
|
||||
'options' => [
|
||||
[
|
||||
'value' => 'Negro',
|
||||
'label' => 'Negro',
|
||||
'sort_order' => 1,
|
||||
'metadata' => ['hex' => '#000000'],
|
||||
if ($tenant->codigo !== 'fiesta_futbol_infantil') {
|
||||
Product::createAttribute($tenant, [
|
||||
'codigo' => 'color',
|
||||
'nombre' => 'Color',
|
||||
'type' => FieldType::Select->value,
|
||||
'is_required' => true,
|
||||
'metadata_schema' => [
|
||||
'hex' => ['type' => 'string'],
|
||||
],
|
||||
[
|
||||
'value' => 'Gris',
|
||||
'label' => 'Gris',
|
||||
'sort_order' => 2,
|
||||
'metadata' => ['hex' => '#808080'],
|
||||
'options' => [
|
||||
[
|
||||
'value' => 'Negro',
|
||||
'label' => 'Negro',
|
||||
'sort_order' => 1,
|
||||
'metadata' => ['hex' => '#000000'],
|
||||
],
|
||||
[
|
||||
'value' => 'Gris',
|
||||
'label' => 'Gris',
|
||||
'sort_order' => 2,
|
||||
'metadata' => ['hex' => '#808080'],
|
||||
],
|
||||
[
|
||||
'value' => 'Blanco',
|
||||
'label' => 'Blanco',
|
||||
'sort_order' => 3,
|
||||
'metadata' => ['hex' => '#FFFFFF'],
|
||||
],
|
||||
[
|
||||
'value' => 'Azul',
|
||||
'label' => 'Azul',
|
||||
'sort_order' => 4,
|
||||
'metadata' => ['hex' => '#0000FF'],
|
||||
],
|
||||
],
|
||||
[
|
||||
'value' => 'Blanco',
|
||||
'label' => 'Blanco',
|
||||
'sort_order' => 3,
|
||||
'metadata' => ['hex' => '#FFFFFF'],
|
||||
],
|
||||
[
|
||||
'value' => 'Azul',
|
||||
'label' => 'Azul',
|
||||
'sort_order' => 4,
|
||||
'metadata' => ['hex' => '#0000FF'],
|
||||
],
|
||||
],
|
||||
]);
|
||||
]);
|
||||
}
|
||||
|
||||
// Seed Talle (Size - Text options) attribute
|
||||
$existingTalle = Attribute::query()
|
||||
|
|
@ -74,18 +76,20 @@ class AttributeSeeder extends Seeder
|
|||
Product::deleteAttribute($existingTalle);
|
||||
}
|
||||
|
||||
Product::createAttribute($tenant, [
|
||||
'codigo' => 'talle',
|
||||
'nombre' => 'Talle',
|
||||
'type' => FieldType::Select->value,
|
||||
'is_required' => true,
|
||||
'options' => [
|
||||
['value' => 'S', 'label' => 'S', 'sort_order' => 1],
|
||||
['value' => 'M', 'label' => 'M', 'sort_order' => 2],
|
||||
['value' => 'L', 'label' => 'L', 'sort_order' => 3],
|
||||
['value' => 'XL', 'label' => 'XL', 'sort_order' => 4],
|
||||
],
|
||||
]);
|
||||
if ($tenant->codigo !== 'fiesta_futbol_infantil') {
|
||||
Product::createAttribute($tenant, [
|
||||
'codigo' => 'talle',
|
||||
'nombre' => 'Talle',
|
||||
'type' => FieldType::Select->value,
|
||||
'is_required' => true,
|
||||
'options' => [
|
||||
['value' => 'S', 'label' => 'S', 'sort_order' => 1],
|
||||
['value' => 'M', 'label' => 'M', 'sort_order' => 2],
|
||||
['value' => 'L', 'label' => 'L', 'sort_order' => 3],
|
||||
['value' => 'XL', 'label' => 'XL', 'sort_order' => 4],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
// Seed Talle Numérico (Numeric Size options) attribute
|
||||
$existingTalleNumerico = Attribute::query()
|
||||
|
|
@ -97,18 +101,20 @@ class AttributeSeeder extends Seeder
|
|||
Product::deleteAttribute($existingTalleNumerico);
|
||||
}
|
||||
|
||||
Product::createAttribute($tenant, [
|
||||
'codigo' => 'talle_numerico',
|
||||
'nombre' => 'Talle Numérico',
|
||||
'type' => FieldType::Select->value,
|
||||
'is_required' => true,
|
||||
'options' => [
|
||||
['value' => '38', 'label' => '38', 'sort_order' => 1],
|
||||
['value' => '40', 'label' => '40', 'sort_order' => 2],
|
||||
['value' => '42', 'label' => '42', 'sort_order' => 3],
|
||||
['value' => '44', 'label' => '44', 'sort_order' => 4],
|
||||
],
|
||||
]);
|
||||
if ($tenant->codigo !== 'fiesta_futbol_infantil') {
|
||||
Product::createAttribute($tenant, [
|
||||
'codigo' => 'talle_numerico',
|
||||
'nombre' => 'Talle Numérico',
|
||||
'type' => FieldType::Select->value,
|
||||
'is_required' => true,
|
||||
'options' => [
|
||||
['value' => '38', 'label' => '38', 'sort_order' => 1],
|
||||
['value' => '40', 'label' => '40', 'sort_order' => 2],
|
||||
['value' => '42', 'label' => '42', 'sort_order' => 3],
|
||||
['value' => '44', 'label' => '44', 'sort_order' => 4],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
// Seed Fecha attribute
|
||||
$existingFecha = Attribute::query()
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Domains\Bundle\Models\Bundle;
|
||||
use App\Domains\Catalog\Enums\InventoryPolicy;
|
||||
use App\Domains\Catalog\Models\Attribute;
|
||||
use App\Domains\Catalog\Models\Category;
|
||||
|
|
@ -24,6 +25,9 @@ class FiestaFutbolInfantilProductSeeder extends Seeder
|
|||
throw new RuntimeException("Tenant 'fiesta_futbol_infantil' no encontrado.");
|
||||
}
|
||||
|
||||
// Bundles reference product variants, so remove them before reseeding products.
|
||||
Bundle::query()->where('tenant_codigo', $tenant->codigo)->delete();
|
||||
|
||||
// Delete existing products for this tenant
|
||||
$existingProducts = Product::query()
|
||||
->where('tenant_codigo', $tenant->codigo)
|
||||
|
|
@ -37,6 +41,7 @@ class FiestaFutbolInfantilProductSeeder extends Seeder
|
|||
$category = Category::firstOrCreate(['nombre' => 'Entradas', 'tenant_code' => null]);
|
||||
|
||||
$dates = ['2026-10-09', '2026-10-10', '2026-10-11', '2026-10-12'];
|
||||
$entradaVariants = [];
|
||||
|
||||
// 1. Entrada General
|
||||
$entrada = $this->productService->create($tenant, [
|
||||
|
|
@ -51,7 +56,7 @@ class FiestaFutbolInfantilProductSeeder extends Seeder
|
|||
]);
|
||||
|
||||
foreach ($dates as $date) {
|
||||
$this->productService->createVariant($entrada, [
|
||||
$entradaVariants[] = $this->productService->createVariant($entrada, [
|
||||
'stock' => 0,
|
||||
'inventory_policy' => InventoryPolicy::Unlimited->value,
|
||||
'has_tickets' => true,
|
||||
|
|
@ -80,8 +85,10 @@ class FiestaFutbolInfantilProductSeeder extends Seeder
|
|||
['slug' => 'estacionamiento-moto', 'nombre' => 'Estacionamiento Moto', 'precio' => 2000, 'cat' => $category->id],
|
||||
];
|
||||
|
||||
$createdProducts = [];
|
||||
|
||||
foreach ($simpleProducts as $p) {
|
||||
$this->productService->create($tenant, [
|
||||
$createdProducts[$p['slug']] = $this->productService->create($tenant, [
|
||||
'categoria_id' => $p['cat'],
|
||||
'slug' => $p['slug'],
|
||||
'nombre' => $p['nombre'],
|
||||
|
|
@ -91,5 +98,37 @@ class FiestaFutbolInfantilProductSeeder extends Seeder
|
|||
'inventory_policy' => InventoryPolicy::Unlimited->value,
|
||||
]);
|
||||
}
|
||||
|
||||
$allDaysBundle = Bundle::query()->create([
|
||||
'tenant_codigo' => $tenant->codigo,
|
||||
'nombre' => 'Entrada General - Todos los días',
|
||||
'descripcion' => 'Incluye una entrada para cada día de la Fiesta Nacional del Fútbol Infantil.',
|
||||
'precio' => 40000,
|
||||
]);
|
||||
|
||||
foreach ($entradaVariants as $variant) {
|
||||
$allDaysBundle->items()->create([
|
||||
'producto_variante_id' => $variant->id,
|
||||
'cantidad' => 1,
|
||||
]);
|
||||
}
|
||||
|
||||
$foodBundle = Bundle::query()->create([
|
||||
'tenant_codigo' => $tenant->codigo,
|
||||
'nombre' => 'Combo 2 Panchos + 2 Hamburguesas',
|
||||
'descripcion' => 'Incluye 2 panchos y 2 hamburguesas con papa frita.',
|
||||
'precio' => 24000,
|
||||
]);
|
||||
|
||||
$foodBundle->items()->createMany([
|
||||
[
|
||||
'producto_variante_id' => $createdProducts['pancho']->variants()->sole()->id,
|
||||
'cantidad' => 2,
|
||||
],
|
||||
[
|
||||
'producto_variante_id' => $createdProducts['hamburguesa-papa-frita']->variants()->sole()->id,
|
||||
'cantidad' => 2,
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -162,7 +162,7 @@ class TenantSeeder extends Seeder
|
|||
'secondary_color' => '#A0A0A0',
|
||||
'danger_color' => '#FF8888',
|
||||
'success_color' => '#198754',
|
||||
'header_bg_color' => '#015327',
|
||||
'header_bg_color' => '#ffffff',
|
||||
'footer_bg_color' => '#015327',
|
||||
'header_logo' => $fiestaHeaderLogo,
|
||||
'footer_logo' => $fiestaFooterLogo,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,114 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature\Catalog;
|
||||
|
||||
use App\Domains\Attachable\Enums\AttachmentType;
|
||||
use App\Domains\Attachable\Models\Attachment;
|
||||
use App\Domains\Bundle\Models\Bundle;
|
||||
use App\Domains\Catalog\Models\Category;
|
||||
use App\Domains\Catalog\Models\FeaturedGroup;
|
||||
use App\Domains\Catalog\Models\Product;
|
||||
use App\Domains\Catalog\Models\ProductVariant;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Str;
|
||||
use Tests\TestCase;
|
||||
|
||||
class GroupItemTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
private FeaturedGroup $featuredGroup;
|
||||
|
||||
private ProductVariant $variant;
|
||||
|
||||
private Bundle $bundle;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$headerAttachment = $this->createAttachment('header.png');
|
||||
$footerAttachment = $this->createAttachment('footer.png');
|
||||
|
||||
$tenant = Tenant::query()->create([
|
||||
'codigo' => 'group-test',
|
||||
'nombre' => 'Group Test',
|
||||
'dominio' => 'group.test',
|
||||
'primary_color' => '#111111',
|
||||
'secondary_color' => '#222222',
|
||||
'danger_color' => '#333333',
|
||||
'success_color' => '#28a745',
|
||||
'header_bg_color' => '#444444',
|
||||
'footer_bg_color' => '#555555',
|
||||
'header_logo_id' => $headerAttachment->id,
|
||||
'footer_logo_id' => $footerAttachment->id,
|
||||
]);
|
||||
|
||||
$category = Category::query()->create([
|
||||
'tenant_code' => $tenant->codigo,
|
||||
'nombre' => 'Group items',
|
||||
]);
|
||||
|
||||
$product = Product::query()->create([
|
||||
'tenant_codigo' => $tenant->codigo,
|
||||
'categoria_id' => $category->id,
|
||||
'slug' => 'group-item-product',
|
||||
'nombre' => 'Group Item Product',
|
||||
'precio' => 100,
|
||||
]);
|
||||
|
||||
$this->variant = ProductVariant::query()->create([
|
||||
'producto_id' => $product->id,
|
||||
'stock' => 10,
|
||||
]);
|
||||
|
||||
$this->bundle = Bundle::query()->create([
|
||||
'tenant_codigo' => $tenant->codigo,
|
||||
'nombre' => 'Group Item Bundle',
|
||||
'precio' => 150,
|
||||
]);
|
||||
|
||||
$this->featuredGroup = FeaturedGroup::query()->create([
|
||||
'tenant_codigo' => $tenant->codigo,
|
||||
'group_name' => 'Featured',
|
||||
'product_layout' => 'row',
|
||||
'group_order' => 0,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_a_group_item_can_reference_a_product_variant(): void
|
||||
{
|
||||
$groupItem = $this->variant->groupItems()->create([
|
||||
'featured_group_id' => $this->featuredGroup->id,
|
||||
'order' => 1,
|
||||
]);
|
||||
|
||||
$this->assertInstanceOf(ProductVariant::class, $groupItem->groupable);
|
||||
$this->assertTrue($groupItem->groupable->is($this->variant));
|
||||
$this->assertTrue($this->variant->groupItems->first()->is($groupItem));
|
||||
}
|
||||
|
||||
public function test_a_group_item_can_reference_a_bundle(): void
|
||||
{
|
||||
$groupItem = $this->bundle->groupItems()->create([
|
||||
'featured_group_id' => $this->featuredGroup->id,
|
||||
'order' => 2,
|
||||
]);
|
||||
|
||||
$this->assertInstanceOf(Bundle::class, $groupItem->groupable);
|
||||
$this->assertTrue($groupItem->groupable->is($this->bundle));
|
||||
$this->assertTrue($this->bundle->groupItems->first()->is($groupItem));
|
||||
}
|
||||
|
||||
private function createAttachment(string $filename): Attachment
|
||||
{
|
||||
return Attachment::query()->create([
|
||||
'key' => (string) Str::uuid(),
|
||||
'path' => 'tests/'.$filename,
|
||||
'filename' => $filename,
|
||||
'type' => AttachmentType::Image,
|
||||
'mime_type' => 'image/png',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -33,6 +33,70 @@ class TelepagosWebhookTest extends TestCase
|
|||
Cache::flush();
|
||||
}
|
||||
|
||||
public function test_transfer_payment_intent_requires_a_valid_transfer_payer_dni(): void
|
||||
{
|
||||
$tenant = $this->createTenant('sonder', 'Sonder', 'sonder.com.ar');
|
||||
$user = User::factory()->create();
|
||||
$variant = $this->createVariantForTenant('sonder', 10, '50.00');
|
||||
$purchase = $this->createPendingTransferPurchase($tenant, $user->id, $variant->id, 1, '12345678');
|
||||
|
||||
$this->actingAs($user, 'sanctum')
|
||||
->postJson("/api/tenants/sonder/compras/{$purchase->id}/payment-intent", [
|
||||
'method' => 'transfer',
|
||||
])
|
||||
->assertUnprocessable()
|
||||
->assertJsonValidationErrors(['transfer_payer_dni']);
|
||||
|
||||
$this->actingAs($user, 'sanctum')
|
||||
->postJson("/api/tenants/sonder/compras/{$purchase->id}/payment-intent", [
|
||||
'method' => 'transfer',
|
||||
'transfer_payer_dni' => '12.345.678',
|
||||
])
|
||||
->assertUnprocessable()
|
||||
->assertJsonValidationErrors(['transfer_payer_dni']);
|
||||
}
|
||||
|
||||
public function test_transfer_payment_intent_persists_transfer_payer_dni_without_replacing_customer_dni(): void
|
||||
{
|
||||
$tenant = $this->createTenant('sonder', 'Sonder', 'sonder.com.ar');
|
||||
$this->configureTelepagosIntegration($tenant);
|
||||
$user = User::factory()->create();
|
||||
$variant = $this->createVariantForTenant('sonder', 10, '50.00');
|
||||
$purchase = $this->createPendingTransferPurchase($tenant, $user->id, $variant->id, 1, '12345678');
|
||||
$purchase->update(['status' => Purchase::STATUS_CREATED]);
|
||||
|
||||
Http::fake([
|
||||
'https://api.telepagos.com.ar/v2/auth/token' => Http::response([
|
||||
'status' => 'ok',
|
||||
'token' => 'test-token',
|
||||
'expires_at' => now()->addHour()->toIso8601String(),
|
||||
]),
|
||||
'https://api.telepagos.com.ar/v2/account/info' => Http::response([
|
||||
'status' => 'ok',
|
||||
'holder' => 'Telepagos Test',
|
||||
'cvu' => '0000003100000000000001',
|
||||
'alias' => 'telepagos.test',
|
||||
'entity' => 'Telepagos S.A.',
|
||||
]),
|
||||
]);
|
||||
|
||||
$this->actingAs($user, 'sanctum')
|
||||
->postJson("/api/tenants/sonder/compras/{$purchase->id}/payment-intent", [
|
||||
'method' => 'transfer',
|
||||
'transfer_payer_dni' => '23456789',
|
||||
])
|
||||
->assertOk()
|
||||
->assertJsonPath('transfer_data.alias', 'telepagos.test');
|
||||
|
||||
$this->assertDatabaseHas('compras', [
|
||||
'id' => $purchase->id,
|
||||
'dni' => '87654321',
|
||||
'transfer_payer_dni' => '23456789',
|
||||
'payment_method' => 'transfer',
|
||||
'status' => Purchase::STATUS_PENDING_PAYMENT,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_transfer_webhook_matches_pending_purchase_by_dni_and_total_amount(): void
|
||||
{
|
||||
$tenant = $this->createTenant('sonder', 'Sonder', 'sonder.com.ar');
|
||||
|
|
@ -209,7 +273,7 @@ class TelepagosWebhookTest extends TestCase
|
|||
|
||||
$purchase = $checkoutService->startCheckout($tenant, $userId, [
|
||||
'cart_id' => $cart->id,
|
||||
'dni' => $dni,
|
||||
'dni' => '87654321',
|
||||
'telefono' => '+54 9 341 555-4321',
|
||||
'nombre_apellido' => 'Juan Perez',
|
||||
'email' => 'juan.perez@example.com',
|
||||
|
|
@ -217,6 +281,7 @@ class TelepagosWebhookTest extends TestCase
|
|||
|
||||
$purchase->update([
|
||||
'payment_method' => 'transfer',
|
||||
'transfer_payer_dni' => $dni,
|
||||
]);
|
||||
|
||||
$checkoutService->completePurchase($purchase);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,95 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature\Seeders;
|
||||
|
||||
use App\Domains\Attachable\Enums\AttachmentType;
|
||||
use App\Domains\Attachable\Models\Attachment;
|
||||
use App\Domains\Bundle\Models\Bundle;
|
||||
use App\Domains\Catalog\Models\Attribute;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use Database\Seeders\AttributeSeeder;
|
||||
use Database\Seeders\FiestaFutbolInfantilProductSeeder;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class FiestaFutbolInfantilProductSeederTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_it_seeds_event_attributes_and_bundles(): void
|
||||
{
|
||||
$headerLogo = Attachment::query()->create([
|
||||
'path' => 'tests/header.png',
|
||||
'filename' => 'header.png',
|
||||
'type' => AttachmentType::Image,
|
||||
'mime_type' => 'image/png',
|
||||
]);
|
||||
$footerLogo = Attachment::query()->create([
|
||||
'path' => 'tests/footer.png',
|
||||
'filename' => 'footer.png',
|
||||
'type' => AttachmentType::Image,
|
||||
'mime_type' => 'image/png',
|
||||
]);
|
||||
|
||||
$tenant = Tenant::query()->create([
|
||||
'codigo' => 'fiesta_futbol_infantil',
|
||||
'nombre' => 'Fiesta Fútbol Infantil',
|
||||
'dominio' => 'fiesta-futbol-infantil.localhost',
|
||||
'primary_color' => '#00973F',
|
||||
'secondary_color' => '#A0A0A0',
|
||||
'danger_color' => '#FF8888',
|
||||
'success_color' => '#198754',
|
||||
'header_bg_color' => '#ffffff',
|
||||
'footer_bg_color' => '#015327',
|
||||
'header_logo_id' => $headerLogo->id,
|
||||
'footer_logo_id' => $footerLogo->id,
|
||||
]);
|
||||
|
||||
$this->seed([
|
||||
AttributeSeeder::class,
|
||||
FiestaFutbolInfantilProductSeeder::class,
|
||||
]);
|
||||
|
||||
$this->assertFalse(Attribute::query()
|
||||
->where('tenant_codigo', $tenant->codigo)
|
||||
->whereIn('codigo', ['color', 'talle', 'talle_numerico'])
|
||||
->exists());
|
||||
$this->assertSame(
|
||||
['fecha'],
|
||||
Attribute::query()->where('tenant_codigo', $tenant->codigo)->pluck('codigo')->all()
|
||||
);
|
||||
|
||||
$allDaysBundle = Bundle::query()
|
||||
->where('tenant_codigo', $tenant->codigo)
|
||||
->where('nombre', 'Entrada General - Todos los días')
|
||||
->with('items.variant.product', 'items.variant.definitions')
|
||||
->sole();
|
||||
|
||||
$this->assertSame('40000.00', $allDaysBundle->precio);
|
||||
$this->assertCount(4, $allDaysBundle->items);
|
||||
$this->assertEqualsCanonicalizing(
|
||||
['2026-10-09', '2026-10-10', '2026-10-11', '2026-10-12'],
|
||||
$allDaysBundle->items->map(fn ($item) => $item->variant->definitions->sole()->value)->all()
|
||||
);
|
||||
$this->assertTrue($allDaysBundle->items->every(
|
||||
fn ($item) => $item->cantidad === 1 && $item->variant->product->slug === 'entrada-general'
|
||||
));
|
||||
|
||||
$foodBundle = Bundle::query()
|
||||
->where('tenant_codigo', $tenant->codigo)
|
||||
->where('nombre', 'Combo 2 Panchos + 2 Hamburguesas')
|
||||
->with('items.variant.product')
|
||||
->sole();
|
||||
|
||||
$this->assertSame('24000.00', $foodBundle->precio);
|
||||
$this->assertEqualsCanonicalizing(
|
||||
[
|
||||
'pancho' => 2,
|
||||
'hamburguesa-papa-frita' => 2,
|
||||
],
|
||||
$foodBundle->items->mapWithKeys(
|
||||
fn ($item) => [$item->variant->product->slug => $item->cantidad]
|
||||
)->all()
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue