pdv.backend/app/Http/Requests/App/InventoryUpdateRequest.php

133 lines
5.7 KiB
PHP

<?php namespace App\Http\Requests\App;
use Illuminate\Foundation\Http\FormRequest;
class InventoryUpdateRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
*/
public function rules(): array
{
$inventoryId = $this->route('inventario')?->id;
return [
// Campos de Inventory
'name' => ['nullable', 'string', 'max:100'],
'key_sat' => ['nullable', 'string', 'max:20'],
'sku' => ['nullable', 'string', 'max:50'],
'barcode' => ['nullable', 'string', 'unique:inventories,barcode,' . $inventoryId],
'category_id' => ['nullable', 'exists:categories,id'],
'subcategory_id' => ['nullable', 'required_with:category_id', 'exists:subcategories,id'],
'unit_of_measure_id' => ['nullable', 'exists:units_of_measurement,id'],
'track_serials' => ['nullable', 'boolean'],
// Campos de Price
'cost' => ['nullable', 'numeric', 'min:0'],
'retail_price' => ['nullable', 'numeric', 'min:0'],
'tax' => ['nullable', 'numeric', 'min:0', 'max:100'],
];
}
public function messages(): array
{
return [
// Mensajes de Inventory
'name.string' => 'El nombre debe ser una cadena de texto.',
'name.max' => 'El nombre no debe exceder los 100 caracteres.',
'sku.string' => 'El SKU debe ser una cadena de texto.',
'sku.max' => 'El SKU no debe exceder los 50 caracteres.',
'sku.unique' => 'El SKU ya está en uso.',
'barcode.string' => 'El código de barras debe ser una cadena de texto.',
'barcode.unique' => 'El código de barras ya está registrado en otro producto.',
'category_id.exists' => 'La clasificación seleccionada no es válida.',
'subcategory_id.required_with' => 'La subclasificación es obligatoria cuando se asigna una clasificación.',
'subcategory_id.exists' => 'La subclasificación seleccionada no es válida.',
// Mensajes de Price
'cost.numeric' => 'El costo debe ser un número.',
'cost.min' => 'El costo no puede ser negativo.',
'retail_price.numeric' => 'El precio de venta debe ser un número.',
'retail_price.min' => 'El precio de venta no puede ser negativo.',
'retail_price.gt' => 'El precio de venta debe ser mayor que el costo.',
'tax.numeric' => 'El impuesto debe ser un número.',
'tax.min' => 'El impuesto no puede ser negativo.',
'tax.max' => 'El impuesto no puede exceder el 100%.',
];
}
/**
* Validación condicional: retail_price > cost solo si cost > 0
* Y validación track_serials con unidades decimales
*/
public function withValidator($validator)
{
$validator->after(function ($validator) {
/** @var \App\Models\Inventory $inventory */
$inventory = $this->route('inventario');
// Validar que la subcategoría pertenezca a la categoría seleccionada
$categoryId = $this->input('category_id', $inventory?->category_id);
$subcategoryId = $this->input('subcategory_id');
if ($subcategoryId && $categoryId) {
$subcategory = \App\Models\Subcategory::find($subcategoryId);
if ($subcategory && (int) $subcategory->category_id !== (int) $categoryId) {
$validator->errors()->add(
'subcategory_id',
'La subclasificación no pertenece a la clasificación seleccionada.'
);
}
}
// Bloquear cambio de unidad de medida si el producto ya tiene movimientos de inventario
if ($this->has('unit_of_measure_id') && $inventory) {
$newUnitId = $this->input('unit_of_measure_id');
$currentUnitId = $inventory->unit_of_measure_id;
if ((int) $newUnitId !== (int) $currentUnitId) {
$hasMovements = \App\Models\InventoryMovement::where('inventory_id', $inventory->id)->exists();
if ($hasMovements) {
$validator->errors()->add(
'unit_of_measure_id',
'No se puede cambiar la unidad de medida porque el producto ya tiene existencias registradas.'
);
}
}
}
$cost = $this->input('cost');
$retailPrice = $this->input('retail_price');
if ($cost !== null && $cost > 0 && $retailPrice !== null && $retailPrice <= $cost) {
$validator->errors()->add(
'retail_price',
'El precio de venta debe ser mayor que el costo.'
);
}
// Validar incompatibilidad track_serials + unidades decimales
$trackSerials = $this->input('track_serials', $inventory?->track_serials);
$unitId = $this->input('unit_of_measure_id', $inventory?->unit_of_measure_id);
if ($trackSerials && $unitId) {
$unit = \App\Models\UnitOfMeasurement::find($unitId);
if ($unit && $unit->allows_decimals) {
$validator->errors()->add(
'track_serials',
'No se pueden usar seriales con unidades fraccionarias (kg, L, m). Cambia a Unidad/Pieza/Caja o desactiva los seriales.'
);
}
}
});
}
}