152 lines
6.7 KiB
C#
152 lines
6.7 KiB
C#
|
|
using Microsoft.AspNetCore.Authorization;
|
||
|
|
using Microsoft.AspNetCore.Mvc;
|
||
|
|
using SIGCM2.Api.Authorization;
|
||
|
|
using SIGCM2.Application.Abstractions;
|
||
|
|
using SIGCM2.Application.Rubros.Create;
|
||
|
|
using SIGCM2.Application.Rubros.Deactivate;
|
||
|
|
using SIGCM2.Application.Rubros.Dtos;
|
||
|
|
using SIGCM2.Application.Rubros.GetById;
|
||
|
|
using SIGCM2.Application.Rubros.GetTree;
|
||
|
|
using SIGCM2.Application.Rubros.Move;
|
||
|
|
using SIGCM2.Application.Rubros.Update;
|
||
|
|
|
||
|
|
namespace SIGCM2.Api.Controllers;
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// CAT-001: Rubro N-ary tree management.
|
||
|
|
/// Read endpoints at /api/v1/rubros — require authentication (any role).
|
||
|
|
/// Write endpoints at /api/v1/admin/rubros — require 'catalogo:rubros:gestionar'.
|
||
|
|
/// </summary>
|
||
|
|
[ApiController]
|
||
|
|
public sealed class RubrosController : ControllerBase
|
||
|
|
{
|
||
|
|
private readonly IDispatcher _dispatcher;
|
||
|
|
|
||
|
|
public RubrosController(IDispatcher dispatcher)
|
||
|
|
{
|
||
|
|
_dispatcher = dispatcher;
|
||
|
|
}
|
||
|
|
|
||
|
|
// ── READ endpoints ─────────────────────────────────────────────────────────
|
||
|
|
|
||
|
|
/// <summary>Returns the full Rubro tree. Requires authentication.</summary>
|
||
|
|
[HttpGet("api/v1/rubros/tree")]
|
||
|
|
[Authorize]
|
||
|
|
[ProducesResponseType(typeof(IReadOnlyList<RubroTreeNodeDto>), StatusCodes.Status200OK)]
|
||
|
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||
|
|
public async Task<IActionResult> GetRubroTree([FromQuery] bool incluirInactivos = false)
|
||
|
|
{
|
||
|
|
var query = new GetRubroTreeQuery(incluirInactivos);
|
||
|
|
var result = await _dispatcher.Send<GetRubroTreeQuery, IReadOnlyList<RubroTreeNodeDto>>(query);
|
||
|
|
return Ok(result);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>Returns a single Rubro by id. Requires authentication.</summary>
|
||
|
|
[HttpGet("api/v1/rubros/{id:int}")]
|
||
|
|
[Authorize]
|
||
|
|
[ProducesResponseType(typeof(RubroDetailDto), StatusCodes.Status200OK)]
|
||
|
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||
|
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||
|
|
public async Task<IActionResult> GetRubroById([FromRoute] int id)
|
||
|
|
{
|
||
|
|
var query = new GetRubroByIdQuery(id);
|
||
|
|
var result = await _dispatcher.Send<GetRubroByIdQuery, RubroDetailDto>(query);
|
||
|
|
return Ok(result);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ── WRITE endpoints ────────────────────────────────────────────────────────
|
||
|
|
|
||
|
|
/// <summary>Creates a new Rubro. Requires catalogo:rubros:gestionar.</summary>
|
||
|
|
[HttpPost("api/v1/admin/rubros")]
|
||
|
|
[RequirePermission("catalogo:rubros:gestionar")]
|
||
|
|
[ProducesResponseType(typeof(RubroCreatedDto), StatusCodes.Status201Created)]
|
||
|
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||
|
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||
|
|
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||
|
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||
|
|
[ProducesResponseType(StatusCodes.Status409Conflict)]
|
||
|
|
[ProducesResponseType(StatusCodes.Status422UnprocessableEntity)]
|
||
|
|
public async Task<IActionResult> CreateRubro([FromBody] CreateRubroRequest request)
|
||
|
|
{
|
||
|
|
var command = new CreateRubroCommand(
|
||
|
|
Nombre: request.Nombre ?? string.Empty,
|
||
|
|
ParentId: request.ParentId,
|
||
|
|
TarifarioBaseId: request.TarifarioBaseId);
|
||
|
|
|
||
|
|
var result = await _dispatcher.Send<CreateRubroCommand, RubroCreatedDto>(command);
|
||
|
|
return CreatedAtAction(nameof(GetRubroById), new { id = result.Id }, result);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>Updates a Rubro's nombre. Requires catalogo:rubros:gestionar.</summary>
|
||
|
|
[HttpPut("api/v1/admin/rubros/{id:int}")]
|
||
|
|
[RequirePermission("catalogo:rubros:gestionar")]
|
||
|
|
[ProducesResponseType(typeof(RubroUpdatedDto), StatusCodes.Status200OK)]
|
||
|
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||
|
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||
|
|
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||
|
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||
|
|
[ProducesResponseType(StatusCodes.Status409Conflict)]
|
||
|
|
public async Task<IActionResult> UpdateRubro([FromRoute] int id, [FromBody] UpdateRubroRequest request)
|
||
|
|
{
|
||
|
|
var command = new UpdateRubroCommand(
|
||
|
|
Id: id,
|
||
|
|
Nombre: request.Nombre ?? string.Empty);
|
||
|
|
|
||
|
|
var result = await _dispatcher.Send<UpdateRubroCommand, RubroUpdatedDto>(command);
|
||
|
|
return Ok(result);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>Soft-deletes (deactivates) a Rubro. Requires catalogo:rubros:gestionar.</summary>
|
||
|
|
[HttpDelete("api/v1/admin/rubros/{id:int}")]
|
||
|
|
[RequirePermission("catalogo:rubros:gestionar")]
|
||
|
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||
|
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||
|
|
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||
|
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||
|
|
[ProducesResponseType(StatusCodes.Status409Conflict)]
|
||
|
|
public async Task<IActionResult> DeactivateRubro([FromRoute] int id)
|
||
|
|
{
|
||
|
|
var command = new DeactivateRubroCommand(id);
|
||
|
|
await _dispatcher.Send<DeactivateRubroCommand, RubroStatusDto>(command);
|
||
|
|
return NoContent();
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>Moves a Rubro to a new parent. Requires catalogo:rubros:gestionar.</summary>
|
||
|
|
[HttpPatch("api/v1/admin/rubros/{id:int}/mover")]
|
||
|
|
[RequirePermission("catalogo:rubros:gestionar")]
|
||
|
|
[ProducesResponseType(typeof(RubroMovedDto), StatusCodes.Status200OK)]
|
||
|
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||
|
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||
|
|
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||
|
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||
|
|
[ProducesResponseType(StatusCodes.Status409Conflict)]
|
||
|
|
[ProducesResponseType(StatusCodes.Status422UnprocessableEntity)]
|
||
|
|
public async Task<IActionResult> MoveRubro([FromRoute] int id, [FromBody] MoveRubroRequest request)
|
||
|
|
{
|
||
|
|
var command = new MoveRubroCommand(
|
||
|
|
Id: id,
|
||
|
|
NuevoParentId: request.NuevoParentId,
|
||
|
|
NuevoOrden: request.NuevoOrden);
|
||
|
|
|
||
|
|
var result = await _dispatcher.Send<MoveRubroCommand, RubroMovedDto>(command);
|
||
|
|
return Ok(result);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// ── Request body records ──────────────────────────────────────────────────────
|
||
|
|
|
||
|
|
/// <summary>CAT-001: Create rubro request body.</summary>
|
||
|
|
public sealed record CreateRubroRequest(
|
||
|
|
string? Nombre,
|
||
|
|
int? ParentId,
|
||
|
|
int? TarifarioBaseId);
|
||
|
|
|
||
|
|
/// <summary>CAT-001: Update rubro request body.</summary>
|
||
|
|
public sealed record UpdateRubroRequest(
|
||
|
|
string? Nombre);
|
||
|
|
|
||
|
|
/// <summary>CAT-001: Move rubro request body.</summary>
|
||
|
|
public sealed record MoveRubroRequest(
|
||
|
|
int? NuevoParentId,
|
||
|
|
int NuevoOrden);
|