feat(cart): implement cart editing policies and update related functionality
This commit is contained in:
@@ -4,6 +4,7 @@ namespace App\Domains\Cart\Resources;
|
||||
|
||||
use App\Domains\Cart\Models\CartItem;
|
||||
use App\Domains\Catalog\Enums\InventoryPolicy;
|
||||
use App\Domains\Catalog\Models\Variant;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
@@ -22,6 +23,8 @@ class CartItemResource extends JsonResource
|
||||
$imageUrl = null;
|
||||
$tenant = $request->route('tenant');
|
||||
$displayImage = ! $tenant instanceof Tenant || $tenant->display_cart_item_images;
|
||||
$includeVariants = $tenant instanceof Tenant
|
||||
&& $tenant->cart_editing_policy->allowsVariantChanges();
|
||||
|
||||
if ($displayImage && $selectedItem?->relationLoaded('attachments')) {
|
||||
$imageUrl = $selectedItem->attachments->first()?->getTemporaryUrl(1440);
|
||||
@@ -39,14 +42,27 @@ class CartItemResource extends JsonResource
|
||||
'variant_id' => $this->variant_id,
|
||||
'nombre' => $selectedItem?->getName(),
|
||||
'imagen' => $imageUrl,
|
||||
'variant' => $this->variant === null ? null : [
|
||||
'id' => $this->variant->id,
|
||||
'precio' => $this->formatMoney($this->variant->getPrice()),
|
||||
'stock_tecnico' => $this->catalogItem->inventory_policy === InventoryPolicy::Unlimited
|
||||
? null
|
||||
: $this->variant->inventory->availableStock(),
|
||||
'values' => $this->variant->selectorOptions($this->catalogItem->itemAttributes),
|
||||
],
|
||||
'variant' => $this->variant === null ? null : $this->variantData($this->variant),
|
||||
'variants' => $this->when(
|
||||
$includeVariants,
|
||||
fn () => $this->catalogItem
|
||||
->visibleVariants($this->variant_id)
|
||||
->map(fn (Variant $variant): array => $this->variantData($variant))
|
||||
->values(),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
/** @return array<string, mixed> */
|
||||
protected function variantData(Variant $variant): array
|
||||
{
|
||||
return [
|
||||
'id' => $variant->id,
|
||||
'precio' => $this->formatMoney($variant->precio ?? $this->catalogItem->precio),
|
||||
'stock_tecnico' => $this->catalogItem->inventory_policy === InventoryPolicy::Unlimited
|
||||
? null
|
||||
: $variant->inventory->availableStock(),
|
||||
'values' => $variant->selectorOptions($this->catalogItem->itemAttributes),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ use App\Domains\Tenant\Models\Tenant;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Symfony\Component\HttpFoundation\Cookie;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
@@ -27,7 +28,7 @@ class CartService
|
||||
return $this->makeEmptyCart($tenant);
|
||||
}
|
||||
|
||||
return $this->loadCart($cart);
|
||||
return $this->loadCart($cart, $tenant);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -46,7 +47,7 @@ class CartService
|
||||
$cart->addItem($catalogItemId, $variantId, $quantity);
|
||||
|
||||
return [
|
||||
'cart' => $this->loadCart($cart),
|
||||
'cart' => $this->loadCart($cart, $tenant),
|
||||
'guest_token' => $resolvedIdentity['generated_guest_token'],
|
||||
];
|
||||
}
|
||||
@@ -59,20 +60,38 @@ class CartService
|
||||
?int $variantId,
|
||||
bool $updateVariant,
|
||||
): Cart {
|
||||
if ($updateVariant && ! $tenant->cart_editing_policy->allowsVariantChanges()) {
|
||||
throw ValidationException::withMessages([
|
||||
'variant_id' => __('api.cart.variant_change_disabled'),
|
||||
]);
|
||||
}
|
||||
|
||||
if (! $updateVariant && ! $tenant->cart_editing_policy->allowsQuantityChanges()) {
|
||||
throw ValidationException::withMessages([
|
||||
'cantidad' => __('api.cart.editing_disabled'),
|
||||
]);
|
||||
}
|
||||
|
||||
$identity = $this->requireIdentity($request);
|
||||
$cart = $this->findCartOrFail($tenant, $identity);
|
||||
$cart->updateItem($cartItemId, $quantity, $variantId, $updateVariant);
|
||||
|
||||
return $this->loadCart($cart);
|
||||
return $this->loadCart($cart, $tenant);
|
||||
}
|
||||
|
||||
public function removeItem(Tenant $tenant, Request $request, int $cartItemId): Cart
|
||||
{
|
||||
if (! $tenant->cart_editing_policy->allowsRemoval()) {
|
||||
throw ValidationException::withMessages([
|
||||
'cart_item' => __('api.cart.editing_disabled'),
|
||||
]);
|
||||
}
|
||||
|
||||
$identity = $this->requireIdentity($request);
|
||||
$cart = $this->findCartOrFail($tenant, $identity);
|
||||
$cart->removeItem($cartItemId);
|
||||
|
||||
return $this->loadCart($cart);
|
||||
return $this->loadCart($cart, $tenant);
|
||||
}
|
||||
|
||||
public function makeGuestTokenCookie(string $guestToken): Cookie
|
||||
@@ -102,9 +121,9 @@ class CartService
|
||||
return $cart;
|
||||
}
|
||||
|
||||
protected function loadCart(Cart $cart): Cart
|
||||
protected function loadCart(Cart $cart, Tenant $tenant): Cart
|
||||
{
|
||||
return $cart->fresh()->load([
|
||||
$relations = [
|
||||
'items.catalogItem.attachments',
|
||||
'items.catalogItem.inventory',
|
||||
'items.catalogItem.itemAttributes.attribute',
|
||||
@@ -113,7 +132,21 @@ class CartService
|
||||
'items.variant.definitions.itemAttribute.attribute.options',
|
||||
'items.variant.eventDates',
|
||||
'items.variant.eventDate',
|
||||
]);
|
||||
];
|
||||
|
||||
if ($tenant->cart_editing_policy->allowsVariantChanges()) {
|
||||
$relations = [
|
||||
...$relations,
|
||||
'items.catalogItem.variants' => fn ($query) => $query->orderBy('id'),
|
||||
'items.catalogItem.variants.inventory',
|
||||
'items.catalogItem.variants.definitions' => fn ($query) => $query->orderBy('id'),
|
||||
'items.catalogItem.variants.definitions.itemAttribute.attribute.options',
|
||||
'items.catalogItem.variants.eventDates',
|
||||
'items.catalogItem.variants.eventDate',
|
||||
];
|
||||
}
|
||||
|
||||
return $cart->fresh()->load($relations);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
32
app/Domains/Tenant/Enums/CartEditingPolicy.php
Normal file
32
app/Domains/Tenant/Enums/CartEditingPolicy.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Tenant\Enums;
|
||||
|
||||
enum CartEditingPolicy: string
|
||||
{
|
||||
case Disabled = 'disabled';
|
||||
case QuantityAndRemove = 'quantity_and_remove';
|
||||
case Full = 'full';
|
||||
|
||||
public function allowsQuantityChanges(): bool
|
||||
{
|
||||
return $this !== self::Disabled;
|
||||
}
|
||||
|
||||
public function allowsRemoval(): bool
|
||||
{
|
||||
return $this !== self::Disabled;
|
||||
}
|
||||
|
||||
public function allowsVariantChanges(): bool
|
||||
{
|
||||
return $this === self::Full;
|
||||
}
|
||||
|
||||
public function allowsModification(): bool
|
||||
{
|
||||
return $this->allowsRemoval()
|
||||
|| $this->allowsQuantityChanges()
|
||||
|| $this->allowsVariantChanges();
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ use App\Domains\Client\Models\Client;
|
||||
use App\Domains\Event\Models\EventDate;
|
||||
use App\Domains\Menu\Models\Menu;
|
||||
use App\Domains\Menu\Models\TenantMenu;
|
||||
use App\Domains\Tenant\Enums\CartEditingPolicy;
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
@@ -44,7 +45,7 @@ use Illuminate\Support\Facades\Schema;
|
||||
'display_categories',
|
||||
'display_seach_bar',
|
||||
'display_cart',
|
||||
'cart_editing_enabled',
|
||||
'cart_editing_policy',
|
||||
'display_cart_item_images',
|
||||
'scanner_category_validation_enabled',
|
||||
'event_title',
|
||||
@@ -63,7 +64,7 @@ class Tenant extends Model
|
||||
'display_categories' => true,
|
||||
'display_seach_bar' => true,
|
||||
'display_cart' => true,
|
||||
'cart_editing_enabled' => true,
|
||||
'cart_editing_policy' => CartEditingPolicy::Full->value,
|
||||
'display_cart_item_images' => true,
|
||||
'scanner_category_validation_enabled' => true,
|
||||
];
|
||||
@@ -114,7 +115,7 @@ class Tenant extends Model
|
||||
'display_categories' => 'boolean',
|
||||
'display_seach_bar' => 'boolean',
|
||||
'display_cart' => 'boolean',
|
||||
'cart_editing_enabled' => 'boolean',
|
||||
'cart_editing_policy' => CartEditingPolicy::class,
|
||||
'display_cart_item_images' => 'boolean',
|
||||
'scanner_category_validation_enabled' => 'boolean',
|
||||
];
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace App\Domains\Tenant\Requests;
|
||||
use App\Domains\Catalog\Enums\GroupLayout;
|
||||
use App\Domains\Catalog\Enums\ProductLayout;
|
||||
use App\Domains\Shared\Rules\ImageOrBase64Rule;
|
||||
use App\Domains\Tenant\Enums\CartEditingPolicy;
|
||||
use App\Domains\Tenant\Services\WebsiteExtraService;
|
||||
use App\Domains\Tenant\Support\TenantDomainNormalizer;
|
||||
use Closure;
|
||||
@@ -107,7 +108,7 @@ class StoreTenantRequest extends FormRequest
|
||||
'display_categories' => ['sometimes', 'boolean'],
|
||||
'display_seach_bar' => ['sometimes', 'boolean'],
|
||||
'display_cart' => ['sometimes', 'boolean'],
|
||||
'cart_editing_enabled' => ['sometimes', 'boolean'],
|
||||
'cart_editing_policy' => ['sometimes', Rule::enum(CartEditingPolicy::class)],
|
||||
'display_cart_item_images' => ['sometimes', 'boolean'],
|
||||
'scanner_category_validation_enabled' => ['sometimes', 'boolean'],
|
||||
'website_type_code' => [
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace App\Domains\Tenant\Requests;
|
||||
use App\Domains\Catalog\Enums\GroupLayout;
|
||||
use App\Domains\Catalog\Enums\ProductLayout;
|
||||
use App\Domains\Shared\Rules\ImageOrBase64Rule;
|
||||
use App\Domains\Tenant\Enums\CartEditingPolicy;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use App\Domains\Tenant\Support\TenantDomainNormalizer;
|
||||
use Closure;
|
||||
@@ -128,7 +129,7 @@ class UpdateTenantRequest extends FormRequest
|
||||
'display_categories' => ['sometimes', 'boolean'],
|
||||
'display_seach_bar' => ['sometimes', 'boolean'],
|
||||
'display_cart' => ['sometimes', 'boolean'],
|
||||
'cart_editing_enabled' => ['sometimes', 'boolean'],
|
||||
'cart_editing_policy' => ['sometimes', Rule::enum(CartEditingPolicy::class)],
|
||||
'display_cart_item_images' => ['sometimes', 'boolean'],
|
||||
'scanner_category_validation_enabled' => ['sometimes', 'boolean'],
|
||||
];
|
||||
|
||||
23
app/Domains/Tenant/Resources/CartEditingPolicyResource.php
Normal file
23
app/Domains/Tenant/Resources/CartEditingPolicyResource.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Tenant\Resources;
|
||||
|
||||
use App\Domains\Tenant\Enums\CartEditingPolicy;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
/** @mixin CartEditingPolicy */
|
||||
class CartEditingPolicyResource extends JsonResource
|
||||
{
|
||||
/** @return array<string, bool|string> */
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
'code' => $this->resource->value,
|
||||
'allow_modify' => $this->resource->allowsModification(),
|
||||
'allow_delete' => $this->resource->allowsRemoval(),
|
||||
'allow_update_quantity' => $this->resource->allowsQuantityChanges(),
|
||||
'allow_update_variant' => $this->resource->allowsVariantChanges(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -75,7 +75,7 @@ class TenantResource extends JsonResource
|
||||
'display_categories' => $this->display_categories,
|
||||
'display_seach_bar' => $this->display_seach_bar,
|
||||
'display_cart' => $this->display_cart,
|
||||
'cart_editing_enabled' => $this->cart_editing_enabled,
|
||||
'cart_editing_policy' => CartEditingPolicyResource::make($this->cart_editing_policy),
|
||||
'display_cart_item_images' => $this->display_cart_item_images,
|
||||
'scanner_category_validation_enabled' => $this->scanner_category_validation_enabled,
|
||||
'social_media' => $this->whenLoaded(
|
||||
|
||||
Reference in New Issue
Block a user