feat: add checkout editing policy to tenants and update related logic across services and resources
This commit is contained in:
@@ -11,9 +11,11 @@ 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\Support\Str;
|
||||
|
||||
#[Fillable([
|
||||
'tenant_code',
|
||||
'code',
|
||||
'source_type',
|
||||
'category_id',
|
||||
'product_layout',
|
||||
@@ -33,6 +35,29 @@ class FeaturedGroup extends Model
|
||||
'source_type' => FeaturedGroupSource::Manual->value,
|
||||
];
|
||||
|
||||
protected static function booted(): void
|
||||
{
|
||||
static::creating(function (FeaturedGroup $featuredGroup): void {
|
||||
if (filled($featuredGroup->code)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$baseCode = Str::slug($featuredGroup->group_name) ?: 'group';
|
||||
$code = $baseCode;
|
||||
$suffix = 2;
|
||||
|
||||
while (static::query()
|
||||
->where('tenant_code', $featuredGroup->tenant_code)
|
||||
->where('code', $code)
|
||||
->exists()) {
|
||||
$code = "{$baseCode}-{$suffix}";
|
||||
$suffix++;
|
||||
}
|
||||
|
||||
$featuredGroup->code = $code;
|
||||
});
|
||||
}
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
|
||||
@@ -15,6 +15,7 @@ class OnTicketFeaturedGroupResource extends JsonResource
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'code' => $this->code,
|
||||
'category_id' => $this->category_id,
|
||||
'category_name' => $this->category->nombre,
|
||||
'group_name' => $this->group_name,
|
||||
|
||||
@@ -20,6 +20,7 @@ class CatalogFeaturedGroupResource extends JsonResource
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'code' => $this->code,
|
||||
'title' => $this->group_name,
|
||||
'layout' => $this->product_layout->value,
|
||||
'group_layout' => $this->group_layout->value,
|
||||
|
||||
@@ -6,7 +6,9 @@ use App\Domains\Catalog\Models\StockReservation;
|
||||
use App\Domains\Catalog\Services\StockReservationService;
|
||||
use App\Domains\Purchase\Models\Purchase;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Throwable;
|
||||
|
||||
class ReleaseCheckoutService
|
||||
{
|
||||
@@ -40,7 +42,21 @@ class ReleaseCheckoutService
|
||||
->where('expires_at', '<=', now())
|
||||
->orderBy('id')
|
||||
->eachById(function (Purchase $purchase) use (&$expiredCount): void {
|
||||
$purchase = $this->expire($purchase);
|
||||
try {
|
||||
$purchase = $this->expire($purchase);
|
||||
} catch (Throwable $exception) {
|
||||
Log::channel('commands')->error('Failed to expire overdue purchase.', [
|
||||
'command' => 'reservations:expire',
|
||||
'purchase_id' => $purchase->getKey(),
|
||||
'tenant_codigo' => $purchase->tenant_codigo,
|
||||
'cart_id' => $purchase->cart_id,
|
||||
'status' => $purchase->status,
|
||||
'expires_at' => $purchase->expires_at,
|
||||
'exception' => $exception,
|
||||
]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($purchase->status === Purchase::STATUS_EXPIRED) {
|
||||
$expiredCount++;
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
|
||||
namespace App\Domains\Sale\Resources\AdminApp;
|
||||
|
||||
use App\Domains\Cart\Models\CartItem;
|
||||
use App\Domains\Purchase\Models\Purchase;
|
||||
use App\Domains\Purchase\Models\PurchaseItem;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/** @mixin Purchase */
|
||||
class SaleDetailResource extends JsonResource
|
||||
@@ -13,23 +15,44 @@ class SaleDetailResource extends JsonResource
|
||||
/** @return array<string, mixed> */
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
$items = $this->saleItems();
|
||||
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'items' => $this->items->map(fn (PurchaseItem $item): array => [
|
||||
'items' => $items->map(fn (PurchaseItem|CartItem $item): array => [
|
||||
'id' => $item->id,
|
||||
'product' => $item->item_nombre,
|
||||
'product' => $item instanceof PurchaseItem
|
||||
? $item->item_nombre
|
||||
: $item->selectedItem()?->getName(),
|
||||
'event_dates' => $this->eventDates($item),
|
||||
'quantity' => (int) $item->cantidad,
|
||||
'unit_price' => $this->formatMoney($item->precio_unitario),
|
||||
'total' => $this->formatMoney($item->total),
|
||||
'unit_price' => $this->formatMoney($this->unitPrice($item)),
|
||||
'total' => $this->formatMoney($this->lineTotal($item)),
|
||||
])->values(),
|
||||
'total' => $this->formatMoney($this->total),
|
||||
];
|
||||
}
|
||||
|
||||
/** @return list<string> */
|
||||
private function eventDates(PurchaseItem $item): array
|
||||
/** @return Collection<int, PurchaseItem|CartItem> */
|
||||
private function saleItems(): Collection
|
||||
{
|
||||
if ($this->items->isNotEmpty()) {
|
||||
return $this->items;
|
||||
}
|
||||
|
||||
return $this->cart?->items ?? collect();
|
||||
}
|
||||
|
||||
/** @return list<string> */
|
||||
private function eventDates(PurchaseItem|CartItem $item): array
|
||||
{
|
||||
if ($item instanceof CartItem) {
|
||||
return $item->variant?->selectedEventDates()
|
||||
->map(fn ($eventDate): string => $eventDate->date->format('Y-m-d'))
|
||||
->values()
|
||||
->all() ?? [];
|
||||
}
|
||||
|
||||
return collect($item->variant_attributes ?? [])
|
||||
->filter(fn (mixed $attribute): bool => is_array($attribute)
|
||||
&& mb_strtolower(trim((string) ($attribute['name'] ?? ''))) === 'fecha')
|
||||
@@ -43,6 +66,20 @@ class SaleDetailResource extends JsonResource
|
||||
->all();
|
||||
}
|
||||
|
||||
private function unitPrice(PurchaseItem|CartItem $item): float|int|string|null
|
||||
{
|
||||
return $item instanceof PurchaseItem
|
||||
? $item->precio_unitario
|
||||
: $item->selectedItem()?->getPrice();
|
||||
}
|
||||
|
||||
private function lineTotal(PurchaseItem|CartItem $item): float|int|string|null
|
||||
{
|
||||
return $item instanceof PurchaseItem
|
||||
? $item->total
|
||||
: ($item->selectedItem()?->getPrice() ?? 0) * $item->cantidad;
|
||||
}
|
||||
|
||||
private function formatMoney(float|int|string|null $amount): string
|
||||
{
|
||||
return number_format((float) ($amount ?? 0), 2, '.', '');
|
||||
|
||||
@@ -51,7 +51,13 @@ class AdminAppSaleService
|
||||
{
|
||||
return Purchase::query()
|
||||
->where('tenant_codigo', $tenant->codigo)
|
||||
->with('items')
|
||||
->with([
|
||||
'items',
|
||||
'cart.items.catalogItem',
|
||||
'cart.items.variant.catalogItem',
|
||||
'cart.items.variant.eventDates',
|
||||
'cart.items.variant.eventDate',
|
||||
])
|
||||
->findOrFail($saleId);
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,8 @@ use Illuminate\Support\Facades\Schema;
|
||||
'dominio',
|
||||
'base_path',
|
||||
'site_title',
|
||||
'address',
|
||||
'phone',
|
||||
'primary_color',
|
||||
'secondary_color',
|
||||
'danger_color',
|
||||
|
||||
@@ -82,6 +82,8 @@ class StoreTenantRequest extends FormRequest
|
||||
->where('dominio', $this->input('dominio')),
|
||||
],
|
||||
'site_title' => ['sometimes', 'nullable', 'string', 'max:255'],
|
||||
'address' => ['sometimes', 'nullable', 'string', 'max:255'],
|
||||
'phone' => ['sometimes', 'nullable', 'string', 'max:255'],
|
||||
'primary_color' => ['required', 'string', 'regex:/^#([a-fA-F0-9]{3,4}|[a-fA-F0-9]{6}|[a-fA-F0-9]{8})$/'],
|
||||
'secondary_color' => ['required', 'string', 'regex:/^#([a-fA-F0-9]{3,4}|[a-fA-F0-9]{6}|[a-fA-F0-9]{8})$/'],
|
||||
'danger_color' => ['required', 'string', 'regex:/^#([a-fA-F0-9]{3,4}|[a-fA-F0-9]{6}|[a-fA-F0-9]{8})$/'],
|
||||
|
||||
@@ -103,6 +103,8 @@ class UpdateTenantRequest extends FormRequest
|
||||
->ignore($tenant?->id),
|
||||
],
|
||||
'site_title' => ['sometimes', 'nullable', 'string', 'max:255'],
|
||||
'address' => ['sometimes', 'nullable', 'string', 'max:255'],
|
||||
'phone' => ['sometimes', 'nullable', 'string', 'max:255'],
|
||||
'primary_color' => ['nullable', 'string', 'regex:/^#([a-fA-F0-9]{3,4}|[a-fA-F0-9]{6}|[a-fA-F0-9]{8})$/'],
|
||||
'secondary_color' => ['nullable', 'string', 'regex:/^#([a-fA-F0-9]{3,4}|[a-fA-F0-9]{6}|[a-fA-F0-9]{8})$/'],
|
||||
'danger_color' => ['nullable', 'string', 'regex:/^#([a-fA-F0-9]{3,4}|[a-fA-F0-9]{6}|[a-fA-F0-9]{8})$/'],
|
||||
|
||||
@@ -32,6 +32,8 @@ class TenantResource extends JsonResource
|
||||
'site_title' => $this->site_title
|
||||
?? $this->websiteType?->site_title
|
||||
?? 'ShopitFront',
|
||||
'address' => $this->address,
|
||||
'phone' => $this->phone,
|
||||
'favicon' => ($this->favicon ?? $this->websiteType?->favicon)
|
||||
?->getTemporaryUrl(1440),
|
||||
'primary_color' => $this->primary_color,
|
||||
|
||||
Reference in New Issue
Block a user