diff --git a/backend/app/Exceptions/AlreadyExistsException.php b/backend/app/Exceptions/AlreadyExistsException.php new file mode 100644 index 0000000..02e525f --- /dev/null +++ b/backend/app/Exceptions/AlreadyExistsException.php @@ -0,0 +1,21 @@ +model = $model; + } + + public function render($request) { + return response()->json([ + 'error' => 'already_exists', + 'message' => 'Ya existe la ' . $this->model + ], 400); + } +} diff --git a/backend/app/Exceptions/ModelNotFoundException.php b/backend/app/Exceptions/ModelNotFoundException.php index 8b40465..cace757 100644 --- a/backend/app/Exceptions/ModelNotFoundException.php +++ b/backend/app/Exceptions/ModelNotFoundException.php @@ -11,7 +11,7 @@ class ModelNotFoundException extends Exception { public function __construct($modelName, $id) { $this->modelName= $modelName; - $this->id = $id; + $this->id = is_array($id) ? implode(', ', $id) : $id; } public function render($request) { diff --git a/backend/app/Http/Controllers/ProductosController.php b/backend/app/Http/Controllers/ProductosController.php new file mode 100644 index 0000000..2e677a2 --- /dev/null +++ b/backend/app/Http/Controllers/ProductosController.php @@ -0,0 +1,167 @@ +validOrFail($restaurante_id); + $restaurante = Restaurante::findOrFail($restaurante_id); + + $productos = $restaurante->productos(); + + $paginate = app(PaginatorService::class)->paginate( + perPage: $request->input('per_page', 15), + page: $request->input('page', 1), + total: $productos->count(), + route: 'restaurant.all', + data: ['restaurante_id' => $restaurante_id] + ); + + $data = $productos->get() + ->skip($paginate['from'] - 1) + ->take($paginate['per_page']) + ->all(); + + return response()->json([ + 'pagination' => $paginate, + 'data' => $data + ]); + } + + /** + * Obtiene un producto por su id + */ + public function get(Request $request, $restaurante_id, $id) { + app(UuidService::class)->validOrFail($restaurante_id); + app(UuidService::class)->validOrFail($id); + + $restaurante = Restaurante::findOrFail($restaurante_id); + $producto = Producto::findOrFail($id); + + if($producto->restaurante != $restaurante) { + throw new ModelNotFoundException("producto", $id); + } + + return response()->json($producto); + } + + /** + * Crea un nuevo producto + */ + public function create(Request $request, $restaurante_id) { + $this->validate($request, [ + 'nombre' => 'required', + 'precio_venta' => 'required|numeric', + 'categoria_id' => 'required|exists:categorias,id', + 'zona_produccion_id' => 'required|exists:zonas_produccion,id', + ]); + + app(UuidService::class)->validOrFail($restaurante_id); + app(UuidService::class)->validOrFail($request->input('categoria_id')); + app(UuidService::class)->validOrFail($request->input('zona_produccion_id')); + + $restaurante = Restaurante::findOrFail($restaurante_id); + $categoria = Categoria::findOrFail($request->input('categoria_id')); + $zonaProduccion = ZonaProduccion::findOrFail($request->input('zona_produccion_id')); + + if($categoria->restaurante != $restaurante) { + throw new ModelNotFoundException("categoria", $id); + } + + if($zonaProduccion->restaurante != $restaurante) { + throw new ModelNotFoundException("zona_produccion", $id); + } + + $producto = Producto::create([ + 'id' => Uuid::uuid4(), + 'nombre' => $request->input('nombre'), + 'precio_venta' => $request->input('precio_venta'), + 'categoria_id' => $categoria->id, + 'zona_produccion_id' => $zonaProduccion->id, + 'restaurante_id' => $restaurante->id + ]); + + return response()->json($producto, 201); + } + + /** + * Actualiza un producto + */ + public function update(Request $request, $restaurante_id, $id) { + $this->validate($request, [ + 'nombre' => 'required', + 'precio_venta' => 'required|numeric', + 'categoria_id' => 'required|exists:categorias,id', + 'zona_produccion_id' => 'required|exists:zonas_produccion,id', + ]); + + app(UuidService::class)->validOrFail($restaurante_id); + app(UuidService::class)->validOrFail($request->input('categoria_id')); + app(UuidService::class)->validOrFail($request->input('zona_produccion_id')); + app(UuidService::class)->validOrFail($id); + + $restaurante = Restaurante::findOrFail($restaurante_id); + $categoria = Categoria::findOrFail($request->input('categoria_id')); + $zonaProduccion = ZonaProduccion::findOrFail($request->input('zona_produccion_id')); + $producto = Producto::findOrFail($id); + + if($categoria->restaurante != $restaurante) { + throw new ModelNotFoundException("categoria", $id); + } + + if($zonaProduccion->restaurante != $restaurante) { + throw new ModelNotFoundException("zona_produccion", $id); + } + + if($producto->restaurante != $restaurante) { + throw new ModelNotFoundException("ingrediente", $id); + } + + $producto->nombre = $request->input('nombre'); + $producto->precio_venta = $request->input('precio_venta'); + $producto->categoria_id = $categoria->id; + $producto->zona_produccion_id = $zonaProduccion->id; + + $producto->save(); + + return response()->json($producto); + } + + /** + * Elimina un producto + */ + public function delete(Request $request, $restaurante_id, $id) { + app(UuidService::class)->validOrFail($restaurante_id); + app(UuidService::class)->validOrFail($id); + + $restaurante = Restaurante::findOrFail($restaurante_id); + $producto = Producto::findOrFail($id); + + if($producto->restaurante != $restaurante) { + throw new ModelNotFoundException("producto", $id); + } + + $producto->delete(); + + return response()->json([], 204); + } +} diff --git a/backend/app/Http/Controllers/RecetasController.php b/backend/app/Http/Controllers/RecetasController.php new file mode 100644 index 0000000..0f96cab --- /dev/null +++ b/backend/app/Http/Controllers/RecetasController.php @@ -0,0 +1,188 @@ +validOrFail($restaurante_id); + app(UuidService::class)->validOrFail($producto_id); + + $restaurante = Restaurante::findOrFail($restaurante_id); + $producto = Producto::findOrFail($producto_id); + + if($producto->restaurante != $restaurante) { + throw new ModelNotFoundException("producto", $producto_id); + } + + $recetas = Receta::where('producto_id', $producto->id)->with('ingrediente'); + + $paginate = app(PaginatorService::class)->paginate( + perPage: $request->input('per_page', 15), + page: $request->input('page', 1), + total: $recetas->count(), + route: 'productos.receta.all', + data: ['restaurante_id' => $restaurante_id, 'producto_id' => $producto_id] + ); + + $data = $recetas->get() + ->skip($paginate['from'] - 1) + ->take($paginate['per_page']) + ->all(); + + return response()->json([ + 'pagination' => $paginate, + 'data' => $data + ]); + } + + /** + * Obtiene los datos de un ingrediente de una receta + */ + public function get(Request $request, $restaurante_id, $producto_id, $ingrediente_id) { + app(UuidService::class)->validOrFail($restaurante_id); + app(UuidService::class)->validOrFail($ingrediente_id); + app(UuidService::class)->validOrFail($producto_id); + + $restaurante = Restaurante::findOrFail($restaurante_id); + $ingrediente = Ingrediente::findOrFail($ingrediente_id); + $producto = Producto::findOrFail($producto_id); + + if($ingrediente->restaurante != $restaurante) { + throw new ModelNotFoundException("ingrediente", $ingrediente_id); + } + + if($producto->restaurante != $restaurante) { + throw new ModelNotFoundException("producto", $producto_id); + } + + $receta = Receta::where('producto_id', $producto->id) + ->where('ingrediente_id', $ingrediente->id) + ->with('ingrediente') + ->first(); + if(!$receta) { + throw new ModelNotFoundException("receta", [$ingrediente_id, $producto_id]); + } + + return response()->json($receta); + } + + /** + * Agrega un ingrediente a la receta del producto + */ + public function create(Request $request, $restaurante_id, $producto_id, $ingrediente_id) { + $this->validate($request, [ + 'unidades' => 'required|numeric' + ]); + + app(UuidService::class)->validOrFail($restaurante_id); + app(UuidService::class)->validOrFail($ingrediente_id); + app(UuidService::class)->validOrFail($producto_id); + + $restaurante = Restaurante::findOrFail($restaurante_id); + $ingrediente = Ingrediente::findOrFail($ingrediente_id); + $producto = Producto::findOrFail($producto_id); + + if($ingrediente->restaurante != $restaurante) { + throw new ModelNotFoundException("ingrediente", $ingrediente_id); + } + + if($producto->restaurante != $restaurante) { + throw new ModelNotFoundException("producto", $producto_id); + } + + $receta = Receta::where('producto_id', $producto->id) + ->where('ingrediente_id', $ingrediente->id) + ->first(); + + if($receta) throw new AlreadyExistsException("receta"); + + $receta = Receta::create([ + 'id' => Uuid::uuid4(), + 'unidades' => $request->input('unidades'), + 'producto_id' => $producto->id, + 'ingrediente_id' => $ingrediente->id, + ]); + + return response()->json($receta); + } + + /** + * Modifica el ingrediente de una receta + */ + public function update(Request $request, $restaurante_id, $producto_id, $ingrediente_id) { + $this->validate($request, [ + 'unidades' => 'required|numeric' + ]); + + app(UuidService::class)->validOrFail($restaurante_id); + app(UuidService::class)->validOrFail($ingrediente_id); + app(UuidService::class)->validOrFail($producto_id); + + $restaurante = Restaurante::findOrFail($restaurante_id); + $ingrediente = Ingrediente::findOrFail($ingrediente_id); + $producto = Producto::findOrFail($producto_id); + + if($ingrediente->restaurante != $restaurante) { + throw new ModelNotFoundException("ingrediente", $ingrediente_id); + } + + if($producto->restaurante != $restaurante) { + throw new ModelNotFoundException("producto", $producto_id); + } + + $receta = Receta::where('producto_id', $producto->id) + ->where('ingrediente_id', $ingrediente->id) + ->first(); + + $receta->unidades = $request->input('unidades'); + $receta->save(); + + return response()->json($receta); + } + + /** + * Elimina un ingrediente de la receta del producto + */ + public function delete(Request $request, $restaurante_id, $producto_id, $ingrediente_id) { + app(UuidService::class)->validOrFail($restaurante_id); + app(UuidService::class)->validOrFail($producto_id); + app(UuidService::class)->validOrFail($ingrediente_id); + + $restaurante = Restaurante::findOrFail($restaurante_id); + $producto = Producto::findOrFail($producto_id); + $ingrediente = Ingrediente::findOrFail($ingrediente_id); + + if($producto->restaurante != $restaurante) { + throw new ModelNotFoundException("producto", $producto_id); + } + + if($ingrediente->restaurante != $restaurante) { + throw new ModelNotFoundException("ingrediente", $ingrediente_id); + } + + $receta = Receta::where('producto_id', $producto->id) + ->where('ingrediente_id', $ingrediente->id) + ->first(); + + $receta->delete(); + return response()->json([], 204); + } +} diff --git a/backend/app/Models/Producto.php b/backend/app/Models/Producto.php index ff5c38c..a938c65 100644 --- a/backend/app/Models/Producto.php +++ b/backend/app/Models/Producto.php @@ -11,6 +11,16 @@ class Producto extends Model { use UuidPrimaryKey, SoftDeletes; protected $table = 'productos'; + protected $fillable = [ + 'id', 'nombre', 'precio_venta', 'categoria_id', + 'zona_produccion_id', 'restaurante_id' + ]; + + public static function findOrFail($id) { + $producto = Producto::find($id); + if(!$producto) throw new ModelNotFoundException("producto", $id); + return $producto; + } public function recetas() { return $this->hasMany(Receta::class, 'producto_id'); diff --git a/backend/app/Models/Receta.php b/backend/app/Models/Receta.php index 619024b..f8834e8 100644 --- a/backend/app/Models/Receta.php +++ b/backend/app/Models/Receta.php @@ -10,6 +10,7 @@ class Receta extends Model { use UuidPrimaryKey; protected $table = 'recetas'; + protected $fillable = ['unidades', 'producto_id', 'ingrediente_id']; public function ingrediente() { return $this->belongsTo(Ingrediente::class); diff --git a/backend/app/Models/Restaurante.php b/backend/app/Models/Restaurante.php index 33ce630..d1ca84a 100644 --- a/backend/app/Models/Restaurante.php +++ b/backend/app/Models/Restaurante.php @@ -8,6 +8,7 @@ use App\Models\ZonaProduccion; use App\Models\Categoria; use App\Models\Proveedor; use App\Models\Ingrediente; +use App\Models\Producto; use App\Traits\UuidPrimaryKey; use App\Exceptions\ModelNotFoundException; use Illuminate\Database\Eloquent\Model; @@ -55,4 +56,8 @@ class Restaurante extends Model { public function ingredientes() { return $this->hasMany(Ingrediente::class, 'restaurante_id'); } + + public function productos() { + return $this->hasMany(Producto::class, 'restaurante_id'); + } } diff --git a/backend/routes/web.php b/backend/routes/web.php index f9c06c7..1a27326 100644 --- a/backend/routes/web.php +++ b/backend/routes/web.php @@ -59,5 +59,17 @@ $router->group(['prefix' => 'api/v1', 'middleware' => ['auth', 'log_endpoint']], $router->post( '/{restaurante_id}/ingredientes', ['as' => 'ingredientes.create', 'uses' => 'IngredientesController@create']); $router->put( '/{restaurante_id}/ingredientes/{id}', ['as' => 'ingredientes.update', 'uses' => 'IngredientesController@update']); $router->delete('/{restaurante_id}/ingredientes/{id}', ['as' => 'ingredientes.delete', 'uses' => 'IngredientesController@delete']); + + $router->get( '/{restaurante_id}/productos', ['as' => 'productos.all', 'uses' => 'ProductosController@all']); + $router->get( '/{restaurante_id}/productos/{id}', ['as' => 'productos.get', 'uses' => 'ProductosController@get']); + $router->post( '/{restaurante_id}/productos', ['as' => 'productos.create', 'uses' => 'ProductosController@create']); + $router->put( '/{restaurante_id}/productos/{id}', ['as' => 'productos.update', 'uses' => 'ProductosController@update']); + $router->delete('/{restaurante_id}/productos/{id}', ['as' => 'productos.delete', 'uses' => 'ProductosController@delete']); + + $router->get( '/{restaurante_id}/productos/{producto_id}/ingredientes/', ['as' => 'productos.receta.all', 'uses' => 'RecetasController@all']); + $router->get( '/{restaurante_id}/productos/{producto_id}/ingredientes/{ingrediente_id}', ['as' => 'productos.receta.get', 'uses' => 'RecetasController@get']); + $router->post( '/{restaurante_id}/productos/{producto_id}/ingredientes/{ingrediente_id}', ['as' => 'productos.receta.add_ingrediente', 'uses' => 'RecetasController@create']); + $router->put( '/{restaurante_id}/productos/{producto_id}/ingredientes/{ingrediente_id}', ['as' => 'productos.receta.update_ingrediente', 'uses' => 'RecetasController@update']); + $router->delete('/{restaurante_id}/productos/{producto_id}/ingredientes/{ingrediente_id}', ['as' => 'productos.receta.remove_ingrediente', 'uses' => 'RecetasController@delete']); }); }); diff --git a/database/init/00-schema.sql b/database/init/00-schema.sql index 8b5746c..da44e72 100644 --- a/database/init/00-schema.sql +++ b/database/init/00-schema.sql @@ -133,6 +133,7 @@ create table productos ( ); create table recetas ( + id uuid primary key default gen_random_uuid(), producto_id uuid references productos, ingrediente_id uuid references ingredientes, unidades numeric not null, diff --git a/database/modifications/00-recetas-id.sql b/database/modifications/00-recetas-id.sql new file mode 100644 index 0000000..208129f --- /dev/null +++ b/database/modifications/00-recetas-id.sql @@ -0,0 +1,2 @@ +alter table recetas drop constraint recetas_pkey; +alter table recetas add column id uuid primary key default gen_random_uuid();