feat: add display_cart_item_images feature to tenants and update related resources

This commit is contained in:
2026-08-18 16:46:45 -03:00
parent 817d0de6d2
commit 8e26611097
13 changed files with 136 additions and 5 deletions

View File

@@ -4,6 +4,7 @@ namespace App\Domains\Cart\Resources;
use App\Domains\Cart\Models\CartItem; use App\Domains\Cart\Models\CartItem;
use App\Domains\Catalog\Enums\InventoryPolicy; use App\Domains\Catalog\Enums\InventoryPolicy;
use App\Domains\Tenant\Models\Tenant;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource; use Illuminate\Http\Resources\Json\JsonResource;
@@ -19,12 +20,14 @@ class CartItemResource extends JsonResource
{ {
$selectedItem = $this->selectedItem(); $selectedItem = $this->selectedItem();
$imageUrl = null; $imageUrl = null;
$tenant = $request->route('tenant');
$displayImage = ! $tenant instanceof Tenant || $tenant->display_cart_item_images;
if ($selectedItem?->relationLoaded('attachments')) { if ($displayImage && $selectedItem?->relationLoaded('attachments')) {
$imageUrl = $selectedItem->attachments->first()?->getTemporaryUrl(1440); $imageUrl = $selectedItem->attachments->first()?->getTemporaryUrl(1440);
} }
if ($imageUrl === null && $this->catalogItem?->relationLoaded('attachments')) { if ($displayImage && $imageUrl === null && $this->catalogItem?->relationLoaded('attachments')) {
$imageUrl = $this->catalogItem->attachments->first()?->getTemporaryUrl(1440); $imageUrl = $this->catalogItem->attachments->first()?->getTemporaryUrl(1440);
} }

View File

@@ -6,6 +6,7 @@ use App\Domains\Cart\Models\CartItem;
use App\Domains\Catalog\Models\CatalogItem; use App\Domains\Catalog\Models\CatalogItem;
use App\Domains\Catalog\Models\Variant; use App\Domains\Catalog\Models\Variant;
use App\Domains\Purchase\Models\PurchaseItem; use App\Domains\Purchase\Models\PurchaseItem;
use App\Domains\Tenant\Models\Tenant;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource; use Illuminate\Http\Resources\Json\JsonResource;
@@ -15,8 +16,13 @@ class PurchaseItemResource extends JsonResource
/** @return array<string, mixed> */ /** @return array<string, mixed> */
public function toArray(Request $request): array public function toArray(Request $request): array
{ {
$tenant = $request->route('tenant');
$displayImage = ! $tenant instanceof Tenant || $tenant->display_cart_item_images;
if ($this->resource instanceof PurchaseItem) { if ($this->resource instanceof PurchaseItem) {
$imageUrl = $this->imageAttachment?->getTemporaryUrl(1440); $imageUrl = $displayImage
? $this->imageAttachment?->getTemporaryUrl(1440)
: null;
$attributes = $this->variant_attributes ?? []; $attributes = $this->variant_attributes ?? [];
return [ return [
@@ -42,7 +48,9 @@ class PurchaseItemResource extends JsonResource
$quantity = (int) ($this->cantidad ?? 0); $quantity = (int) ($this->cantidad ?? 0);
$unitPrice = $this->resolveUnitPrice($selectedItem); $unitPrice = $this->resolveUnitPrice($selectedItem);
$lineTotal = $unitPrice * $quantity; $lineTotal = $unitPrice * $quantity;
$imageUrl = $this->resolveImageUrl($selectedItem, $catalogItem); $imageUrl = $displayImage
? $this->resolveImageUrl($selectedItem, $catalogItem)
: null;
return [ return [
'id' => $this->id, 'id' => $this->id,

View File

@@ -44,6 +44,7 @@ use Illuminate\Support\Facades\Schema;
'display_seach_bar', 'display_seach_bar',
'display_cart', 'display_cart',
'cart_editing_enabled', 'cart_editing_enabled',
'display_cart_item_images',
'scanner_category_validation_enabled', 'scanner_category_validation_enabled',
'event_title', 'event_title',
'event_location', 'event_location',
@@ -61,6 +62,7 @@ class Tenant extends Model
'display_seach_bar' => true, 'display_seach_bar' => true,
'display_cart' => true, 'display_cart' => true,
'cart_editing_enabled' => true, 'cart_editing_enabled' => true,
'display_cart_item_images' => true,
'scanner_category_validation_enabled' => true, 'scanner_category_validation_enabled' => true,
]; ];
@@ -111,6 +113,7 @@ class Tenant extends Model
'display_seach_bar' => 'boolean', 'display_seach_bar' => 'boolean',
'display_cart' => 'boolean', 'display_cart' => 'boolean',
'cart_editing_enabled' => 'boolean', 'cart_editing_enabled' => 'boolean',
'display_cart_item_images' => 'boolean',
'scanner_category_validation_enabled' => 'boolean', 'scanner_category_validation_enabled' => 'boolean',
]; ];
} }

View File

@@ -84,6 +84,7 @@ class StoreTenantRequest extends FormRequest
'display_seach_bar' => ['sometimes', 'boolean'], 'display_seach_bar' => ['sometimes', 'boolean'],
'display_cart' => ['sometimes', 'boolean'], 'display_cart' => ['sometimes', 'boolean'],
'cart_editing_enabled' => ['sometimes', 'boolean'], 'cart_editing_enabled' => ['sometimes', 'boolean'],
'display_cart_item_images' => ['sometimes', 'boolean'],
'scanner_category_validation_enabled' => ['sometimes', 'boolean'], 'scanner_category_validation_enabled' => ['sometimes', 'boolean'],
'website_type_code' => [ 'website_type_code' => [
'required_with:extras', 'required_with:extras',

View File

@@ -94,6 +94,7 @@ class UpdateTenantRequest extends FormRequest
'display_seach_bar' => ['sometimes', 'boolean'], 'display_seach_bar' => ['sometimes', 'boolean'],
'display_cart' => ['sometimes', 'boolean'], 'display_cart' => ['sometimes', 'boolean'],
'cart_editing_enabled' => ['sometimes', 'boolean'], 'cart_editing_enabled' => ['sometimes', 'boolean'],
'display_cart_item_images' => ['sometimes', 'boolean'],
'scanner_category_validation_enabled' => ['sometimes', 'boolean'], 'scanner_category_validation_enabled' => ['sometimes', 'boolean'],
]; ];
} }

View File

@@ -75,6 +75,7 @@ class TenantResource extends JsonResource
'display_seach_bar' => $this->display_seach_bar, 'display_seach_bar' => $this->display_seach_bar,
'display_cart' => $this->display_cart, 'display_cart' => $this->display_cart,
'cart_editing_enabled' => $this->cart_editing_enabled, 'cart_editing_enabled' => $this->cart_editing_enabled,
'display_cart_item_images' => $this->display_cart_item_images,
'scanner_category_validation_enabled' => $this->scanner_category_validation_enabled, 'scanner_category_validation_enabled' => $this->scanner_category_validation_enabled,
'social_media' => $this->whenLoaded( 'social_media' => $this->whenLoaded(
'socialMedia', 'socialMedia',

View File

@@ -0,0 +1,29 @@
<?php
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
{
private const DESFILE_TENANT_CODE = 'desfile_pura_tendencia';
public function up(): void
{
Schema::table('tenants', function (Blueprint $table): void {
$table->boolean('display_cart_item_images')->default(true)->after('cart_editing_enabled');
});
DB::table('tenants')
->where('codigo', self::DESFILE_TENANT_CODE)
->update(['display_cart_item_images' => false]);
}
public function down(): void
{
Schema::table('tenants', function (Blueprint $table): void {
$table->dropColumn('display_cart_item_images');
});
}
};

View File

@@ -43,6 +43,7 @@ class DesfilePuraTendenciaSeeder extends Seeder
$tenant->update([ $tenant->update([
'client_id' => $client->id, 'client_id' => $client->id,
'cart_editing_enabled' => false, 'cart_editing_enabled' => false,
'display_cart_item_images' => false,
]); ]);
return; return;
@@ -76,6 +77,7 @@ class DesfilePuraTendenciaSeeder extends Seeder
'display_seach_bar' => false, 'display_seach_bar' => false,
'display_cart' => true, 'display_cart' => true,
'cart_editing_enabled' => false, 'cart_editing_enabled' => false,
'display_cart_item_images' => false,
'scanner_category_validation_enabled' => false, 'scanner_category_validation_enabled' => false,
'website_type_code' => 'onticket', 'website_type_code' => 'onticket',
'header_logo' => $this->uploadedImage( 'header_logo' => $this->uploadedImage(

View File

@@ -75,6 +75,21 @@ class CartControllerTest extends TestCase
]); ]);
} }
public function test_it_filters_item_images_when_the_tenant_disables_them(): void
{
$tenant = $this->createTenant('acme');
$tenant->update(['display_cart_item_images' => false]);
$item = $this->createDirectItem($tenant, 10, '49.90');
$item->attachments()->attach($this->createAttachment('cart-item'), ['orden' => 0]);
$this->postJson('/api/tenants/acme/cart/items', [
'catalog_item_id' => $item->id,
'cantidad' => 1,
])
->assertOk()
->assertJsonPath('data.items.0.imagen', null);
}
public function test_it_adds_a_specific_variant_and_merges_repeated_additions(): void public function test_it_adds_a_specific_variant_and_merges_repeated_additions(): void
{ {
$tenant = $this->createTenant('acme'); $tenant = $this->createTenant('acme');

View File

@@ -0,0 +1,38 @@
<?php
namespace Tests\Feature\Migrations;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
class AddDisplayCartItemImagesToTenantsTest extends TestCase
{
use RefreshDatabase;
public function test_it_hides_cart_item_images_only_for_desfile(): void
{
$migration = require database_path(
'migrations/2026_08_18_061000_add_display_cart_item_images_to_tenants_table.php'
);
$migration->down();
$migration->up();
$this->assertDatabaseHas('tenants', [
'codigo' => 'desfile_pura_tendencia',
'display_cart_item_images' => false,
]);
DB::table('tenants')->insert([
'codigo' => 'cart-images-default',
'nombre' => 'Cart images default',
'dominio' => 'cart-images-default.test',
]);
$this->assertDatabaseHas('tenants', [
'codigo' => 'cart-images-default',
'display_cart_item_images' => true,
]);
}
}

View File

@@ -847,6 +847,30 @@ class StorePurchaseTest extends TestCase
->assertJsonPath('data.total', '100.00'); ->assertJsonPath('data.total', '100.00');
} }
public function test_purchase_detail_filters_item_images_when_the_tenant_disables_them(): void
{
$tenant = $this->createTenant('sonder', 'Sonder', 'sonder.com.ar');
$tenant->update(['display_cart_item_images' => false]);
$user = User::factory()->create([
'email' => 'buyer@example.com',
]);
$variant = $this->createVariantForTenant('sonder', 10, '50.00');
$purchase = $this->createCheckoutPurchase($user, 'sonder', $variant, 2);
$image = Attachment::create([
'key' => (string) Str::uuid(),
'path' => 'catalog/purchase-item.png',
'filename' => 'purchase-item.png',
'type' => AttachmentType::Image,
'mime_type' => 'image/png',
]);
$purchase->items()->firstOrFail()->update(['image_attachment_id' => $image->id]);
$this->actingAs($user, 'sanctum')
->getJson("/api/tenants/sonder/compras/{$purchase->id}")
->assertOk()
->assertJsonPath('data.items.0.item_details.imagen', null);
}
public function test_purchase_detail_uses_purchase_items_for_pending_payment_purchase(): void public function test_purchase_detail_uses_purchase_items_for_pending_payment_purchase(): void
{ {
$this->createTenant('sonder', 'Sonder', 'sonder.com.ar'); $this->createTenant('sonder', 'Sonder', 'sonder.com.ar');

View File

@@ -45,6 +45,7 @@ class DesfilePuraTendenciaSeederTest extends TestCase
'display_categories' => false, 'display_categories' => false,
'display_seach_bar' => false, 'display_seach_bar' => false,
'cart_editing_enabled' => false, 'cart_editing_enabled' => false,
'display_cart_item_images' => false,
'site_title' => 'Desfile Pura Tendencia', 'site_title' => 'Desfile Pura Tendencia',
]); ]);

