diff --git a/app/Domains/Ticketing/FiestaFutbolInfantil/Requests/UpdateHistoricalFoodStockRequest.php b/app/Domains/Ticketing/FiestaFutbolInfantil/Requests/UpdateHistoricalFoodStockRequest.php index d137b168..caa5e853 100644 --- a/app/Domains/Ticketing/FiestaFutbolInfantil/Requests/UpdateHistoricalFoodStockRequest.php +++ b/app/Domains/Ticketing/FiestaFutbolInfantil/Requests/UpdateHistoricalFoodStockRequest.php @@ -17,10 +17,9 @@ class UpdateHistoricalFoodStockRequest extends FormRequest return [ 'variants' => ['required', 'array', 'min:1', 'max:500'], 'stock_adjustment_id' => ['nullable', 'uuid'], - 'variants.*' => ['required', 'array:id,stock,stock_difference'], + 'variants.*' => ['required', 'array:id,stock_difference'], 'variants.*.id' => ['required', 'integer', 'distinct'], - 'variants.*.stock' => ['required', 'integer', 'min:0'], - 'variants.*.stock_difference' => ['nullable', 'integer'], + 'variants.*.stock_difference' => ['required', 'integer'], ]; } } diff --git a/app/Domains/Ticketing/FiestaFutbolInfantil/Requests/UpsertAccommodationVariantsRequest.php b/app/Domains/Ticketing/FiestaFutbolInfantil/Requests/UpsertAccommodationVariantsRequest.php index 11ff365c..7e2475eb 100644 --- a/app/Domains/Ticketing/FiestaFutbolInfantil/Requests/UpsertAccommodationVariantsRequest.php +++ b/app/Domains/Ticketing/FiestaFutbolInfantil/Requests/UpsertAccommodationVariantsRequest.php @@ -3,6 +3,7 @@ namespace App\Domains\Ticketing\FiestaFutbolInfantil\Requests; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Validation\Validator; class UpsertAccommodationVariantsRequest extends FormRequest { @@ -21,9 +22,37 @@ class UpsertAccommodationVariantsRequest extends FormRequest 'variants.*.id' => ['sometimes', 'nullable', 'integer', 'distinct'], 'variants.*.title' => ['required', 'string', 'max:255'], 'variants.*.description' => ['sometimes', 'nullable', 'string'], - 'variants.*.stock' => ['required', 'integer', 'min:0'], - 'variants.*.stock_difference' => ['nullable', 'integer'], + 'variants.*.stock' => ['sometimes', 'integer', 'min:0'], + 'variants.*.stock_difference' => ['sometimes', 'integer'], 'variants.*.price' => ['required', 'numeric', 'min:0', 'max:99999999.99'], ]; } + + /** @return array */ + public function after(): array + { + return [ + function (Validator $validator): void { + foreach ($this->input('variants', []) as $index => $variant) { + if (! is_array($variant)) { + continue; + } + + if (isset($variant['id']) && ! array_key_exists('stock_difference', $variant)) { + $validator->errors()->add( + "variants.{$index}.stock_difference", + 'La diferencia de stock es obligatoria al actualizar una variante.', + ); + } + + if (! isset($variant['id']) && ! array_key_exists('stock', $variant)) { + $validator->errors()->add( + "variants.{$index}.stock", + 'El stock inicial es obligatorio al crear una variante.', + ); + } + } + }, + ]; + } } diff --git a/app/Domains/Ticketing/FiestaFutbolInfantil/Requests/UpsertEntriesRequest.php b/app/Domains/Ticketing/FiestaFutbolInfantil/Requests/UpsertEntriesRequest.php index b18cf1f3..2b982850 100644 --- a/app/Domains/Ticketing/FiestaFutbolInfantil/Requests/UpsertEntriesRequest.php +++ b/app/Domains/Ticketing/FiestaFutbolInfantil/Requests/UpsertEntriesRequest.php @@ -47,8 +47,8 @@ class UpsertEntriesRequest extends FormRequest fn ($query) => $query->where('tenant_code', $tenantCode) ), ], - 'entries.*.stock' => ['required', 'integer', 'min:0'], - 'entries.*.stock_difference' => ['nullable', 'integer'], + 'entries.*.stock' => ['sometimes', 'integer', 'min:0'], + 'entries.*.stock_difference' => ['sometimes', 'integer'], 'entries.*.price' => ['required', 'numeric', 'min:0', 'max:99999999.99'], ]; } @@ -63,6 +63,20 @@ class UpsertEntriesRequest extends FormRequest continue; } + if (isset($entry['id']) && ! array_key_exists('stock_difference', $entry)) { + $validator->errors()->add( + "entries.{$index}.stock_difference", + 'La diferencia de stock es obligatoria al actualizar una entrada.', + ); + } + + if (! isset($entry['id']) && ! array_key_exists('stock', $entry)) { + $validator->errors()->add( + "entries.{$index}.stock", + 'El stock inicial es obligatorio al crear una entrada.', + ); + } + $dateIds = $entry['event_date_ids'] ?? []; if (! is_array($dateIds)) { diff --git a/app/Domains/Ticketing/FiestaFutbolInfantil/Requests/UpsertFoodVariantsRequest.php b/app/Domains/Ticketing/FiestaFutbolInfantil/Requests/UpsertFoodVariantsRequest.php index 1f6872ec..50b557d5 100644 --- a/app/Domains/Ticketing/FiestaFutbolInfantil/Requests/UpsertFoodVariantsRequest.php +++ b/app/Domains/Ticketing/FiestaFutbolInfantil/Requests/UpsertFoodVariantsRequest.php @@ -33,8 +33,8 @@ class UpsertFoodVariantsRequest extends FormRequest 'variants.*.schedule' => ['required', 'string', 'max:255'], 'variants.*.service' => ['required', 'string', 'max:255'], 'variants.*.description' => ['sometimes', 'nullable', 'string'], - 'variants.*.stock' => ['required', 'integer', 'min:0'], - 'variants.*.stock_difference' => ['nullable', 'integer'], + 'variants.*.stock' => ['sometimes', 'integer', 'min:0'], + 'variants.*.stock_difference' => ['sometimes', 'integer'], 'variants.*.price' => ['required', 'numeric', 'min:0', 'max:99999999.99'], ]; } @@ -51,6 +51,20 @@ class UpsertFoodVariantsRequest extends FormRequest continue; } + if (isset($variant['id']) && ! array_key_exists('stock_difference', $variant)) { + $validator->errors()->add( + "variants.{$index}.stock_difference", + 'La diferencia de stock es obligatoria al actualizar una variante.', + ); + } + + if (! isset($variant['id']) && ! array_key_exists('stock', $variant)) { + $validator->errors()->add( + "variants.{$index}.stock", + 'El stock inicial es obligatorio al crear una variante.', + ); + } + $key = implode('|', [ $variant['event_date_id'] ?? '', mb_strtolower(trim((string) ($variant['schedule'] ?? ''))), diff --git a/app/Domains/Ticketing/FiestaFutbolInfantil/Requests/UpsertMerchandiseRequest.php b/app/Domains/Ticketing/FiestaFutbolInfantil/Requests/UpsertMerchandiseRequest.php index 15b05967..0e683413 100644 --- a/app/Domains/Ticketing/FiestaFutbolInfantil/Requests/UpsertMerchandiseRequest.php +++ b/app/Domains/Ticketing/FiestaFutbolInfantil/Requests/UpsertMerchandiseRequest.php @@ -4,6 +4,7 @@ namespace App\Domains\Ticketing\FiestaFutbolInfantil\Requests; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Validation\Rule; +use Illuminate\Validation\Validator; class UpsertMerchandiseRequest extends FormRequest { @@ -44,9 +45,44 @@ class UpsertMerchandiseRequest extends FormRequest 'items.*.variants.*.id' => ['sometimes', 'nullable', 'integer', 'distinct'], 'items.*.variants.*.color' => ['required', 'string', 'max:255'], 'items.*.variants.*.size' => ['required', 'string', 'max:255'], - 'items.*.variants.*.stock' => ['required', 'integer', 'min:0'], - 'items.*.variants.*.stock_difference' => ['nullable', 'integer'], + 'items.*.variants.*.stock' => ['sometimes', 'integer', 'min:0'], + 'items.*.variants.*.stock_difference' => ['sometimes', 'integer'], 'items.*.variants.*.price' => ['required', 'numeric', 'min:0', 'max:99999999.99'], ]; } + + /** @return array */ + public function after(): array + { + return [ + function (Validator $validator): void { + foreach ($this->input('items', []) as $itemIndex => $item) { + if (! is_array($item)) { + continue; + } + + foreach ($item['variants'] ?? [] as $variantIndex => $variant) { + if (! is_array($variant)) { + continue; + } + + $prefix = "items.{$itemIndex}.variants.{$variantIndex}"; + if (isset($variant['id']) && ! array_key_exists('stock_difference', $variant)) { + $validator->errors()->add( + "{$prefix}.stock_difference", + 'La diferencia de stock es obligatoria al actualizar una variante.', + ); + } + + if (! isset($variant['id']) && ! array_key_exists('stock', $variant)) { + $validator->errors()->add( + "{$prefix}.stock", + 'El stock inicial es obligatorio al crear una variante.', + ); + } + } + } + }, + ]; + } } diff --git a/app/Domains/Ticketing/FiestaFutbolInfantil/Services/AccommodationService.php b/app/Domains/Ticketing/FiestaFutbolInfantil/Services/AccommodationService.php index 20e44ee6..4e33b50e 100644 --- a/app/Domains/Ticketing/FiestaFutbolInfantil/Services/AccommodationService.php +++ b/app/Domains/Ticketing/FiestaFutbolInfantil/Services/AccommodationService.php @@ -169,7 +169,7 @@ class AccommodationService 'title' => trim($variant['title']), 'value' => $this->valueCode($variant['title']), 'description' => $variant['description'] ?? null, - 'stock' => (int) $variant['stock'], + ...(array_key_exists('stock', $variant) ? ['stock' => (int) $variant['stock']] : []), ])->all(); } @@ -257,8 +257,10 @@ class AccommodationService 'descripcion' => $data['description'], 'precio' => $data['price'], ]); - $stockDifference = (int) ($data['stock_difference'] ?? ($data['stock'] - $inventory->availableStock())); - $inventory->adjustAvailableStock($stockDifference, idempotencyKey: $stockAdjustmentId); + $inventory->adjustAvailableStock( + (int) $data['stock_difference'], + idempotencyKey: $stockAdjustmentId, + ); $variant->definitions()->updateOrCreate( ['item_attribute_id' => $itemAttribute->id], ['value' => $data['value']], diff --git a/app/Domains/Ticketing/FiestaFutbolInfantil/Services/EntryService.php b/app/Domains/Ticketing/FiestaFutbolInfantil/Services/EntryService.php index 5c486029..06ac4e09 100644 --- a/app/Domains/Ticketing/FiestaFutbolInfantil/Services/EntryService.php +++ b/app/Domains/Ticketing/FiestaFutbolInfantil/Services/EntryService.php @@ -141,8 +141,10 @@ class EntryService $catalogItem->itemAttributes() ->whereHas('attribute', fn ($query) => $query->where('codigo', 'event_date')) ->update(['allow_multi_select' => true]); - $stockDifference = (int) ($entry['stock_difference'] ?? ((int) $entry['stock'] - $inventory->availableStock())); - $inventory->adjustAvailableStock($stockDifference, idempotencyKey: $stockAdjustmentId); + $inventory->adjustAvailableStock( + (int) $entry['stock_difference'], + idempotencyKey: $stockAdjustmentId, + ); return $catalogItem->load([ 'variants.inventory', diff --git a/app/Domains/Ticketing/FiestaFutbolInfantil/Services/FoodService.php b/app/Domains/Ticketing/FiestaFutbolInfantil/Services/FoodService.php index 8b9f4c05..1e194eaf 100644 --- a/app/Domains/Ticketing/FiestaFutbolInfantil/Services/FoodService.php +++ b/app/Domains/Ticketing/FiestaFutbolInfantil/Services/FoodService.php @@ -113,7 +113,7 @@ class FoodService } /** - * @param array $variants + * @param array $variants */ public function updateHistoricalStock(Tenant $tenant, array $variants, ?string $stockAdjustmentId = null): CatalogItem { @@ -143,8 +143,10 @@ class FoodService } $inventory = $this->inventoryForHistoricalStockUpdate($variant); - $stockDifference = (int) ($data['stock_difference'] ?? ((int) $data['stock'] - $inventory->availableStock())); - $inventory->adjustAvailableStock($stockDifference, idempotencyKey: $stockAdjustmentId); + $inventory->adjustAvailableStock( + (int) $data['stock_difference'], + idempotencyKey: $stockAdjustmentId, + ); } return $this->current($tenant) ?? $food; @@ -294,7 +296,7 @@ class FoodService 'schedule' => $schedule->value, 'service' => $service->value, 'description' => (string) ($variant['description'] ?? ''), - 'stock' => (int) $variant['stock'], + ...(array_key_exists('stock', $variant) ? ['stock' => (int) $variant['stock']] : []), ]; })->all(); } @@ -407,8 +409,10 @@ class FoodService 'precio' => $data['price'], ]); $variant->eventDates()->sync([$data['event_date_id']]); - $stockDifference = (int) ($data['stock_difference'] ?? ($data['stock'] - $inventory->availableStock())); - $inventory->adjustAvailableStock($stockDifference, idempotencyKey: $stockAdjustmentId); + $inventory->adjustAvailableStock( + (int) $data['stock_difference'], + idempotencyKey: $stockAdjustmentId, + ); $this->syncDefinitions($variant, $itemAttributes, $data); } diff --git a/app/Domains/Ticketing/FiestaFutbolInfantil/Services/MerchandiseService.php b/app/Domains/Ticketing/FiestaFutbolInfantil/Services/MerchandiseService.php index 735406df..9beaac2d 100644 --- a/app/Domains/Ticketing/FiestaFutbolInfantil/Services/MerchandiseService.php +++ b/app/Domains/Ticketing/FiestaFutbolInfantil/Services/MerchandiseService.php @@ -245,7 +245,7 @@ class MerchandiseService ...$variant, 'color' => $color->value, 'size' => $size->value, - 'stock' => (int) $variant['stock'], + ...(array_key_exists('stock', $variant) ? ['stock' => (int) $variant['stock']] : []), ]; })->all(); } @@ -369,8 +369,10 @@ class MerchandiseService ->firstOrFail(); $variant->update(['precio' => $data['price']]); - $stockDifference = (int) ($data['stock_difference'] ?? ($data['stock'] - $inventory->availableStock())); - $inventory->adjustAvailableStock($stockDifference, idempotencyKey: $stockAdjustmentId); + $inventory->adjustAvailableStock( + (int) $data['stock_difference'], + idempotencyKey: $stockAdjustmentId, + ); $this->syncDefinitions($variant, $itemAttributes, $data); } diff --git a/tests/Feature/FiestaFutbolInfantil/AccommodationControllerTest.php b/tests/Feature/FiestaFutbolInfantil/AccommodationControllerTest.php index d04907fb..3050ac09 100644 --- a/tests/Feature/FiestaFutbolInfantil/AccommodationControllerTest.php +++ b/tests/Feature/FiestaFutbolInfantil/AccommodationControllerTest.php @@ -97,6 +97,8 @@ class AccommodationControllerTest extends TestCase $updated = $this->variantPayload('Casa Rodante Premium', 'Con electricidad', 15, 50000); $updated['id'] = $variantId; + unset($updated['stock']); + $updated['stock_difference'] = -5; $this->postJson('/api/v1/adminapp/tenant/accommodations', [ 'variants' => [ diff --git a/tests/Feature/FiestaFutbolInfantil/EntryControllerTest.php b/tests/Feature/FiestaFutbolInfantil/EntryControllerTest.php index fb3b59fb..b6066fe9 100644 --- a/tests/Feature/FiestaFutbolInfantil/EntryControllerTest.php +++ b/tests/Feature/FiestaFutbolInfantil/EntryControllerTest.php @@ -194,7 +194,7 @@ class EntryControllerTest extends TestCase 'title' => 'Abono actualizado', 'description' => 'Ahora incluye ambas fechas', 'event_date_ids' => [$firstDate->id, $secondDate->id], - 'stock' => 20, + 'stock_difference' => 10, 'price' => 250, ], [ @@ -268,7 +268,7 @@ class EntryControllerTest extends TestCase 'id' => $entryId, 'title' => 'Abono editado', 'event_date_ids' => [$date->id, $otherDate->id], - 'stock' => 30, + 'stock_difference' => 10, 'price' => 200, ]], ])->assertOk() @@ -329,7 +329,7 @@ class EntryControllerTest extends TestCase 'title' => 'Entrada inválida', 'description' => null, 'event_date_ids' => [$foreignDate->id], - 'stock' => 10, + 'stock_difference' => 0, 'price' => 100, ]], ]) @@ -371,7 +371,7 @@ class EntryControllerTest extends TestCase 'title' => 'Entrada inválida', 'description' => null, 'event_date_ids' => [$eventDate->id], - 'stock' => 10, + 'stock_difference' => 0, 'price' => 100, ]], ]) diff --git a/tests/Feature/FiestaFutbolInfantil/FoodControllerTest.php b/tests/Feature/FiestaFutbolInfantil/FoodControllerTest.php index bef74360..c0339163 100644 --- a/tests/Feature/FiestaFutbolInfantil/FoodControllerTest.php +++ b/tests/Feature/FiestaFutbolInfantil/FoodControllerTest.php @@ -124,6 +124,8 @@ class FoodControllerTest extends TestCase $updated = $this->variantPayload($secondDate->id, 'Cena', 'Vianda', 80, 8500); $updated['id'] = $variantId; + unset($updated['stock']); + $updated['stock_difference'] = -20; $updated['description'] = 'Cena para llevar'; $this->postJson('/api/v1/adminapp/tenant/foods', [ @@ -240,7 +242,7 @@ class FoodControllerTest extends TestCase Inventory::query()->whereKey($replacementInventoryId)->delete(); $updated = $this->patchJson('/api/v1/adminapp/tenant/foods/history-stock', [ - 'variants' => [['id' => $historicalVariantId, 'stock' => 45]], + 'variants' => [['id' => $historicalVariantId, 'stock_difference' => -55]], ]) ->assertOk() ->assertJsonPath('data.history.0.variants.0.id', $historicalVariantId) @@ -258,7 +260,7 @@ class FoodControllerTest extends TestCase ]); $this->patchJson('/api/v1/adminapp/tenant/foods/history-stock', [ - 'variants' => [['id' => $activeVariantId, 'stock' => 20]], + 'variants' => [['id' => $activeVariantId, 'stock_difference' => -60]], ]) ->assertUnprocessable() ->assertJsonValidationErrors(['variants.0.id']); diff --git a/tests/Feature/FiestaFutbolInfantil/MerchandiseControllerTest.php b/tests/Feature/FiestaFutbolInfantil/MerchandiseControllerTest.php index 09752a40..7f8ddcf8 100644 --- a/tests/Feature/FiestaFutbolInfantil/MerchandiseControllerTest.php +++ b/tests/Feature/FiestaFutbolInfantil/MerchandiseControllerTest.php @@ -112,6 +112,8 @@ class MerchandiseControllerTest extends TestCase $updatedVariant = $this->variantPayload('Blanco', 'M', 80, 12500); $updatedVariant['id'] = $variantId; + unset($updatedVariant['stock']); + $updatedVariant['stock_difference'] = -20; $updatedItem = $this->itemPayload('Camiseta oficial', 2, [ $updatedVariant, $this->variantPayload('Verde', 'L', 60, 15000), @@ -234,6 +236,8 @@ class MerchandiseControllerTest extends TestCase $secondItemVariantId = $created->json('data.1.variants.0.id'); $foreignVariant = $this->variantPayload('Azul Marino', 'XL', 20, 30000); $foreignVariant['id'] = $secondItemVariantId; + unset($foreignVariant['stock']); + $foreignVariant['stock_difference'] = 0; $firstItem = $this->itemPayload('Camiseta', 3, [$foreignVariant]); $firstItem['id'] = $firstItemId;