feat(expense-snapshot): REST controller + DI registration
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using ROLAC.API.Authorization;
|
||||
using ROLAC.API.DTOs.Expense;
|
||||
using ROLAC.API.Services;
|
||||
|
||||
namespace ROLAC.API.Controllers;
|
||||
|
||||
// Snapshots are reusable vendor-payment templates — a finance tool. Every action requires
|
||||
// Expenses:Write (super_admin bypasses), matching who can create vendor payments.
|
||||
[ApiController]
|
||||
[Route("api/expense-snapshots")]
|
||||
[Authorize]
|
||||
public class ExpenseSnapshotsController : ControllerBase
|
||||
{
|
||||
private readonly IExpenseSnapshotService _svc;
|
||||
private readonly IPermissionService _perms;
|
||||
public ExpenseSnapshotsController(IExpenseSnapshotService svc, IPermissionService perms)
|
||||
{
|
||||
_svc = svc;
|
||||
_perms = perms;
|
||||
}
|
||||
|
||||
private List<string> Roles() => User.FindAll("role").Select(claim => claim.Value).ToList();
|
||||
private bool IsSuperAdmin() => User.IsInRole(PermissionAuthorizationHandler.SuperAdminRole);
|
||||
private async Task<bool> CanManageAsync() =>
|
||||
IsSuperAdmin() || await _perms.HasPermissionAsync(Roles(), Modules.Expenses, PermissionActions.Write);
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> GetAll()
|
||||
{
|
||||
if (!await CanManageAsync()) return Forbid();
|
||||
return Ok(await _svc.GetAllAsync());
|
||||
}
|
||||
|
||||
[HttpGet("{id:int}")]
|
||||
public async Task<IActionResult> GetById(int id)
|
||||
{
|
||||
if (!await CanManageAsync()) return Forbid();
|
||||
var dto = await _svc.GetByIdAsync(id);
|
||||
return dto is null ? NotFound() : Ok(dto);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> Create([FromBody] CreateExpenseSnapshotRequest r)
|
||||
{
|
||||
if (!await CanManageAsync()) return Forbid();
|
||||
try { return Ok(new { id = await _svc.CreateAsync(r) }); }
|
||||
catch (InvalidOperationException ex) { return Conflict(new { message = ex.Message }); }
|
||||
}
|
||||
|
||||
[HttpPut("{id:int}")]
|
||||
public async Task<IActionResult> Update(int id, [FromBody] UpdateExpenseSnapshotRequest r)
|
||||
{
|
||||
if (!await CanManageAsync()) return Forbid();
|
||||
try { await _svc.UpdateAsync(id, r); return NoContent(); }
|
||||
catch (KeyNotFoundException) { return NotFound(); }
|
||||
catch (InvalidOperationException ex) { return Conflict(new { message = ex.Message }); }
|
||||
}
|
||||
|
||||
[HttpDelete("{id:int}")]
|
||||
public async Task<IActionResult> Delete(int id)
|
||||
{
|
||||
if (!await CanManageAsync()) return Forbid();
|
||||
try { await _svc.DeleteAsync(id); return NoContent(); }
|
||||
catch (KeyNotFoundException) { return NotFound(); }
|
||||
}
|
||||
}
|
||||
@@ -153,6 +153,7 @@ builder.Services.AddScoped<ROLAC.API.Services.Storage.IFileStorage,
|
||||
ROLAC.API.Services.Storage.LocalDiskFileStorage>();
|
||||
builder.Services.AddScoped<IExpenseCategoryService, ExpenseCategoryService>();
|
||||
builder.Services.AddScoped<IExpenseService, ExpenseService>();
|
||||
builder.Services.AddScoped<IExpenseSnapshotService, ExpenseSnapshotService>();
|
||||
builder.Services.AddScoped<IMonthlyStatementService, MonthlyStatementService>();
|
||||
builder.Services.AddScoped<IFinanceDashboardService, FinanceDashboardService>();
|
||||
builder.Services.AddScoped<IForm990ReportService, Form990ReportService>();
|
||||
|
||||
Reference in New Issue
Block a user