auth layer implemented

This commit is contained in:
Kalana B. Thilakarathna
2026-10-09 09:58:45 +05:30
parent 5b2ac8cc14
commit 558afc6f9d
22 changed files with 4731 additions and 82 deletions
@@ -1,4 +1,5 @@
using Distribution.Infra.Authorization;
using Distribution.Infra.Authorization;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.Claims;
@@ -7,10 +8,12 @@ namespace Distribution.Features.Claims;
public class ClaimController(ClaimService service, ICurrentUserService currentUser) : ControllerBase
{
[HttpGet("api/claims")]
[Authorize("claim.view")]
public async Task<IActionResult> GetAll() =>
Ok(await service.GetAllAsync());
[HttpGet("api/claims/{id:guid}")]
[Authorize("claim.view")]
public async Task<IActionResult> GetById(Guid id)
{
var result = await service.GetByIdAsync(id);
@@ -18,6 +21,7 @@ public class ClaimController(ClaimService service, ICurrentUserService currentUs
}
[HttpPost("api/claims")]
[Authorize("claim.manage")]
public async Task<IActionResult> Create(CreateClaimRequest request, CancellationToken ct)
{
try
@@ -29,6 +33,7 @@ public class ClaimController(ClaimService service, ICurrentUserService currentUs
}
[HttpPut("api/claims/{id:guid}")]
[Authorize("claim.manage")]
public async Task<IActionResult> Update(Guid id, UpdateClaimRequest request, CancellationToken ct)
{
try
@@ -40,6 +45,7 @@ public class ClaimController(ClaimService service, ICurrentUserService currentUs
}
[HttpPost("api/claims/{id:guid}/settle")]
[Authorize("claim.manage")]
public async Task<IActionResult> Settle(Guid id, SettleClaimRequest request, CancellationToken ct)
{
var userId = currentUser.UserId ?? request.SettledByUserId;
@@ -1,4 +1,5 @@
using Distribution.Models;
using Distribution.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.DeliveryRide;
@@ -8,6 +9,7 @@ namespace Distribution.Features.DeliveryRide;
public class DeliveryTaskController(DeliveryTaskService service) : ControllerBase
{
[HttpPost("rides/{rideId:guid}/tasks/generate")]
[Authorize("delivery-task.manage")]
public async Task<IActionResult> Generate(Guid rideId, CancellationToken ct)
{
try
@@ -21,6 +23,7 @@ public class DeliveryTaskController(DeliveryTaskService service) : ControllerBas
[HttpGet("rides/{rideId:guid}/tasks")]
[HttpGet("rides/{rideId:guid}/delivery-tasks")]
[Authorize("delivery-task.view")]
public async Task<IActionResult> GetByRide(Guid rideId)
{
var result = await service.GetByRideIdAsync(rideId);
@@ -28,6 +31,7 @@ public class DeliveryTaskController(DeliveryTaskService service) : ControllerBas
}
[HttpGet("delivery-tasks")]
[Authorize("delivery-task.view")]
public async Task<IActionResult> GetAll(
[FromQuery] DeliveryTaskStatus? status,
[FromQuery] int page = 1,
@@ -38,6 +42,7 @@ public class DeliveryTaskController(DeliveryTaskService service) : ControllerBas
}
[HttpGet("delivery-tasks/{id:guid}")]
[Authorize("delivery-task.view")]
public async Task<IActionResult> GetById(Guid id)
{
var result = await service.GetByIdAsync(id);
@@ -45,6 +50,7 @@ public class DeliveryTaskController(DeliveryTaskService service) : ControllerBas
}
[HttpPut("delivery-tasks/{id:guid}/quantities")]
[Authorize("delivery-task.manage")]
public async Task<IActionResult> SetQuantities(Guid id, SetQuantitiesRequest request, CancellationToken ct)
{
try
@@ -57,6 +63,7 @@ public class DeliveryTaskController(DeliveryTaskService service) : ControllerBas
}
[HttpPost("delivery-tasks/{id:guid}/confirm")]
[Authorize("delivery-task.manage")]
public async Task<IActionResult> Confirm(Guid id, CancellationToken ct)
{
try
@@ -69,6 +76,7 @@ public class DeliveryTaskController(DeliveryTaskService service) : ControllerBas
}
[HttpPost("delivery-tasks/{id:guid}/fail")]
[Authorize("delivery-task.manage")]
public async Task<IActionResult> Fail(Guid id, FailTaskRequest request, CancellationToken ct)
{
try
@@ -1,5 +1,6 @@
using Distribution.Features.DriverComplianceConfig;
using Distribution.Features.DriverComplianceConfig;
using Distribution.Infra.Authorization;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.DriverProfile;
@@ -12,6 +13,7 @@ public class DriverProfileController(
ICurrentUserService currentUser) : ControllerBase
{
[HttpGet]
[Authorize("driver.view")]
public async Task<IActionResult> GetAll([FromQuery] int page = 1, [FromQuery] int pageSize = 20)
{
var result = await service.GetAllAsync(page, pageSize);
@@ -19,6 +21,7 @@ public class DriverProfileController(
}
[HttpGet("{id:guid}")]
[Authorize("driver.view")]
public async Task<IActionResult> GetById(Guid id)
{
var result = await service.GetByIdAsync(id);
@@ -26,6 +29,7 @@ public class DriverProfileController(
}
[HttpPost]
[Authorize("driver.manage")]
public async Task<IActionResult> Create(CreateDriverProfileRequest request, CancellationToken ct)
{
try
@@ -44,6 +48,7 @@ public class DriverProfileController(
}
[HttpPut("{id:guid}")]
[Authorize("driver.manage")]
public async Task<IActionResult> Update(Guid id, UpdateDriverProfileRequest request, CancellationToken ct)
{
var result = await service.UpdateAsync(id, request, ct);
@@ -51,6 +56,7 @@ public class DriverProfileController(
}
[HttpDelete("{id:guid}")]
[Authorize("driver.manage")]
public async Task<IActionResult> Deactivate(Guid id, CancellationToken ct)
{
var success = await service.DeactivateAsync(id, ct);
@@ -58,6 +64,7 @@ public class DriverProfileController(
}
[HttpGet("compliance")]
[Authorize("driver.view")]
public async Task<IActionResult> GetCompliance()
{
var result = await complianceService.GetAsync();
@@ -65,6 +72,7 @@ public class DriverProfileController(
}
[HttpPut("compliance")]
[Authorize("driver.manage")]
public async Task<IActionResult> UpsertCompliance(UpdateDriverComplianceConfigRequest request, CancellationToken ct)
{
var userId = currentUser.UserId ?? throw new UnauthorizedAccessException();
+8 -1
View File
@@ -1,4 +1,5 @@
using Distribution.Infra.Authorization;
using Distribution.Infra.Authorization;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.Grn;
@@ -7,6 +8,7 @@ namespace Distribution.Features.Grn;
public class GrnController(GrnService service, ICurrentUserService currentUser) : ControllerBase
{
[HttpGet("api/goods-receipts")]
[Authorize("grn.view")]
public async Task<IActionResult> GetAll(
[FromQuery] Guid? purchaseOrderId,
[FromQuery] int page = 1,
@@ -17,6 +19,7 @@ public class GrnController(GrnService service, ICurrentUserService currentUser)
}
[HttpGet("api/goods-receipts/{id:guid}")]
[Authorize("grn.view")]
public async Task<IActionResult> GetById(Guid id)
{
var result = await service.GetByIdAsync(id);
@@ -24,6 +27,7 @@ public class GrnController(GrnService service, ICurrentUserService currentUser)
}
[HttpPost("api/goods-receipts")]
[Authorize("grn.manage")]
public async Task<IActionResult> CreateFlat(CreateFlatGrnRequest request, CancellationToken ct)
{
// TEMPORARY: currentUser.UserId is always unset due to the broken auth middleware
@@ -42,6 +46,7 @@ public class GrnController(GrnService service, ICurrentUserService currentUser)
/// <summary>Goods received without a purchase order.</summary>
[HttpPost("api/goods-receipts/direct")]
[Authorize("grn.manage")]
public async Task<IActionResult> CreateDirect(CreateDirectGrnRequest request, CancellationToken ct)
{
var userId = currentUser.UserId ?? request.ReceivedByUserId;
@@ -55,10 +60,12 @@ public class GrnController(GrnService service, ICurrentUserService currentUser)
}
[HttpGet("api/purchase-orders/{id:guid}/goods-receipts")]
[Authorize("grn.view")]
public async Task<IActionResult> GetByPurchaseOrder(Guid id) =>
Ok(await service.GetByPurchaseOrderIdAsync(id));
[HttpPost("api/purchase-orders/{id:guid}/goods-receipts")]
[Authorize("grn.manage")]
public async Task<IActionResult> Create(Guid id, CreateGrnRequest request, CancellationToken ct)
{
var userId = currentUser.UserId ?? request.ReceivedByUserId;
@@ -1,14 +1,19 @@
using Distribution.Models;
using Distribution.Infra.Authorization;
using Distribution.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.LoadingEvent;
[ApiController]
[Route("api/loading-sheets")]
public class LoadingSheetController(LoadingSheetService service, ICurrentUserService currentUser) : ControllerBase
public class LoadingSheetController(
LoadingSheetService service,
IScopeAccessService scopeAccess,
ICurrentUserService currentUser) : ControllerBase
{
[HttpGet]
[Authorize("loading-sheet.view")]
public async Task<IActionResult> GetAll(
[FromQuery] Guid? rideId,
[FromQuery] LoadingSheetStatus? status,
@@ -17,11 +22,26 @@ public class LoadingSheetController(LoadingSheetService service, ICurrentUserSer
[FromQuery] int page = 1,
[FromQuery] int pageSize = 20)
{
if (routeId.HasValue)
{
var userId = currentUser.UserId;
if (userId.HasValue && !await scopeAccess.HasRouteScopeAsync(userId.Value, routeId.Value))
return StatusCode(403);
}
if (regionId.HasValue)
{
var userId = currentUser.UserId;
if (userId.HasValue && !await scopeAccess.HasRegionScopeAsync(userId.Value, regionId.Value))
return StatusCode(403);
}
var result = await service.GetAllAsync(rideId, status, routeId, regionId, page, pageSize);
return Ok(result);
}
[HttpGet("{id:guid}")]
[Authorize("loading-sheet.view")]
public async Task<IActionResult> GetById(Guid id)
{
var result = await service.GetByIdAsync(id);
@@ -29,6 +49,7 @@ public class LoadingSheetController(LoadingSheetService service, ICurrentUserSer
}
[HttpPost]
[Authorize("loading-sheet.manage")]
public async Task<IActionResult> Create(CreateLoadingSheetRequest request, CancellationToken ct)
{
// TEMPORARY: currentUser.UserId is always unset due to the broken auth middleware
@@ -45,6 +66,7 @@ public class LoadingSheetController(LoadingSheetService service, ICurrentUserSer
}
[HttpPut("{id:guid}/items")]
[Authorize("loading-sheet.manage")]
public async Task<IActionResult> SetItems(Guid id, SetItemsRequest request, CancellationToken ct)
{
try
@@ -57,6 +79,7 @@ public class LoadingSheetController(LoadingSheetService service, ICurrentUserSer
}
[HttpPost("{id:guid}/confirm")]
[Authorize("loading-sheet.manage")]
public async Task<IActionResult> Confirm(Guid id, ConfirmLoadingSheetRequest request, CancellationToken ct)
{
// TEMPORARY: currentUser.UserId is always unset due to the broken auth middleware
@@ -1,16 +1,31 @@
using Microsoft.AspNetCore.Mvc;
using Distribution.Infra.Authorization;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.PriceList;
[ApiController]
[Route("api/price-lists")]
public class PriceListController(PriceListService service) : ControllerBase
public class PriceListController(
PriceListService service,
IScopeAccessService scopeAccess,
ICurrentUserService currentUser) : ControllerBase
{
[HttpGet]
public async Task<IActionResult> GetAll([FromQuery] Guid? regionId) =>
Ok(await service.GetAllAsync(regionId));
[Authorize("price-list.view")]
public async Task<IActionResult> GetAll([FromQuery] Guid? regionId)
{
if (regionId.HasValue)
{
var userId = currentUser.UserId;
if (userId.HasValue && !await scopeAccess.HasRegionScopeAsync(userId.Value, regionId.Value))
return StatusCode(403);
}
return Ok(await service.GetAllAsync(regionId));
}
[HttpGet("resolve")]
[Authorize("price-list.view")]
public async Task<IActionResult> Resolve([FromQuery] Guid itemId, [FromQuery] Guid storeId, [FromQuery] DateTime saleDate)
{
var result = await service.ResolveByStoreAsync(itemId, storeId, saleDate);
@@ -18,6 +33,7 @@ public class PriceListController(PriceListService service) : ControllerBase
}
[HttpGet("{id:guid}")]
[Authorize("price-list.view")]
public async Task<IActionResult> GetById(Guid id)
{
var result = await service.GetByIdAsync(id);
@@ -25,6 +41,7 @@ public class PriceListController(PriceListService service) : ControllerBase
}
[HttpPost]
[Authorize("price-list.manage")]
public async Task<IActionResult> Create(CreatePriceListRequest request, CancellationToken ct)
{
var result = await service.CreateAsync(request, ct);
@@ -32,6 +49,7 @@ public class PriceListController(PriceListService service) : ControllerBase
}
[HttpPut("{id:guid}")]
[Authorize("price-list.manage")]
public async Task<IActionResult> Update(Guid id, UpdatePriceListRequest request, CancellationToken ct)
{
try
@@ -46,6 +64,7 @@ public class PriceListController(PriceListService service) : ControllerBase
}
[HttpDelete("{id:guid}")]
[Authorize("price-list.manage")]
public async Task<IActionResult> Delete(Guid id, CancellationToken ct)
{
var deleted = await service.DeleteAsync(id, ct);
@@ -53,6 +72,7 @@ public class PriceListController(PriceListService service) : ControllerBase
}
[HttpPut("{id:guid}/items/{sku}")]
[Authorize("price-list.manage")]
public async Task<IActionResult> UpsertItem(Guid id, string sku, UpsertPriceListItemRequest request, CancellationToken ct)
{
try
@@ -67,6 +87,7 @@ public class PriceListController(PriceListService service) : ControllerBase
}
[HttpDelete("{id:guid}/items/{sku}")]
[Authorize("price-list.manage")]
public async Task<IActionResult> RemoveItem(Guid id, string sku, CancellationToken ct)
{
try
@@ -1,5 +1,6 @@
using Distribution.Infra.Authorization;
using Distribution.Infra.Authorization;
using Distribution.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.Po;
@@ -9,10 +10,12 @@ namespace Distribution.Features.Po;
public class PoController(PoService service, ICurrentUserService currentUser) : ControllerBase
{
[HttpGet]
[Authorize("purchase-order.view")]
public async Task<IActionResult> GetAll([FromQuery] Guid? supplierId, [FromQuery] PurchaseOrderStatus? status) =>
Ok(await service.GetAllAsync(supplierId, status));
[HttpGet("{id:guid}")]
[Authorize("purchase-order.view")]
public async Task<IActionResult> GetById(Guid id)
{
var result = await service.GetByIdAsync(id);
@@ -20,6 +23,7 @@ public class PoController(PoService service, ICurrentUserService currentUser) :
}
[HttpPost]
[Authorize("purchase-order.manage")]
public async Task<IActionResult> Create(CreatePoRequest request, CancellationToken ct)
{
// TEMPORARY: currentUser.UserId is always unset due to the broken auth middleware
@@ -35,6 +39,7 @@ public class PoController(PoService service, ICurrentUserService currentUser) :
}
[HttpPut("{id:guid}")]
[Authorize("purchase-order.manage")]
public async Task<IActionResult> Update(Guid id, UpdatePoRequest request, CancellationToken ct)
{
try
@@ -46,6 +51,7 @@ public class PoController(PoService service, ICurrentUserService currentUser) :
}
[HttpPost("{id:guid}/close")]
[Authorize("purchase-order.manage")]
public async Task<IActionResult> Close(Guid id, CancellationToken ct)
{
try
@@ -57,6 +63,7 @@ public class PoController(PoService service, ICurrentUserService currentUser) :
}
[HttpPost("{id:guid}/cancel")]
[Authorize("purchase-order.manage")]
public async Task<IActionResult> Cancel(Guid id, CancelPoRequest request, CancellationToken ct)
{
try
@@ -68,6 +75,7 @@ public class PoController(PoService service, ICurrentUserService currentUser) :
}
[HttpPost("{id:guid}/items/{itemId:guid}/cancel")]
[Authorize("purchase-order.manage")]
public async Task<IActionResult> CancelLine(Guid id, Guid itemId, CancelPoLineRequest request, CancellationToken ct)
{
try
@@ -79,6 +87,7 @@ public class PoController(PoService service, ICurrentUserService currentUser) :
}
[HttpPost("{id:guid}/match-invoice")]
[Authorize("purchase-order.view")]
public async Task<IActionResult> MatchInvoice(Guid id, [FromQuery] Guid? invoiceId, [FromQuery] Guid? grnId)
{
var result = await service.MatchInvoiceToPoAsync(id, invoiceId, grnId);
@@ -86,6 +95,7 @@ public class PoController(PoService service, ICurrentUserService currentUser) :
}
[HttpPost("{id:guid}/resolve-variance")]
[Authorize("purchase-order.manage")]
public async Task<IActionResult> ResolveVariance(Guid id, ResolveInvoiceVarianceRequest request, CancellationToken ct)
{
try
@@ -1,4 +1,5 @@
using Distribution.Infra.Authorization;
using Distribution.Infra.Authorization;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.PurchaseReturn;
@@ -7,6 +8,7 @@ namespace Distribution.Features.PurchaseReturn;
public class PurchaseReturnController(PurchaseReturnService service, ICurrentUserService currentUser) : ControllerBase
{
[HttpGet("api/purchase-returns/{id:guid}")]
[Authorize("purchase-order.view")]
public async Task<IActionResult> GetById(Guid id)
{
var result = await service.GetByIdAsync(id);
@@ -14,10 +16,12 @@ public class PurchaseReturnController(PurchaseReturnService service, ICurrentUse
}
[HttpGet("api/purchase-orders/{id:guid}/returns")]
[Authorize("purchase-order.view")]
public async Task<IActionResult> GetByPurchaseOrder(Guid id) =>
Ok(await service.GetByPurchaseOrderIdAsync(id));
[HttpPost("api/purchase-orders/{id:guid}/returns")]
[Authorize("purchase-return.manage")]
public async Task<IActionResult> Create(Guid id, CreatePurchaseReturnRequest request, CancellationToken ct)
{
// TEMPORARY: currentUser.UserId is always unset due to the broken auth middleware
@@ -1,28 +1,39 @@
using Microsoft.AspNetCore.Mvc;
using Distribution.Infra.Authorization;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.Region;
[ApiController]
[Route("api/regions")]
public class RegionController(RegionService service) : ControllerBase
public class RegionController(
RegionService service,
IScopeAccessService scopeAccess,
ICurrentUserService currentUser) : ControllerBase
{
[HttpGet]
[Authorize("region.view")]
public async Task<IActionResult> GetAll() =>
Ok(await service.GetAllAsync());
// Suggested code for the Add Region form; the user may override it.
[HttpGet("next-code")]
[Authorize("region.view")]
public async Task<IActionResult> GetNextCode() =>
Ok(await service.GetNextCodeAsync());
[HttpGet("{id:guid}")]
[Authorize("region.view")]
public async Task<IActionResult> GetById(Guid id)
{
var userId = currentUser.UserId;
if (userId.HasValue && !await scopeAccess.HasRegionScopeAsync(userId.Value, id))
return StatusCode(403);
var result = await service.GetByIdAsync(id);
return result is null ? NotFound() : Ok(result);
}
[HttpPost]
[Authorize("region.manage")]
public async Task<IActionResult> Create(CreateRegionRequest request, CancellationToken ct)
{
var result = await service.CreateAsync(request, ct);
@@ -30,6 +41,7 @@ public class RegionController(RegionService service) : ControllerBase
}
[HttpPut("{id:guid}")]
[Authorize("region.manage")]
public async Task<IActionResult> Update(Guid id, UpdateRegionRequest request, CancellationToken ct)
{
var result = await service.UpdateAsync(id, request, ct);
@@ -37,6 +49,7 @@ public class RegionController(RegionService service) : ControllerBase
}
[HttpDelete("{id:guid}")]
[Authorize("region.manage")]
public async Task<IActionResult> Delete(Guid id, CancellationToken ct)
{
var deleted = await service.DeleteAsync(id, ct);
@@ -44,6 +57,7 @@ public class RegionController(RegionService service) : ControllerBase
}
[HttpGet("{id:guid}/manager")]
[Authorize("region.view")]
public async Task<IActionResult> GetManager(Guid id)
{
var result = await service.GetManagerAsync(id);
@@ -51,6 +65,7 @@ public class RegionController(RegionService service) : ControllerBase
}
[HttpPut("{id:guid}/manager")]
[Authorize("region.manage")]
public async Task<IActionResult> SetManager(Guid id, SetRegionManagerRequest request, CancellationToken ct)
{
try
@@ -65,14 +80,15 @@ public class RegionController(RegionService service) : ControllerBase
}
[HttpDelete("{id:guid}/manager")]
[Authorize("region.manage")]
public async Task<IActionResult> RemoveManager(Guid id, CancellationToken ct)
{
var removed = await service.RemoveManagerAsync(id, ct);
return removed ? NoContent() : NotFound();
}
//targets/budgets/agencies/tax-rule.
[HttpPut("{id:guid}/manage")]
[Authorize("region.manage")]
public async Task<IActionResult> Manage(Guid id, ManageRegionRequest request, CancellationToken ct)
{
var result = await service.ManageAsync(id, request, ct);
@@ -80,6 +96,7 @@ public class RegionController(RegionService service) : ControllerBase
}
[HttpGet("{id:guid}/hierarchy")]
[Authorize("region.view")]
public async Task<IActionResult> GetHierarchy(Guid id)
{
var result = await service.GetHierarchyAsync(id);
@@ -87,18 +104,20 @@ public class RegionController(RegionService service) : ControllerBase
}
[HttpGet("{id:guid}/dashboard")]
[Authorize("region.view")]
public async Task<IActionResult> GetDashboard(Guid id, [FromQuery] string period)
{
var result = await service.GetDashboardAsync(id, period);
return result is null ? NotFound() : Ok(result);
}
// Flat CRUD-list view over targets/budgets/tax-rules/agency-maps across all regions.
[HttpGet("manage-entries")]
[Authorize("region.view")]
public async Task<IActionResult> GetManageEntries() =>
Ok(await service.GetManageEntriesAsync());
[HttpDelete("manage-entries/{entryType}/{id:guid}")]
[Authorize("region.manage")]
public async Task<IActionResult> DeleteManageEntry(RegionManageEntryType entryType, Guid id, CancellationToken ct)
{
var deleted = await service.DeleteManageEntryAsync(entryType, id, ct);
@@ -1,4 +1,5 @@
using Distribution.Infra.Authorization;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.Returns;
@@ -7,10 +8,12 @@ namespace Distribution.Features.Returns;
public class ReturnController(ReturnService service, ICurrentUserService currentUser) : ControllerBase
{
[HttpGet("api/returns")]
[Authorize("return.view")]
public async Task<IActionResult> GetAll() =>
Ok(await service.GetAllAsync());
[HttpGet("api/returns/{id:guid}")]
[Authorize("return.view")]
public async Task<IActionResult> GetById(Guid id)
{
var result = await service.GetByIdAsync(id);
@@ -18,6 +21,7 @@ public class ReturnController(ReturnService service, ICurrentUserService current
}
[HttpPatch("api/returns/{id:guid}/reasons")]
[Authorize("return.manage")]
public async Task<IActionResult> UpdateReasons(Guid id, UpdateSaleReturnReasonsRequest request, CancellationToken ct)
{
try
@@ -29,10 +33,12 @@ public class ReturnController(ReturnService service, ICurrentUserService current
}
[HttpGet("api/shop-pos/{id:guid}/returns")]
[Authorize("return.view")]
public async Task<IActionResult> GetByShopPO(Guid id) =>
Ok(await service.GetByShopPOIdAsync(id));
[HttpPost("api/shop-pos/{id:guid}/returns")]
[Authorize("return.manage")]
public async Task<IActionResult> Create(Guid id, CreateSaleReturnRequest request, CancellationToken ct)
{
var userId = currentUser.UserId ?? request.CreatedByUserId;
+21 -2
View File
@@ -1,23 +1,34 @@
using Microsoft.AspNetCore.Mvc;
using Distribution.Infra.Authorization;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.Route;
[ApiController]
[Route("api/routes")]
public class RouteController(RouteService service) : ControllerBase
public class RouteController(
RouteService service,
IScopeAccessService scopeAccess,
ICurrentUserService currentUser) : ControllerBase
{
[HttpGet]
[Authorize("route.view")]
public async Task<IActionResult> GetAll() =>
Ok(await service.GetAllAsync());
[HttpGet("{id:guid}")]
[Authorize("route.view")]
public async Task<IActionResult> GetById(Guid id)
{
var userId = currentUser.UserId;
if (userId.HasValue && !await scopeAccess.HasRouteScopeAsync(userId.Value, id))
return StatusCode(403);
var result = await service.GetByIdAsync(id);
return result is null ? NotFound() : Ok(result);
}
[HttpPost]
[Authorize("route.manage")]
public async Task<IActionResult> Create(CreateRouteRequest request, CancellationToken ct)
{
var result = await service.CreateAsync(request, ct);
@@ -25,6 +36,7 @@ public class RouteController(RouteService service) : ControllerBase
}
[HttpPut("{id:guid}")]
[Authorize("route.manage")]
public async Task<IActionResult> Update(Guid id, UpdateRouteRequest request, CancellationToken ct)
{
var result = await service.UpdateAsync(id, request, ct);
@@ -32,6 +44,7 @@ public class RouteController(RouteService service) : ControllerBase
}
[HttpPut("{id:guid}/region")]
[Authorize("route.manage")]
public async Task<IActionResult> AssignToRegion(Guid id, AssignRouteToRegionRequest request, CancellationToken ct)
{
var result = await service.AssignToRegionAsync(id, request, ct);
@@ -39,6 +52,7 @@ public class RouteController(RouteService service) : ControllerBase
}
[HttpDelete("{id:guid}")]
[Authorize("route.manage")]
public async Task<IActionResult> Delete(Guid id, CancellationToken ct)
{
var deleted = await service.DeleteAsync(id, ct);
@@ -46,6 +60,7 @@ public class RouteController(RouteService service) : ControllerBase
}
[HttpPost("{id:guid}/stores")]
[Authorize("route.manage")]
public async Task<IActionResult> AssignStores(Guid id, AssignStoresToRouteRequest request, CancellationToken ct)
{
var result = await service.AssignStoresToRouteAsync(id, request, ct);
@@ -53,6 +68,7 @@ public class RouteController(RouteService service) : ControllerBase
}
[HttpPut("{id:guid}/owner")]
[Authorize("route.manage")]
public async Task<IActionResult> AssignOwner(Guid id, AssignRouteOwnerRequest request, CancellationToken ct)
{
var result = await service.AssignOwnerAsync(id, request, ct);
@@ -60,10 +76,12 @@ public class RouteController(RouteService service) : ControllerBase
}
[HttpPost("{id:guid}/optimize")]
[Authorize("route.manage")]
public IActionResult OptimizeSequence(Guid id) =>
StatusCode(StatusCodes.Status501NotImplemented);
[HttpPost("{id:guid}/visits/check-in")]
[Authorize("route.manage")]
public async Task<IActionResult> CheckIn(Guid id, RecordCheckInRequest request, CancellationToken ct)
{
try
@@ -75,6 +93,7 @@ public class RouteController(RouteService service) : ControllerBase
}
[HttpPost("{id:guid}/visits/check-out")]
[Authorize("route.manage")]
public async Task<IActionResult> CheckOut(Guid id, RecordCheckOutRequest request, CancellationToken ct)
{
var result = await service.RecordCheckOutAsync(id, request, ct);
@@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.Scheme;
@@ -7,10 +8,12 @@ namespace Distribution.Features.Scheme;
public class SchemeController(SchemeService service) : ControllerBase
{
[HttpGet]
[Authorize("scheme.view")]
public async Task<IActionResult> GetAll([FromQuery] Guid? regionId) =>
Ok(await service.GetAllAsync(regionId));
[HttpGet("{id:guid}")]
[Authorize("scheme.view")]
public async Task<IActionResult> GetById(Guid id)
{
var result = await service.GetByIdAsync(id);
@@ -18,6 +21,7 @@ public class SchemeController(SchemeService service) : ControllerBase
}
[HttpPost]
[Authorize("scheme.manage")]
public async Task<IActionResult> Create(CreateSchemeRequest request, CancellationToken ct)
{
try
@@ -32,6 +36,7 @@ public class SchemeController(SchemeService service) : ControllerBase
}
[HttpPut("{id:guid}")]
[Authorize("scheme.manage")]
public async Task<IActionResult> Update(Guid id, UpdateSchemeRequest request, CancellationToken ct)
{
try
@@ -46,6 +51,7 @@ public class SchemeController(SchemeService service) : ControllerBase
}
[HttpDelete("{id:guid}")]
[Authorize("scheme.manage")]
public async Task<IActionResult> Delete(Guid id, CancellationToken ct)
{
var deleted = await service.DeleteAsync(id, ct);
@@ -53,6 +59,7 @@ public class SchemeController(SchemeService service) : ControllerBase
}
[HttpPut("{id:guid}/free-goods-rule")]
[Authorize("scheme.manage")]
public async Task<IActionResult> SetFreeGoodsRule(Guid id, SetFreeGoodsRuleRequest request, CancellationToken ct)
{
try
@@ -67,6 +74,7 @@ public class SchemeController(SchemeService service) : ControllerBase
}
[HttpDelete("{id:guid}/free-goods-rule")]
[Authorize("scheme.manage")]
public async Task<IActionResult> ClearFreeGoodsRule(Guid id, CancellationToken ct)
{
var result = await service.ClearFreeGoodsRuleAsync(id, ct);
@@ -74,6 +82,7 @@ public class SchemeController(SchemeService service) : ControllerBase
}
[HttpPut("{id:guid}/bundle-components/{itemId:guid}")]
[Authorize("scheme.manage")]
public async Task<IActionResult> UpsertBundleComponent(Guid id, Guid itemId, UpsertBundleComponentRequest request, CancellationToken ct)
{
try
@@ -88,6 +97,7 @@ public class SchemeController(SchemeService service) : ControllerBase
}
[HttpDelete("{id:guid}/bundle-components/{itemId:guid}")]
[Authorize("scheme.manage")]
public async Task<IActionResult> RemoveBundleComponent(Guid id, Guid itemId, CancellationToken ct)
{
var result = await service.RemoveBundleComponentAsync(id, itemId, ct);
@@ -95,6 +105,7 @@ public class SchemeController(SchemeService service) : ControllerBase
}
[HttpGet("eligible")]
[Authorize("scheme.view")]
public async Task<IActionResult> GetEligible([FromQuery] Guid storeId, [FromQuery] Guid orderId)
{
var result = await service.EvaluateEligibilityAsync(storeId, orderId);
@@ -102,6 +113,7 @@ public class SchemeController(SchemeService service) : ControllerBase
}
[HttpGet("{id:guid}/spend")]
[Authorize("scheme.view")]
public async Task<IActionResult> GetSpend(Guid id)
{
var result = await service.GetSpendAsync(id);
@@ -109,6 +121,7 @@ public class SchemeController(SchemeService service) : ControllerBase
}
[HttpGet("{id:guid}/roi")]
[Authorize("scheme.view")]
public async Task<IActionResult> GetROI(Guid id)
{
var result = await service.GetROIAsync(id);
@@ -121,6 +134,7 @@ public class SchemeController(SchemeService service) : ControllerBase
public class SchemeOrderController(SchemeService service) : ControllerBase
{
[HttpPost("{id:guid}/apply-scheme")]
[Authorize("scheme.manage")]
public async Task<IActionResult> ApplyScheme(Guid id, ApplySchemeRequest request, CancellationToken ct)
{
try
+14 -3
View File
@@ -1,5 +1,6 @@
using Distribution.Infra.Authorization;
using Distribution.Infra.Authorization;
using Distribution.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.Stock;
@@ -8,9 +9,10 @@ namespace Distribution.Features.Stock;
[Route("api/stock")]
public class StockController(StockService service, ICurrentUserService currentUser) : ControllerBase
{
// ── Query ──────────────────────────────────────────────────────────────
// ── Query ──────────────────────────────────────────────────────────────────
[HttpGet("balances")]
[Authorize("stock.view")]
public async Task<IActionResult> GetBalances(
[FromQuery] Guid? locationId,
[FromQuery] LocationType? locationType,
@@ -23,6 +25,7 @@ public class StockController(StockService service, ICurrentUserService currentUs
}
[HttpGet("low-stock")]
[Authorize("stock.view")]
public async Task<IActionResult> GetLowStock([FromQuery] Guid? locationId)
{
var result = await service.GetLowStockAsync(locationId);
@@ -30,6 +33,7 @@ public class StockController(StockService service, ICurrentUserService currentUs
}
[HttpGet("balances/{itemId}/total")]
[Authorize("stock.view")]
public async Task<IActionResult> GetItemTotal(Guid itemId)
{
var result = await service.GetItemTotalAsync(itemId);
@@ -39,6 +43,7 @@ public class StockController(StockService service, ICurrentUserService currentUs
/// <summary>Next free number for auto-generated serials (e.g. after ...-0005 the answer is 6).</summary>
[HttpGet("serials/next-sequence")]
[Authorize("stock.view")]
public async Task<IActionResult> GetNextSerialSequence([FromQuery] Guid itemId, [FromQuery] string prefix = "")
{
var next = await service.GetNextSerialSequenceAsync(itemId, prefix);
@@ -46,6 +51,7 @@ public class StockController(StockService service, ICurrentUserService currentUs
}
[HttpGet("movements")]
[Authorize("stock.view")]
public async Task<IActionResult> GetMovements(
[FromQuery] Guid? locationId,
[FromQuery] Guid? itemId,
@@ -59,9 +65,10 @@ public class StockController(StockService service, ICurrentUserService currentUs
return Ok(result);
}
// ── Write ──────────────────────────────────────────────────────────────
// ── Write ──────────────────────────────────────────────────────────────────
[HttpPost("transfer")]
[Authorize("stock.manage")]
public async Task<IActionResult> Transfer(TransferRequest request, CancellationToken ct)
{
if (currentUser.UserId is null) return Unauthorized();
@@ -74,6 +81,7 @@ public class StockController(StockService service, ICurrentUserService currentUs
}
[HttpPost("damage")]
[Authorize("stock.manage")]
public async Task<IActionResult> RecordDamage(DamageRequest request, CancellationToken ct)
{
if (currentUser.UserId is null) return Unauthorized();
@@ -86,6 +94,7 @@ public class StockController(StockService service, ICurrentUserService currentUs
}
[HttpPost("customer-return")]
[Authorize("stock.manage")]
public async Task<IActionResult> RecordCustomerReturn(CustomerReturnRequest request, CancellationToken ct)
{
if (currentUser.UserId is null) return Unauthorized();
@@ -98,6 +107,7 @@ public class StockController(StockService service, ICurrentUserService currentUs
}
[HttpPost("adjustment")]
[Authorize("stock.manage")]
public async Task<IActionResult> Adjust(AdjustmentRequest request, CancellationToken ct)
{
// TEMPORARY: currentUser.UserId is always unset due to the broken auth middleware
@@ -113,6 +123,7 @@ public class StockController(StockService service, ICurrentUserService currentUs
}
[HttpPatch("movements/{id}/reason")]
[Authorize("stock.manage")]
public async Task<IActionResult> UpdateMovementReason(Guid id, UpdateMovementReasonRequest request, CancellationToken ct)
{
try
+23 -2
View File
@@ -1,13 +1,19 @@
using Distribution.Models;
using Distribution.Infra.Authorization;
using Distribution.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.Store;
[ApiController]
[Route("api/stores")]
public class StoreController(StoreService service) : ControllerBase
public class StoreController(
StoreService service,
IScopeAccessService scopeAccess,
ICurrentUserService currentUser) : ControllerBase
{
[HttpGet]
[Authorize("store.view")]
public async Task<IActionResult> GetAll(
[FromQuery] Guid? routeId,
[FromQuery] string? search,
@@ -16,13 +22,18 @@ public class StoreController(StoreService service) : ControllerBase
Ok(await service.GetAllAsync(routeId, search, page, pageSize));
[HttpGet("{id:guid}")]
[Authorize("store.view")]
public async Task<IActionResult> GetById(Guid id)
{
var userId = currentUser.UserId;
if (userId.HasValue && !await scopeAccess.HasStoreScopeAsync(userId.Value, id))
return StatusCode(403);
var result = await service.GetByIdAsync(id);
return result is null ? NotFound() : Ok(result);
}
[HttpPost]
[Authorize("store.manage")]
public async Task<IActionResult> Create(CreateStoreRequest request, CancellationToken ct)
{
var result = await service.CreateAsync(request, ct);
@@ -30,6 +41,7 @@ public class StoreController(StoreService service) : ControllerBase
}
[HttpPut("{id:guid}")]
[Authorize("store.manage")]
public async Task<IActionResult> Update(Guid id, UpdateStoreRequest request, CancellationToken ct)
{
var result = await service.UpdateAsync(id, request, ct);
@@ -37,6 +49,7 @@ public class StoreController(StoreService service) : ControllerBase
}
[HttpDelete("{id:guid}")]
[Authorize("store.manage")]
public async Task<IActionResult> Delete(Guid id, CancellationToken ct)
{
var deleted = await service.DeleteAsync(id, ct);
@@ -44,6 +57,7 @@ public class StoreController(StoreService service) : ControllerBase
}
[HttpPut("{id:guid}/credit-limit")]
[Authorize("store.manage")]
public async Task<IActionResult> UpdateCreditLimit(Guid id, UpdateCreditLimitRequest request, CancellationToken ct)
{
var result = await service.UpdateCreditLimitAsync(id, request, ct);
@@ -51,6 +65,7 @@ public class StoreController(StoreService service) : ControllerBase
}
[HttpPut("{id:guid}/outstanding-balance")]
[Authorize("store.manage")]
public async Task<IActionResult> UpdateOutstandingBalance(Guid id, UpdateOutstandingBalanceRequest request, CancellationToken ct)
{
var result = await service.UpdateOutstandingBalanceAsync(id, request, ct);
@@ -58,6 +73,7 @@ public class StoreController(StoreService service) : ControllerBase
}
[HttpGet("{id:guid}/outstanding-balance")]
[Authorize("store.view")]
public async Task<IActionResult> GetOutstandingBalance(Guid id)
{
var result = await service.GetOutstandingBalanceAsync(id);
@@ -65,6 +81,7 @@ public class StoreController(StoreService service) : ControllerBase
}
[HttpPut("{id:guid}/segment")]
[Authorize("store.manage")]
public async Task<IActionResult> ClassifySegment(Guid id, ClassifySegmentRequest request, CancellationToken ct)
{
var result = await service.ClassifySegmentAsync(id, request, ct);
@@ -72,6 +89,7 @@ public class StoreController(StoreService service) : ControllerBase
}
[HttpGet("{id:guid}/visit-history")]
[Authorize("store.view")]
public async Task<IActionResult> GetVisitHistory(Guid id)
{
var result = await service.GetVisitHistoryAsync(id);
@@ -79,6 +97,7 @@ public class StoreController(StoreService service) : ControllerBase
}
[HttpGet("{id:guid}/schemes/{schemeId:guid}/eligibility")]
[Authorize("store.view")]
public async Task<IActionResult> CheckSchemeEligibility(Guid id, Guid schemeId)
{
var result = await service.CheckSchemeEligibilityAsync(id, schemeId);
@@ -86,6 +105,7 @@ public class StoreController(StoreService service) : ControllerBase
}
[HttpGet("{id:guid}/dashboard")]
[Authorize("store.view")]
public async Task<IActionResult> GetDashboard(Guid id, [FromQuery] string period)
{
var result = await service.GetDashboardAsync(id, period);
@@ -93,6 +113,7 @@ public class StoreController(StoreService service) : ControllerBase
}
[HttpPatch("{id:guid}/status")]
[Authorize("store.manage")]
public async Task<IActionResult> PatchStatus(Guid id, PatchStoreStatusRequest request, CancellationToken ct)
{
var result = await service.SetActiveStatusAsync(id, request.IsActive, ct);
@@ -1,4 +1,5 @@
using Distribution.Infra.Authorization;
using Distribution.Infra.Authorization;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.SupplierClaims;
@@ -7,10 +8,12 @@ namespace Distribution.Features.SupplierClaims;
public class SupplierClaimController(SupplierClaimService service, ICurrentUserService currentUser) : ControllerBase
{
[HttpGet("api/supplier-claims")]
[Authorize("supplier-claim.view")]
public async Task<IActionResult> GetAll() =>
Ok(await service.GetAllAsync());
[HttpGet("api/supplier-claims/{id:guid}")]
[Authorize("supplier-claim.view")]
public async Task<IActionResult> GetById(Guid id)
{
var result = await service.GetByIdAsync(id);
@@ -18,10 +21,12 @@ public class SupplierClaimController(SupplierClaimService service, ICurrentUserS
}
[HttpGet("api/purchase-orders/{id:guid}/claims")]
[Authorize("supplier-claim.view")]
public async Task<IActionResult> GetByPurchaseOrder(Guid id) =>
Ok(await service.GetByPurchaseOrderIdAsync(id));
[HttpPost("api/purchase-orders/{id:guid}/claims")]
[Authorize("supplier-claim.manage")]
public async Task<IActionResult> Create(Guid id, CreateSupplierClaimRequest request, CancellationToken ct)
{
// TEMPORARY: currentUser.UserId is always unset due to the broken auth middleware
@@ -37,6 +42,7 @@ public class SupplierClaimController(SupplierClaimService service, ICurrentUserS
}
[HttpPut("api/supplier-claims/{id:guid}")]
[Authorize("supplier-claim.manage")]
public async Task<IActionResult> Update(Guid id, UpdateSupplierClaimRequest request, CancellationToken ct)
{
try
@@ -48,6 +54,7 @@ public class SupplierClaimController(SupplierClaimService service, ICurrentUserS
}
[HttpPost("api/supplier-claims/{id:guid}/approve")]
[Authorize("supplier-claim.manage")]
public async Task<IActionResult> Approve(Guid id, ApproveSupplierClaimRequest request, CancellationToken ct)
{
var userId = currentUser.UserId ?? request.ApprovedByUserId;
@@ -62,6 +69,7 @@ public class SupplierClaimController(SupplierClaimService service, ICurrentUserS
}
[HttpPost("api/supplier-claims/{id:guid}/reject")]
[Authorize("supplier-claim.manage")]
public async Task<IActionResult> Reject(Guid id, RejectSupplierClaimRequest request, CancellationToken ct)
{
var userId = currentUser.UserId ?? request.RejectedByUserId;
@@ -1,5 +1,6 @@
using Distribution.Infra.Authorization;
using Distribution.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.SysConfig;
@@ -9,11 +10,12 @@ namespace Distribution.Features.SysConfig;
public class SysConfigController(SysConfigService service, ICurrentUserService currentUser) : ControllerBase
{
[HttpGet]
[Authorize("sys-config.view")]
public async Task<IActionResult> GetAll() =>
Ok(await service.GetAllAsync());
// Typed shortcut — must be declared before {key} to take precedence
[HttpGet("invoice-creation-mode")]
[Authorize("sys-config.view")]
public async Task<IActionResult> GetInvoiceCreationMode()
{
var mode = await service.GetInvoiceCreationModeAsync();
@@ -21,6 +23,7 @@ public class SysConfigController(SysConfigService service, ICurrentUserService c
}
[HttpPut("invoice-creation-mode")]
[Authorize("sys-config.manage")]
public async Task<IActionResult> SetInvoiceCreationMode(SetInvoiceCreationModeRequest request, CancellationToken ct)
{
var userId = currentUser.UserId ?? throw new UnauthorizedAccessException();
@@ -33,6 +36,7 @@ public class SysConfigController(SysConfigService service, ICurrentUserService c
}
[HttpGet("{key}")]
[Authorize("sys-config.view")]
public async Task<IActionResult> GetByKey(string key)
{
var result = await service.GetByKeyAsync(key);
@@ -40,6 +44,7 @@ public class SysConfigController(SysConfigService service, ICurrentUserService c
}
[HttpPut("{key}")]
[Authorize("sys-config.manage")]
public async Task<IActionResult> Set(string key, SetSysConfigRequest request, CancellationToken ct)
{
var userId = currentUser.UserId ?? request.UserId;