feat: implement product creation workflow with variants, attribute definitions, and validation logic

This commit is contained in:
2026-06-29 11:02:04 -03:00
parent 9d3ed17c00
commit 18504594eb
23 changed files with 774 additions and 146 deletions

View File

@@ -7,7 +7,7 @@ use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
use Illuminate\Validation\Validator;
class StoreProductAttributeRequest extends FormRequest
class StoreAttributeRequest extends FormRequest
{
public function authorize(): bool
{
@@ -24,7 +24,7 @@ class StoreProductAttributeRequest extends FormRequest
'required',
'string',
'max:255',
Rule::unique('productos_attributes', 'codigo')->where(
Rule::unique('attribute', 'codigo')->where(
fn ($query) => $query->where('tenant_codigo', $this->route('tenant')?->codigo)
),
],

View File

@@ -23,6 +23,38 @@ class StoreProductRequest extends FormRequest
'nombre' => ['required', 'string', 'max:255'],
'descripcion' => ['nullable', 'string'],
'precio' => ['required', 'numeric', 'min:0'],
'attribute_ids' => ['sometimes', 'array'],
'attribute_ids.*' => [
'required',
'integer',
Rule::exists('attribute', 'id')->where(
fn ($query) => $query->where('tenant_codigo', $this->route('tenant')?->codigo)
),
],
'variants' => ['sometimes', 'array'],
'variants.*.slug' => ['nullable', 'string', 'max:255'],
'variants.*.nombre' => ['nullable', 'string', 'max:255'],
'variants.*.stock' => ['sometimes', 'integer', 'min:0'],
'variants.*.descripcion' => ['nullable', 'string'],
'variants.*.precio' => ['required', 'numeric', 'min:0'],
'variants.*.definitions' => [
'sometimes',
'array',
function ($attribute, $value, $fail) {
$attributeIds = array_column($value, 'attribute_id');
if (count($attributeIds) !== count(array_unique($attributeIds))) {
$fail('The definitions must have distinct attribute ids.');
}
}
],
'variants.*.definitions.*.attribute_id' => [
'required',
'integer',
Rule::exists('attribute', 'id')->where(
fn ($query) => $query->where('tenant_codigo', $this->route('tenant')?->codigo)
),
],
'variants.*.definitions.*.value' => ['nullable', 'string'],
];
}
}

View File

@@ -42,7 +42,7 @@ class StoreProductVariantRequest extends FormRequest
'required',
'integer',
'distinct',
Rule::exists('productos_attributes', 'id')->where(
Rule::exists('attribute', 'id')->where(
fn ($query) => $query->where('tenant_codigo', $this->route('tenant')?->codigo)
),
],

View File

@@ -2,13 +2,13 @@
namespace App\Domains\Catalog\Requests;
use App\Domains\Catalog\Models\ProductAttribute;
use App\Domains\Catalog\Models\Attribute;
use App\Domains\Shared\Enums\FieldType;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
use Illuminate\Validation\Validator;
class UpdateProductAttributeRequest extends FormRequest
class UpdateAttributeRequest extends FormRequest
{
public function authorize(): bool
{
@@ -20,15 +20,15 @@ class UpdateProductAttributeRequest extends FormRequest
*/
public function rules(): array
{
/** @var ProductAttribute|null $attribute */
$attribute = $this->route('productAttribute');
/** @var Attribute|null $attribute */
$attribute = $this->route('attribute');
return [
'codigo' => [
'required',
'string',
'max:255',
Rule::unique('productos_attributes', 'codigo')
Rule::unique('attribute', 'codigo')
->ignore($attribute?->id)
->where(fn ($query) => $query->where('tenant_codigo', $attribute?->tenant_codigo)),
],

View File

@@ -32,6 +32,45 @@ class UpdateProductRequest extends FormRequest
'nombre' => ['required', 'string', 'max:255'],
'descripcion' => ['nullable', 'string'],
'precio' => ['required', 'numeric', 'min:0'],
'attribute_ids' => ['sometimes', 'array'],
'attribute_ids.*' => [
'required',
'integer',
Rule::exists('attribute', 'id')->where(
fn ($query) => $query->where('tenant_codigo', $this->route('tenant')?->codigo)
),
],
'variants' => ['sometimes', 'array'],
'variants.*.id' => [
'sometimes',
'integer',
Rule::exists('productos_variantes', 'id')->where(
fn ($query) => $query->where('producto_id', $product?->id)
),
],
'variants.*.slug' => ['nullable', 'string', 'max:255'],
'variants.*.nombre' => ['nullable', 'string', 'max:255'],
'variants.*.stock' => ['sometimes', 'integer', 'min:0'],
'variants.*.descripcion' => ['nullable', 'string'],
'variants.*.precio' => ['required', 'numeric', 'min:0'],
'variants.*.definitions' => [
'sometimes',
'array',
function ($attribute, $value, $fail) {
$attributeIds = array_column($value, 'attribute_id');
if (count($attributeIds) !== count(array_unique($attributeIds))) {
$fail('The definitions must have distinct attribute ids.');
}
}
],
'variants.*.definitions.*.attribute_id' => [
'required',
'integer',
Rule::exists('attribute', 'id')->where(
fn ($query) => $query->where('tenant_codigo', $this->route('tenant')?->codigo)
),
],
'variants.*.definitions.*.value' => ['nullable', 'string'],
];
}
}

View File

@@ -46,7 +46,7 @@ class UpdateProductVariantRequest extends FormRequest
'required',
'integer',
'distinct',
Rule::exists('productos_attributes', 'id')->where(
Rule::exists('attribute', 'id')->where(
fn ($query) => $query->where('tenant_codigo', $this->route('tenant')?->codigo)
),
],