View File

@@ -74,6 +74,7 @@ class BootstrapTenantControllerTest extends TestCase
'display_seach_bar' => false, 'display_seach_bar' => false,
'display_cart' => false, 'display_cart' => false,
'cart_editing_enabled' => false, 'cart_editing_enabled' => false,
'display_cart_item_images' => false,
]); ]);
$response = $this->getJson('/api/tenants/bootstrap?dominio=acme.com&path=%2F'); $response = $this->getJson('/api/tenants/bootstrap?dominio=acme.com&path=%2F');
@@ -91,6 +92,7 @@ class BootstrapTenantControllerTest extends TestCase
->assertJsonPath('data.display_seach_bar', false) ->assertJsonPath('data.display_seach_bar', false)
->assertJsonPath('data.display_cart', false) ->assertJsonPath('data.display_cart', false)
->assertJsonPath('data.cart_editing_enabled', false) ->assertJsonPath('data.cart_editing_enabled', false)
->assertJsonPath('data.display_cart_item_images', false)
->assertJsonPath('data.header_bg_color', '#ffffff')->assertJsonPath('data.footer_bg_color', '#ffffff'); ->assertJsonPath('data.header_bg_color', '#ffffff')->assertJsonPath('data.footer_bg_color', '#ffffff');
$headerUrl = $response->json('data.header_logo'); $headerUrl = $response->json('data.header_logo');
@@ -748,6 +750,7 @@ class BootstrapTenantControllerTest extends TestCase
$response = $this->putJson("/api/tenants/{$tenant->codigo}", [ $response = $this->putJson("/api/tenants/{$tenant->codigo}", [
'primary_color' => '#000000', 'primary_color' => '#000000',
'cart_editing_enabled' => false, 'cart_editing_enabled' => false,
'display_cart_item_images' => false,
]); ]);
$response $response
@@ -755,7 +758,8 @@ class BootstrapTenantControllerTest extends TestCase
->assertJsonPath('data.codigo', 'acme') ->assertJsonPath('data.codigo', 'acme')
->assertJsonPath('data.nombre', 'Acme') ->assertJsonPath('data.nombre', 'Acme')
->assertJsonPath('data.primary_color', '#000000') ->assertJsonPath('data.primary_color', '#000000')
->assertJsonPath('data.cart_editing_enabled', false); ->assertJsonPath('data.cart_editing_enabled', false)
->assertJsonPath('data.display_cart_item_images', false);
$this->assertDatabaseHas('tenants', [ $this->assertDatabaseHas('tenants', [
'id' => $tenant->id, 'id' => $tenant->id,
@@ -763,6 +767,7 @@ class BootstrapTenantControllerTest extends TestCase
'nombre' => 'Acme', 'nombre' => 'Acme',
'primary_color' => '#000000', 'primary_color' => '#000000',
'cart_editing_enabled' => false, 'cart_editing_enabled' => false,
'display_cart_item_images' => false,
]); ]);
} }