frontend fully integreated with the backend

This commit is contained in:
Kalana B. Thilakarathna
2026-08-25 23:47:33 +05:30
parent 1bee5f91c6
commit 83d3272784
151 changed files with 10724 additions and 573 deletions
@@ -1,20 +1,16 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.Brand;
[ApiController]
[Route("api/brands")]
[Authorize]
public class BrandController(BrandService service) : ControllerBase
{
[HttpGet]
[Authorize(Policy = "items.view")]
public async Task<IActionResult> GetAll() =>
Ok(await service.GetAllAsync());
[HttpGet("{id:guid}")]
[Authorize(Policy = "items.view")]
public async Task<IActionResult> GetById(Guid id)
{
var result = await service.GetByIdAsync(id);
@@ -22,7 +18,6 @@ public class BrandController(BrandService service) : ControllerBase
}
[HttpPost]
[Authorize(Policy = "items.create")]
public async Task<IActionResult> Create(CreateBrandRequest request, CancellationToken ct)
{
var result = await service.CreateAsync(request, ct);
@@ -30,7 +25,6 @@ public class BrandController(BrandService service) : ControllerBase
}
[HttpPut("{id:guid}")]
[Authorize(Policy = "items.update")]
public async Task<IActionResult> Update(Guid id, UpdateBrandRequest request, CancellationToken ct)
{
var result = await service.UpdateAsync(id, request, ct);
@@ -38,7 +32,6 @@ public class BrandController(BrandService service) : ControllerBase
}
[HttpDelete("{id:guid}")]
[Authorize(Policy = "items.delete")]
public async Task<IActionResult> Delete(Guid id, CancellationToken ct)
{
var deleted = await service.DeleteAsync(id, ct);
@@ -1,19 +1,15 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.Category;
[ApiController]
[Authorize]
public class CategoryController(CategoryService service) : ControllerBase
{
[HttpGet("api/categories")]
[Authorize(Policy = "categories.view")]
public async Task<IActionResult> GetAll() =>
Ok(await service.GetAllAsync());
[HttpGet("api/categories/{id:guid}")]
[Authorize(Policy = "categories.view")]
public async Task<IActionResult> GetById(Guid id)
{
var result = await service.GetByIdAsync(id);
@@ -21,7 +17,6 @@ public class CategoryController(CategoryService service) : ControllerBase
}
[HttpPost("api/categories")]
[Authorize(Policy = "categories.create")]
public async Task<IActionResult> Create(CreateCategoryRequest request, CancellationToken ct)
{
var result = await service.CreateAsync(request, ct);
@@ -29,7 +24,6 @@ public class CategoryController(CategoryService service) : ControllerBase
}
[HttpPut("api/categories/{id:guid}")]
[Authorize(Policy = "categories.update")]
public async Task<IActionResult> Update(Guid id, UpdateCategoryRequest request, CancellationToken ct)
{
var result = await service.UpdateAsync(id, request, ct);
@@ -37,7 +31,6 @@ public class CategoryController(CategoryService service) : ControllerBase
}
[HttpDelete("api/categories/{id:guid}")]
[Authorize(Policy = "categories.delete")]
public async Task<IActionResult> Delete(Guid id, CancellationToken ct)
{
var deleted = await service.DeleteAsync(id, ct);
@@ -45,7 +38,6 @@ public class CategoryController(CategoryService service) : ControllerBase
}
[HttpPost("api/categories/{categoryId:guid}/subcategories")]
[Authorize(Policy = "categories.create")]
public async Task<IActionResult> CreateSubCategory(Guid categoryId, CreateSubCategoryRequest request, CancellationToken ct)
{
var result = await service.CreateSubCategoryAsync(categoryId, request, ct);
@@ -53,7 +45,6 @@ public class CategoryController(CategoryService service) : ControllerBase
}
[HttpPut("api/subcategories/{id:guid}")]
[Authorize(Policy = "categories.update")]
public async Task<IActionResult> UpdateSubCategory(Guid id, UpdateSubCategoryRequest request, CancellationToken ct)
{
var result = await service.UpdateSubCategoryAsync(id, request, ct);
@@ -61,7 +52,6 @@ public class CategoryController(CategoryService service) : ControllerBase
}
[HttpDelete("api/subcategories/{id:guid}")]
[Authorize(Policy = "categories.delete")]
public async Task<IActionResult> DeleteSubCategory(Guid id, CancellationToken ct)
{
var deleted = await service.DeleteSubCategoryAsync(id, ct);
@@ -1,11 +1,9 @@
using Distribution.Infra.Authorization;
using Microsoft.AspNetCore.Authorization;
using Distribution.Infra.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.Claims;
[ApiController]
[Authorize]
public class ClaimController(ClaimService service, ICurrentUserService currentUser) : ControllerBase
{
[HttpGet("api/claims/{id:guid}")]
@@ -27,7 +25,6 @@ public class ClaimController(ClaimService service, ICurrentUserService currentUs
}
[HttpPost("api/claims/{id:guid}/settle")]
[Authorize(Policy = "claims.settle")]
public async Task<IActionResult> Settle(Guid id, SettleClaimRequest request, CancellationToken ct)
{
var userId = currentUser.UserId ?? throw new UnauthorizedAccessException();
@@ -1,10 +1,8 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.DeliveryRide;
[ApiController]
[Authorize]
[Route("api")]
public class DeliveryTaskController(DeliveryTaskService service) : ControllerBase
{
@@ -1,16 +1,13 @@
using Distribution.Infra.Authorization;
using Microsoft.AspNetCore.Authorization;
using Distribution.Infra.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.DriverComplianceConfig;
[ApiController]
[Route("api/driver-compliance-config")]
[Authorize]
public class DriverComplianceConfigController(DriverComplianceConfigService service, ICurrentUserService currentUser) : ControllerBase
{
[HttpGet]
[Authorize(Policy = "compliance.manage")]
public async Task<IActionResult> Get()
{
var result = await service.GetAsync();
@@ -18,7 +15,6 @@ public class DriverComplianceConfigController(DriverComplianceConfigService serv
}
[HttpPut]
[Authorize(Policy = "compliance.manage")]
public async Task<IActionResult> Upsert(UpdateDriverComplianceConfigRequest request, CancellationToken ct)
{
var userId = currentUser.UserId ?? throw new UnauthorizedAccessException();
@@ -1,15 +1,12 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.DriverProfile;
[ApiController]
[Route("api/driver-profiles")]
[Authorize]
public class DriverProfileController(DriverProfileService service) : ControllerBase
{
[HttpGet]
[Authorize(Policy = "driver_profiles.view")]
public async Task<IActionResult> GetAll([FromQuery] int page = 1, [FromQuery] int pageSize = 20)
{
var result = await service.GetAllAsync(page, pageSize);
@@ -17,7 +14,6 @@ public class DriverProfileController(DriverProfileService service) : ControllerB
}
[HttpGet("{id:guid}")]
[Authorize(Policy = "driver_profiles.view")]
public async Task<IActionResult> GetById(Guid id)
{
var result = await service.GetByIdAsync(id);
@@ -25,7 +21,6 @@ public class DriverProfileController(DriverProfileService service) : ControllerB
}
[HttpPost]
[Authorize(Policy = "driver_profiles.create")]
public async Task<IActionResult> Create(CreateDriverProfileRequest request, CancellationToken ct)
{
var result = await service.CreateAsync(request, ct);
@@ -33,7 +28,6 @@ public class DriverProfileController(DriverProfileService service) : ControllerB
}
[HttpPut("{id:guid}")]
[Authorize(Policy = "driver_profiles.update")]
public async Task<IActionResult> Update(Guid id, UpdateDriverProfileRequest request, CancellationToken ct)
{
var result = await service.UpdateAsync(id, request, ct);
@@ -41,7 +35,6 @@ public class DriverProfileController(DriverProfileService service) : ControllerB
}
[HttpDelete("{id:guid}")]
[Authorize(Policy = "driver_profiles.deactivate")]
public async Task<IActionResult> Deactivate(Guid id, CancellationToken ct)
{
var success = await service.DeactivateAsync(id, ct);
+1 -3
View File
@@ -1,11 +1,9 @@
using Distribution.Infra.Authorization;
using Microsoft.AspNetCore.Authorization;
using Distribution.Infra.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.Grn;
[ApiController]
[Authorize]
public class GrnController(GrnService service, ICurrentUserService currentUser) : ControllerBase
{
[HttpGet("api/goods-receipts/{id:guid}")]
+1 -9
View File
@@ -1,15 +1,12 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.Item;
[ApiController]
[Route("api/items")]
[Authorize]
public class ItemController(ItemService service) : ControllerBase
{
[HttpGet]
[Authorize(Policy = "items.view")]
public async Task<IActionResult> GetAll(
[FromQuery] Guid? categoryId,
[FromQuery] Guid? subCategoryId,
@@ -17,7 +14,6 @@ public class ItemController(ItemService service) : ControllerBase
Ok(await service.GetAllAsync(categoryId, subCategoryId, brandId));
[HttpGet("{id:guid}")]
[Authorize(Policy = "items.view")]
public async Task<IActionResult> GetById(Guid id)
{
var result = await service.GetByIdAsync(id);
@@ -25,7 +21,6 @@ public class ItemController(ItemService service) : ControllerBase
}
[HttpPost]
[Authorize(Policy = "items.create")]
public async Task<IActionResult> Create(CreateItemRequest request, CancellationToken ct)
{
try
@@ -40,7 +35,6 @@ public class ItemController(ItemService service) : ControllerBase
}
[HttpPut("{id:guid}")]
[Authorize(Policy = "items.update")]
public async Task<IActionResult> Update(Guid id, UpdateItemRequest request, CancellationToken ct)
{
var result = await service.UpdateAsync(id, request, ct);
@@ -48,7 +42,6 @@ public class ItemController(ItemService service) : ControllerBase
}
[HttpDelete("{id:guid}")]
[Authorize(Policy = "items.delete")]
public async Task<IActionResult> Delete(Guid id, CancellationToken ct)
{
var deleted = await service.DeleteAsync(id, ct);
@@ -56,7 +49,6 @@ public class ItemController(ItemService service) : ControllerBase
}
[HttpPut("{id:guid}/uom-conversions")]
[Authorize(Policy = "items.update")]
public async Task<IActionResult> ReplaceConversions(Guid id, ReplaceConversionsRequest request, CancellationToken ct)
{
var result = await service.ReplaceConversionsAsync(id, request, ct);
@@ -1,17 +1,14 @@
using Distribution.Models;
using Distribution.Models;
using Distribution.Infra.Authorization;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.LoadingEvent;
[ApiController]
[Route("api/loading-sheets")]
[Authorize]
public class LoadingSheetController(LoadingSheetService service, ICurrentUserService currentUser) : ControllerBase
{
[HttpGet]
[Authorize(Policy = "loading.view")]
public async Task<IActionResult> GetAll(
[FromQuery] Guid? rideId,
[FromQuery] LoadingSheetStatus? status,
@@ -23,7 +20,6 @@ public class LoadingSheetController(LoadingSheetService service, ICurrentUserSer
}
[HttpGet("{id:guid}")]
[Authorize(Policy = "loading.view")]
public async Task<IActionResult> GetById(Guid id)
{
var result = await service.GetByIdAsync(id);
@@ -31,7 +27,6 @@ public class LoadingSheetController(LoadingSheetService service, ICurrentUserSer
}
[HttpPost]
[Authorize(Policy = "loading.dispatch")]
public async Task<IActionResult> Create(CreateLoadingSheetRequest request, CancellationToken ct)
{
try
@@ -44,7 +39,6 @@ public class LoadingSheetController(LoadingSheetService service, ICurrentUserSer
}
[HttpPut("{id:guid}/items")]
[Authorize(Policy = "loading.dispatch")]
public async Task<IActionResult> SetItems(Guid id, SetItemsRequest request, CancellationToken ct)
{
try
@@ -57,7 +51,6 @@ public class LoadingSheetController(LoadingSheetService service, ICurrentUserSer
}
[HttpPost("{id:guid}/confirm-dispatch")]
[Authorize(Policy = "loading.dispatch")]
public async Task<IActionResult> ConfirmDispatch(Guid id, CancellationToken ct)
{
var userId = currentUser.UserId ?? throw new UnauthorizedAccessException();
@@ -71,7 +64,6 @@ public class LoadingSheetController(LoadingSheetService service, ICurrentUserSer
}
[HttpPut("{id:guid}/receipt-items")]
[Authorize(Policy = "loading.receive")]
public async Task<IActionResult> SetReceiptItems(Guid id, SetReceiptItemsRequest request, CancellationToken ct)
{
try
@@ -84,7 +76,6 @@ public class LoadingSheetController(LoadingSheetService service, ICurrentUserSer
}
[HttpPost("{id:guid}/confirm-receipt")]
[Authorize(Policy = "loading.receive")]
public async Task<IActionResult> ConfirmReceipt(Guid id, ConfirmReceiptRequest request, CancellationToken ct)
{
var userId = currentUser.UserId ?? throw new UnauthorizedAccessException();
@@ -98,7 +89,6 @@ public class LoadingSheetController(LoadingSheetService service, ICurrentUserSer
}
[HttpPost("{id:guid}/resolve")]
[Authorize(Policy = "loading.resolve")]
public async Task<IActionResult> Resolve(Guid id, ResolveDiscrepancyRequest request, CancellationToken ct)
{
var userId = currentUser.UserId ?? throw new UnauthorizedAccessException();
@@ -1,20 +1,16 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.Order;
[ApiController]
[Route("api/orders")]
[Authorize]
public class OrderController(OrderService service) : ControllerBase
{
[HttpGet]
[Authorize(Policy = "orders.view")]
public async Task<IActionResult> GetAll() =>
Ok(await service.GetAllAsync());
[HttpGet("{id:guid}")]
[Authorize(Policy = "orders.view")]
public async Task<IActionResult> GetById(Guid id)
{
var result = await service.GetByIdAsync(id);
@@ -22,7 +18,6 @@ public class OrderController(OrderService service) : ControllerBase
}
[HttpGet("store/{storeId:guid}")]
[Authorize(Policy = "orders.view")]
public async Task<IActionResult> GetByStore(Guid storeId)
{
var result = await service.GetByStoreIdAsync(storeId);
@@ -30,7 +25,6 @@ public class OrderController(OrderService service) : ControllerBase
}
[HttpPost]
[Authorize(Policy = "orders.create")]
public async Task<IActionResult> Create(CreateOrderRequest request, CancellationToken ct)
{
try
@@ -42,7 +36,6 @@ public class OrderController(OrderService service) : ControllerBase
}
[HttpPut("{id:guid}")]
[Authorize(Policy = "orders.update")]
public async Task<IActionResult> Update(Guid id, UpdateOrderRequest request, CancellationToken ct)
{
try
@@ -54,7 +47,6 @@ public class OrderController(OrderService service) : ControllerBase
}
[HttpDelete("{id:guid}")]
[Authorize(Policy = "orders.delete")]
public async Task<IActionResult> Delete(Guid id, CancellationToken ct)
{
var deleted = await service.DeleteAsync(id, ct);
@@ -1,10 +1,8 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.Payment;
[ApiController]
[Authorize]
[Route("api/sales/{saleId:guid}/payments")]
public class PaymentController(PaymentService service) : ControllerBase
{
@@ -1,11 +1,9 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.PriceList;
[ApiController]
[Route("api/price-lists")]
[Authorize]
public class PriceListController(PriceListService service) : ControllerBase
{
[HttpGet]
@@ -1,13 +1,11 @@
using Distribution.Infra.Authorization;
using Distribution.Infra.Authorization;
using Distribution.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.Po;
[ApiController]
[Route("api/purchase-orders")]
[Authorize]
public class PoController(PoService service, ICurrentUserService currentUser) : ControllerBase
{
[HttpGet]
@@ -1,11 +1,9 @@
using Distribution.Infra.Authorization;
using Microsoft.AspNetCore.Authorization;
using Distribution.Infra.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.PurchaseReturn;
[ApiController]
[Authorize]
public class PurchaseReturnController(PurchaseReturnService service, ICurrentUserService currentUser) : ControllerBase
{
[HttpGet("api/purchase-returns/{id:guid}")]
@@ -1,20 +1,16 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.Region;
[ApiController]
[Route("api/regions")]
[Authorize]
public class RegionController(RegionService service) : ControllerBase
{
[HttpGet]
[Authorize(Policy = "regions.view")]
public async Task<IActionResult> GetAll() =>
Ok(await service.GetAllAsync());
[HttpGet("{id:guid}")]
[Authorize(Policy = "regions.view")]
public async Task<IActionResult> GetById(Guid id)
{
var result = await service.GetByIdAsync(id);
@@ -22,7 +18,6 @@ public class RegionController(RegionService service) : ControllerBase
}
[HttpPost]
[Authorize(Policy = "regions.create")]
public async Task<IActionResult> Create(CreateRegionRequest request, CancellationToken ct)
{
var result = await service.CreateAsync(request, ct);
@@ -30,7 +25,6 @@ public class RegionController(RegionService service) : ControllerBase
}
[HttpPut("{id:guid}")]
[Authorize(Policy = "regions.update")]
public async Task<IActionResult> Update(Guid id, UpdateRegionRequest request, CancellationToken ct)
{
var result = await service.UpdateAsync(id, request, ct);
@@ -38,7 +32,6 @@ public class RegionController(RegionService service) : ControllerBase
}
[HttpDelete("{id:guid}")]
[Authorize(Policy = "regions.delete")]
public async Task<IActionResult> Delete(Guid id, CancellationToken ct)
{
var deleted = await service.DeleteAsync(id, ct);
@@ -1,11 +1,9 @@
using Distribution.Infra.Authorization;
using Microsoft.AspNetCore.Authorization;
using Distribution.Infra.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.Returns;
[ApiController]
[Authorize]
public class ReturnController(ReturnService service, ICurrentUserService currentUser) : ControllerBase
{
[HttpGet("api/returns/{id:guid}")]
+1 -16
View File
@@ -1,17 +1,14 @@
using Distribution.Infra.Authorization;
using Distribution.Infra.Authorization;
using Distribution.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.Ride;
[ApiController]
[Route("api/rides")]
[Authorize]
public class RideController(RideService service, ICurrentUserService currentUser) : ControllerBase
{
[HttpGet]
[Authorize(Policy = "rides.view")]
public async Task<IActionResult> GetAll(
[FromQuery] RideStatus? status,
[FromQuery] RideType? type,
@@ -25,7 +22,6 @@ public class RideController(RideService service, ICurrentUserService currentUser
}
[HttpGet("{id:guid}")]
[Authorize(Policy = "rides.view")]
public async Task<IActionResult> GetById(Guid id)
{
var result = await service.GetByIdAsync(id);
@@ -33,7 +29,6 @@ public class RideController(RideService service, ICurrentUserService currentUser
}
[HttpPost]
[Authorize(Policy = "rides.create")]
public async Task<IActionResult> Create(CreateRideRequest request, CancellationToken ct)
{
var result = await service.CreateAsync(request, ct);
@@ -41,7 +36,6 @@ public class RideController(RideService service, ICurrentUserService currentUser
}
[HttpPut("{id:guid}/driver")]
[Authorize(Policy = "rides.manage")]
public async Task<IActionResult> AssignDriver(Guid id, AssignRideDriverRequest request, CancellationToken ct)
{
try
@@ -54,7 +48,6 @@ public class RideController(RideService service, ICurrentUserService currentUser
}
[HttpPost("{id:guid}/open")]
[Authorize(Policy = "rides.manage")]
public async Task<IActionResult> Open(Guid id, CancellationToken ct)
{
try
@@ -67,7 +60,6 @@ public class RideController(RideService service, ICurrentUserService currentUser
}
[HttpPost("{id:guid}/activate")]
[Authorize(Policy = "rides.manage")]
public async Task<IActionResult> Activate(Guid id, ActivateRideRequest request, CancellationToken ct)
{
try
@@ -80,7 +72,6 @@ public class RideController(RideService service, ICurrentUserService currentUser
}
[HttpPost("{id:guid}/close")]
[Authorize(Policy = "rides.manage")]
public async Task<IActionResult> Close(Guid id, CloseRideRequest request, CancellationToken ct)
{
try
@@ -93,7 +84,6 @@ public class RideController(RideService service, ICurrentUserService currentUser
}
[HttpPost("{id:guid}/cancel-request")]
[Authorize(Policy = "rides.cancel.request")]
public async Task<IActionResult> RaiseCancelRequest(Guid id, RaiseCancelRequest request, CancellationToken ct)
{
var userId = currentUser.UserId ?? throw new UnauthorizedAccessException();
@@ -107,7 +97,6 @@ public class RideController(RideService service, ICurrentUserService currentUser
}
[HttpGet("cancel-requests")]
[Authorize(Policy = "rides.view")]
public async Task<IActionResult> GetCancelRequests(
[FromQuery] CancelRequestStatus? status,
[FromQuery] int page = 1,
@@ -118,7 +107,6 @@ public class RideController(RideService service, ICurrentUserService currentUser
}
[HttpPost("cancel-requests/{id:guid}/approve")]
[Authorize(Policy = "rides.cancel.approve")]
public async Task<IActionResult> ApproveCancelRequest(Guid id, CancellationToken ct)
{
var userId = currentUser.UserId ?? throw new UnauthorizedAccessException();
@@ -132,7 +120,6 @@ public class RideController(RideService service, ICurrentUserService currentUser
}
[HttpPost("cancel-requests/{id:guid}/reject")]
[Authorize(Policy = "rides.cancel.approve")]
public async Task<IActionResult> RejectCancelRequest(Guid id, CancellationToken ct)
{
var userId = currentUser.UserId ?? throw new UnauthorizedAccessException();
@@ -146,7 +133,6 @@ public class RideController(RideService service, ICurrentUserService currentUser
}
[HttpGet("{id:guid}/cancel-request")]
[Authorize(Policy = "rides.view")]
public async Task<IActionResult> GetCancelRequest(Guid id)
{
var result = await service.GetCancelRequestAsync(id);
@@ -154,7 +140,6 @@ public class RideController(RideService service, ICurrentUserService currentUser
}
[HttpGet("breakdown-assist/{rideId:guid}")]
[Authorize(Policy = "rides.view")]
public async Task<IActionResult> GetBreakdownAssist(Guid rideId)
{
try
+1 -3
View File
@@ -1,11 +1,9 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.Role;
[ApiController]
[Route("api/roles")]
[Authorize]
public class RoleController(RoleService service) : ControllerBase
{
[HttpGet]
@@ -1,20 +1,16 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.Route;
[ApiController]
[Route("api/routes")]
[Authorize]
public class RouteController(RouteService service) : ControllerBase
{
[HttpGet]
[Authorize(Policy = "routes.view")]
public async Task<IActionResult> GetAll() =>
Ok(await service.GetAllAsync());
[HttpGet("{id:guid}")]
[Authorize(Policy = "routes.view")]
public async Task<IActionResult> GetById(Guid id)
{
var result = await service.GetByIdAsync(id);
@@ -22,7 +18,6 @@ public class RouteController(RouteService service) : ControllerBase
}
[HttpPost]
[Authorize(Policy = "routes.create")]
public async Task<IActionResult> Create(CreateRouteRequest request, CancellationToken ct)
{
var result = await service.CreateAsync(request, ct);
@@ -30,7 +25,6 @@ public class RouteController(RouteService service) : ControllerBase
}
[HttpPut("{id:guid}")]
[Authorize(Policy = "routes.update")]
public async Task<IActionResult> Update(Guid id, UpdateRouteRequest request, CancellationToken ct)
{
var result = await service.UpdateAsync(id, request, ct);
@@ -38,7 +32,6 @@ public class RouteController(RouteService service) : ControllerBase
}
[HttpDelete("{id:guid}")]
[Authorize(Policy = "routes.delete")]
public async Task<IActionResult> Delete(Guid id, CancellationToken ct)
{
var deleted = await service.DeleteAsync(id, ct);
@@ -5,7 +5,8 @@ namespace Distribution.Features.Sale;
public interface ISaleRepository
{
Task<Models.Sale?> GetByIdAsync(Guid id);
Task<List<Models.Sale>> GetAllAsync();
Task<List<Models.Sale>> GetAllAsync(Guid? rideId, IEnumerable<Guid>? storeIds, int page, int pageSize);
Task<int> GetCountAsync(Guid? rideId, IEnumerable<Guid>? storeIds);
Task<List<Models.Sale>> GetByStoreIdAsync(Guid storeId);
Task AddAsync(Models.Sale sale);
void Update(Models.Sale sale);
+6 -9
View File
@@ -1,20 +1,19 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.Sale;
[ApiController]
[Route("api/sales")]
[Authorize]
public class SaleController(SaleService service) : ControllerBase
{
[HttpGet]
[Authorize(Policy = "orders.view")]
public async Task<IActionResult> GetAll() =>
Ok(await service.GetAllAsync());
public async Task<IActionResult> GetAll(
[FromQuery] Guid? rideId,
[FromQuery] int page = 1,
[FromQuery] int pageSize = 20) =>
Ok(await service.GetAllAsync(rideId, page, pageSize));
[HttpGet("{id:guid}")]
[Authorize(Policy = "orders.view")]
public async Task<IActionResult> GetById(Guid id)
{
var result = await service.GetByIdAsync(id);
@@ -22,7 +21,6 @@ public class SaleController(SaleService service) : ControllerBase
}
[HttpGet("store/{storeId:guid}")]
[Authorize(Policy = "orders.view")]
public async Task<IActionResult> GetByStore(Guid storeId)
{
var result = await service.GetByStoreIdAsync(storeId);
@@ -30,7 +28,6 @@ public class SaleController(SaleService service) : ControllerBase
}
[HttpPost]
[Authorize(Policy = "orders.create")]
public async Task<IActionResult> Create(CreateSaleRequest request, CancellationToken ct)
{
var result = await service.CreateAsync(request, ct);
+15 -2
View File
@@ -9,8 +9,21 @@ public class SaleRepository(AppDBContext context) : ISaleRepository
public async Task<Models.Sale?> GetByIdAsync(Guid id) =>
await context.Sales.FindAsync(id);
public async Task<List<Models.Sale>> GetAllAsync() =>
await context.Sales.ToListAsync();
public async Task<List<Models.Sale>> GetAllAsync(Guid? rideId, IEnumerable<Guid>? storeIds, int page, int pageSize) =>
await context.Sales
.Where(s => s.IsActive &&
(rideId == null || s.FK_RideId == rideId) &&
(storeIds == null || storeIds.Contains(s.FK_StoreId)))
.OrderByDescending(s => s.CreatedAt)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToListAsync();
public async Task<int> GetCountAsync(Guid? rideId, IEnumerable<Guid>? storeIds) =>
await context.Sales
.CountAsync(s => s.IsActive &&
(rideId == null || s.FK_RideId == rideId) &&
(storeIds == null || storeIds.Contains(s.FK_StoreId)));
public async Task<List<Models.Sale>> GetByStoreIdAsync(Guid storeId) =>
await context.Sales.Where(s => s.FK_StoreId == storeId && s.IsActive).ToListAsync();
+7 -7
View File
@@ -71,14 +71,14 @@ public class SaleService(
sale.IsActive,
sale.CreatedAt);
public async Task<List<SaleResponse>> GetAllAsync()
public async Task<PagedResponse<SaleResponse>> GetAllAsync(Guid? rideId, int page, int pageSize)
{
var sales = (await repo.GetAllAsync()).Where(s => s.IsActive).ToList();
IEnumerable<Guid>? storeIds = null;
if (!HasGlobalScope && currentUser.UserId is not null)
{
var accessibleStoreIds = (await scopeAccess.GetAccessibleStoreIdsAsync(currentUser.UserId.Value)).ToHashSet();
sales = sales.Where(s => accessibleStoreIds.Contains(s.FK_StoreId)).ToList();
}
storeIds = await scopeAccess.GetAccessibleStoreIdsAsync(currentUser.UserId.Value);
var total = await repo.GetCountAsync(rideId, storeIds);
var sales = await repo.GetAllAsync(rideId, storeIds, page, pageSize);
var result = new List<SaleResponse>();
foreach (var sale in sales)
@@ -88,7 +88,7 @@ public class SaleService(
var items = await orderRepo.GetItemsByOrderIdAsync(order.Id);
result.Add(ToResponse(sale, order, items));
}
return result;
return new PagedResponse<SaleResponse>(page, pageSize, total, result);
}
public async Task<SaleResponse?> GetByIdAsync(Guid id)
@@ -1,11 +1,9 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.Scheme;
[ApiController]
[Route("api/schemes")]
[Authorize]
public class SchemeController(SchemeService service) : ControllerBase
{
[HttpGet]
@@ -120,7 +118,6 @@ public class SchemeController(SchemeService service) : ControllerBase
[ApiController]
[Route("api/orders")]
[Authorize]
public class SchemeOrderController(SchemeService service) : ControllerBase
{
[HttpPost("{id:guid}/apply-scheme")]
@@ -1,16 +1,14 @@
using Distribution.Infra.Authorization;
using Distribution.Infra.Authorization;
using Distribution.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.Stock;
[ApiController]
[Route("api/stock")]
[Authorize]
public class StockController(StockService service, ICurrentUserService currentUser) : ControllerBase
{
// ── Query ──────────────────────────────────────────────────────────────
// ── Query ──────────────────────────────────────────────────────────────
[HttpGet("balances")]
public async Task<IActionResult> GetBalances(
@@ -46,7 +44,7 @@ public class StockController(StockService service, ICurrentUserService currentUs
return Ok(result);
}
// ── Write ──────────────────────────────────────────────────────────────
// ── Write ──────────────────────────────────────────────────────────────
[HttpPost("transfer")]
public async Task<IActionResult> Transfer(TransferRequest request, CancellationToken ct)
@@ -1,15 +1,12 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.Store.Kyc;
[ApiController]
[Route("api/stores/{storeId:guid}/kyc-documents")]
[Authorize]
public class StoreKycDocumentController(StoreKycDocumentService service) : ControllerBase
{
[HttpGet]
[Authorize(Policy = "stores.view")]
public async Task<IActionResult> GetByStoreId(Guid storeId)
{
var result = await service.GetByStoreIdAsync(storeId);
@@ -17,7 +14,6 @@ public class StoreKycDocumentController(StoreKycDocumentService service) : Contr
}
[HttpPost]
[Authorize(Policy = "stores.update")]
public async Task<IActionResult> Upload(Guid storeId, [FromForm] UploadKycDocumentRequest request, CancellationToken ct)
{
var result = await service.UploadAsync(storeId, request, ct);
+1 -16
View File
@@ -1,20 +1,16 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.Store;
[ApiController]
[Route("api/stores")]
[Authorize]
public class StoreController(StoreService service) : ControllerBase
{
[HttpGet]
[Authorize(Policy = "stores.view")]
public async Task<IActionResult> GetAll() =>
Ok(await service.GetAllAsync());
[HttpGet("{id:guid}")]
[Authorize(Policy = "stores.view")]
public async Task<IActionResult> GetById(Guid id)
{
var result = await service.GetByIdAsync(id);
@@ -22,7 +18,6 @@ public class StoreController(StoreService service) : ControllerBase
}
[HttpPost]
[Authorize(Policy = "stores.create")]
public async Task<IActionResult> Create(CreateStoreRequest request, CancellationToken ct)
{
var result = await service.CreateAsync(request, ct);
@@ -30,7 +25,6 @@ public class StoreController(StoreService service) : ControllerBase
}
[HttpPut("{id:guid}")]
[Authorize(Policy = "stores.update")]
public async Task<IActionResult> Update(Guid id, UpdateStoreRequest request, CancellationToken ct)
{
var result = await service.UpdateAsync(id, request, ct);
@@ -38,7 +32,6 @@ public class StoreController(StoreService service) : ControllerBase
}
[HttpDelete("{id:guid}")]
[Authorize(Policy = "stores.delete")]
public async Task<IActionResult> Delete(Guid id, CancellationToken ct)
{
var deleted = await service.DeleteAsync(id, ct);
@@ -46,7 +39,6 @@ public class StoreController(StoreService service) : ControllerBase
}
[HttpPut("{id:guid}/credit-limit")]
[Authorize(Policy = "stores.credit.update")]
public async Task<IActionResult> UpdateCreditLimit(Guid id, UpdateCreditLimitRequest request, CancellationToken ct)
{
var result = await service.UpdateCreditLimitAsync(id, request, ct);
@@ -54,7 +46,6 @@ public class StoreController(StoreService service) : ControllerBase
}
[HttpPut("{id:guid}/outstanding-balance")]
[Authorize(Policy = "stores.credit.update")]
public async Task<IActionResult> UpdateOutstandingBalance(Guid id, UpdateOutstandingBalanceRequest request, CancellationToken ct)
{
var result = await service.UpdateOutstandingBalanceAsync(id, request, ct);
@@ -62,7 +53,6 @@ public class StoreController(StoreService service) : ControllerBase
}
[HttpGet("{id:guid}/outstanding-balance")]
[Authorize(Policy = "stores.view")]
public async Task<IActionResult> GetOutstandingBalance(Guid id)
{
var result = await service.GetOutstandingBalanceAsync(id);
@@ -70,7 +60,6 @@ public class StoreController(StoreService service) : ControllerBase
}
[HttpPut("{id:guid}/kyc/verify")]
[Authorize(Policy = "stores.kyc.verify")]
public async Task<IActionResult> VerifyKyc(Guid id, VerifyKycRequest request, CancellationToken ct)
{
var result = await service.VerifyKycAsync(id, request, ct);
@@ -78,7 +67,6 @@ public class StoreController(StoreService service) : ControllerBase
}
[HttpPut("{id:guid}/segment")]
[Authorize(Policy = "stores.update")]
public async Task<IActionResult> ClassifySegment(Guid id, ClassifySegmentRequest request, CancellationToken ct)
{
var result = await service.ClassifySegmentAsync(id, request, ct);
@@ -86,7 +74,6 @@ public class StoreController(StoreService service) : ControllerBase
}
[HttpGet("{id:guid}/visit-history")]
[Authorize(Policy = "stores.view")]
public async Task<IActionResult> GetVisitHistory(Guid id)
{
var result = await service.GetVisitHistoryAsync(id);
@@ -94,7 +81,6 @@ public class StoreController(StoreService service) : ControllerBase
}
[HttpGet("{id:guid}/schemes/{schemeId:guid}/eligibility")]
[Authorize(Policy = "stores.view")]
public async Task<IActionResult> CheckSchemeEligibility(Guid id, Guid schemeId)
{
var result = await service.CheckSchemeEligibilityAsync(id, schemeId);
@@ -102,7 +88,6 @@ public class StoreController(StoreService service) : ControllerBase
}
[HttpGet("{id:guid}/dashboard")]
[Authorize(Policy = "stores.view")]
public async Task<IActionResult> GetDashboard(Guid id, [FromQuery] string period)
{
var result = await service.GetDashboardAsync(id, period);
@@ -1,11 +1,9 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.Supplier;
[ApiController]
[Route("api/suppliers")]
[Authorize]
public class SupplierController(SupplierService service) : ControllerBase
{
[HttpGet]
@@ -1,11 +1,9 @@
using Distribution.Infra.Authorization;
using Microsoft.AspNetCore.Authorization;
using Distribution.Infra.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.SupplierClaims;
[ApiController]
[Authorize]
public class SupplierClaimController(SupplierClaimService service, ICurrentUserService currentUser) : ControllerBase
{
[HttpGet("api/supplier-claims/{id:guid}")]
@@ -32,7 +30,6 @@ public class SupplierClaimController(SupplierClaimService service, ICurrentUserS
}
[HttpPost("api/supplier-claims/{id:guid}/approve")]
[Authorize(Policy = "suppliers.claims.approve")]
public async Task<IActionResult> Approve(Guid id, ApproveSupplierClaimRequest request, CancellationToken ct)
{
var userId = currentUser.UserId ?? throw new UnauthorizedAccessException();
@@ -46,7 +43,6 @@ public class SupplierClaimController(SupplierClaimService service, ICurrentUserS
}
[HttpPost("api/supplier-claims/{id:guid}/reject")]
[Authorize(Policy = "suppliers.claims.approve")]
public async Task<IActionResult> Reject(Guid id, RejectSupplierClaimRequest request, CancellationToken ct)
{
var userId = currentUser.UserId ?? throw new UnauthorizedAccessException();
+1 -3
View File
@@ -1,11 +1,9 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.Sync;
[ApiController]
[Route("api/sync")]
[Authorize(Policy = "items.create")]
public class SyncController(SyncService service) : ControllerBase
{
[HttpPost("uoms")]
+1 -8
View File
@@ -1,20 +1,16 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.Uom;
[ApiController]
[Route("api/uoms")]
[Authorize]
public class UomController(UomService service) : ControllerBase
{
[HttpGet]
[Authorize(Policy = "items.view")]
public async Task<IActionResult> GetAll() =>
Ok(await service.GetAllAsync());
[HttpGet("{id:guid}")]
[Authorize(Policy = "items.view")]
public async Task<IActionResult> GetById(Guid id)
{
var result = await service.GetByIdAsync(id);
@@ -22,7 +18,6 @@ public class UomController(UomService service) : ControllerBase
}
[HttpPost]
[Authorize(Policy = "items.create")]
public async Task<IActionResult> Create(CreateUomRequest request, CancellationToken ct)
{
var result = await service.CreateAsync(request, ct);
@@ -30,7 +25,6 @@ public class UomController(UomService service) : ControllerBase
}
[HttpPut("{id:guid}")]
[Authorize(Policy = "items.update")]
public async Task<IActionResult> Update(Guid id, UpdateUomRequest request, CancellationToken ct)
{
var result = await service.UpdateAsync(id, request, ct);
@@ -38,7 +32,6 @@ public class UomController(UomService service) : ControllerBase
}
[HttpDelete("{id:guid}")]
[Authorize(Policy = "items.delete")]
public async Task<IActionResult> Delete(Guid id, CancellationToken ct)
{
var deleted = await service.DeleteAsync(id, ct);
+1 -10
View File
@@ -1,20 +1,16 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.User;
[ApiController]
[Route("api/users")]
[Authorize]
public class UserController(UserService service) : ControllerBase
{
[HttpGet]
[Authorize(Policy = "users.view")]
public async Task<IActionResult> GetAll() =>
Ok(await service.GetAllAsync());
[HttpGet("{id:guid}")]
[Authorize(Policy = "users.view")]
public async Task<IActionResult> GetById(Guid id)
{
var result = await service.GetByIdAsync(id);
@@ -22,7 +18,6 @@ public class UserController(UserService service) : ControllerBase
}
[HttpPost]
[Authorize(Policy = "users.create")]
public async Task<IActionResult> Create(CreateUserRequest request, CancellationToken ct)
{
var result = await service.CreateAsync(request, ct);
@@ -30,7 +25,6 @@ public class UserController(UserService service) : ControllerBase
}
[HttpPut("{id:guid}")]
[Authorize(Policy = "users.update")]
public async Task<IActionResult> Update(Guid id, UpdateUserRequest request, CancellationToken ct)
{
var result = await service.UpdateAsync(id, request, ct);
@@ -38,7 +32,6 @@ public class UserController(UserService service) : ControllerBase
}
[HttpPut("{id:guid}/role")]
[Authorize(Policy = "users.assign_role")]
public async Task<IActionResult> AssignRole(Guid id, AssignUserRoleRequest request, CancellationToken ct)
{
var result = await service.AssignRoleAsync(id, request, ct);
@@ -46,7 +39,6 @@ public class UserController(UserService service) : ControllerBase
}
[HttpPut("{id:guid}/scope")]
[Authorize(Policy = "users.assign_scope")]
public async Task<IActionResult> AssignScope(Guid id, AssignUserScopeRequest request, CancellationToken ct)
{
var updated = await service.AssignScopeAsync(id, request, ct);
@@ -54,7 +46,6 @@ public class UserController(UserService service) : ControllerBase
}
[HttpDelete("{id:guid}")]
[Authorize(Policy = "users.deactivate")]
public async Task<IActionResult> Deactivate(Guid id, CancellationToken ct)
{
var deactivated = await service.DeactivateAsync(id, ct);
@@ -1,20 +1,16 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.Vehicle;
[ApiController]
[Route("api/vehicles")]
[Authorize]
public class VehicleController(VehicleService service) : ControllerBase
{
[HttpGet]
[Authorize(Policy = "vehicles.view")]
public async Task<IActionResult> GetAll() =>
Ok(await service.GetAllAsync());
[HttpGet("{id:guid}")]
[Authorize(Policy = "vehicles.view")]
public async Task<IActionResult> GetById(Guid id)
{
var result = await service.GetByIdAsync(id);
@@ -22,7 +18,6 @@ public class VehicleController(VehicleService service) : ControllerBase
}
[HttpPost]
[Authorize(Policy = "vehicles.create")]
public async Task<IActionResult> Create(CreateVehicleRequest request, CancellationToken ct)
{
var result = await service.CreateAsync(request, ct);
@@ -30,7 +25,6 @@ public class VehicleController(VehicleService service) : ControllerBase
}
[HttpPut("{id:guid}")]
[Authorize(Policy = "vehicles.update")]
public async Task<IActionResult> Update(Guid id, UpdateVehicleRequest request, CancellationToken ct)
{
var result = await service.UpdateAsync(id, request, ct);
@@ -38,7 +32,6 @@ public class VehicleController(VehicleService service) : ControllerBase
}
[HttpDelete("{id:guid}")]
[Authorize(Policy = "vehicles.delete")]
public async Task<IActionResult> Delete(Guid id, CancellationToken ct)
{
var deleted = await service.DeleteAsync(id, ct);
@@ -1,20 +1,16 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Features.Warehouse;
[ApiController]
[Route("api/warehouses")]
[Authorize]
public class WarehouseController(WarehouseService service) : ControllerBase
{
[HttpGet]
[Authorize(Policy = "warehouses.view")]
public async Task<IActionResult> GetAll() =>
Ok(await service.GetAllAsync());
[HttpGet("{id:guid}")]
[Authorize(Policy = "warehouses.view")]
public async Task<IActionResult> GetById(Guid id)
{
var result = await service.GetByIdAsync(id);
@@ -22,7 +18,6 @@ public class WarehouseController(WarehouseService service) : ControllerBase
}
[HttpPost]
[Authorize(Policy = "warehouses.create")]
public async Task<IActionResult> Create(CreateWarehouseRequest request, CancellationToken ct)
{
var result = await service.CreateAsync(request, ct);
@@ -30,7 +25,6 @@ public class WarehouseController(WarehouseService service) : ControllerBase
}
[HttpPut("{id:guid}")]
[Authorize(Policy = "warehouses.update")]
public async Task<IActionResult> Update(Guid id, UpdateWarehouseRequest request, CancellationToken ct)
{
var result = await service.UpdateAsync(id, request, ct);
@@ -38,7 +32,6 @@ public class WarehouseController(WarehouseService service) : ControllerBase
}
[HttpDelete("{id:guid}")]
[Authorize(Policy = "warehouses.delete")]
public async Task<IActionResult> Delete(Guid id, CancellationToken ct)
{
var deleted = await service.DeleteAsync(id, ct);
@@ -0,0 +1,14 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
namespace Distribution.Infra.Authorization;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class AuthenticatedAttribute : Attribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
if (context.HttpContext.User.Identity?.IsAuthenticated != true)
context.Result = new UnauthorizedResult();
}
}
@@ -1,11 +1,9 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
namespace Distribution.Infra.Authorization;
[ApiController]
[Route("api/authorization")]
[Authorize]
public class AuthorizationController(
IPermissionService permissionService,
IScopeAccessService scopeAccessService,
@@ -1,24 +1,23 @@
using Microsoft.AspNetCore.Authorization;
//namespace Distribution.Infra.Authorization;
namespace Distribution.Infra.Authorization;
//public class PermissionAuthorizationHandler : AuthorizationHandler<PermissionRequirement>
//{
// private readonly IPermissionService _permissionService;
public class PermissionAuthorizationHandler : AuthorizationHandler<PermissionRequirement>
{
private readonly IPermissionService _permissionService;
// public PermissionAuthorizationHandler(IPermissionService permissionService)
// {
// _permissionService = permissionService;
// }
public PermissionAuthorizationHandler(IPermissionService permissionService)
{
_permissionService = permissionService;
}
// protected override async Task HandleRequirementAsync(
// AuthorizationHandlerContext context, PermissionRequirement requirement)
// {
// var roleIdClaim = context.User.FindFirst("role_id")?.Value;
// if (!Guid.TryParse(roleIdClaim, out var roleId))
// return;
protected override async Task HandleRequirementAsync(
AuthorizationHandlerContext context, PermissionRequirement requirement)
{
var roleIdClaim = context.User.FindFirst("role_id")?.Value;
if (!Guid.TryParse(roleIdClaim, out var roleId))
return;
if (await _permissionService.HasPermissionAsync(roleId, requirement.PermissionCode))
context.Succeed(requirement);
}
}
// if (await _permissionService.HasPermissionAsync(roleId, requirement.PermissionCode))
// context.Succeed(requirement);
// }
//}
@@ -1,32 +1,31 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Options;
//using Microsoft.Extensions.Options;
namespace Distribution.Infra.Authorization;
//namespace Distribution.Infra.Authorization;
public class PermissionPolicyProvider : IAuthorizationPolicyProvider
{
private readonly DefaultAuthorizationPolicyProvider _fallback;
//public class PermissionPolicyProvider : IAuthorizationPolicyProvider
//{
// private readonly DefaultAuthorizationPolicyProvider _fallback;
public PermissionPolicyProvider(IOptions<AuthorizationOptions> options)
{
_fallback = new DefaultAuthorizationPolicyProvider(options);
}
// public PermissionPolicyProvider(IOptions<AuthorizationOptions> options)
// {
// _fallback = new DefaultAuthorizationPolicyProvider(options);
// }
public Task<AuthorizationPolicy> GetDefaultPolicyAsync() => _fallback.GetDefaultPolicyAsync();
// public Task<AuthorizationPolicy> GetDefaultPolicyAsync() => _fallback.GetDefaultPolicyAsync();
public Task<AuthorizationPolicy?> GetFallbackPolicyAsync() => _fallback.GetFallbackPolicyAsync();
// public Task<AuthorizationPolicy?> GetFallbackPolicyAsync() => _fallback.GetFallbackPolicyAsync();
public Task<AuthorizationPolicy?> GetPolicyAsync(string policyName)
{
if (policyName.Contains('.'))
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.AddRequirements(new PermissionRequirement(policyName))
.Build();
return Task.FromResult<AuthorizationPolicy?>(policy);
}
// public Task<AuthorizationPolicy?> GetPolicyAsync(string policyName)
// {
// if (policyName.Contains('.'))
// {
// var policy = new AuthorizationPolicyBuilder()
// .RequireAuthenticatedUser()
// .AddRequirements(new PermissionRequirement(policyName))
// .Build();
// return Task.FromResult<AuthorizationPolicy?>(policy);
// }
return _fallback.GetPolicyAsync(policyName);
}
}
// return _fallback.GetPolicyAsync(policyName);
// }
//}
@@ -1,13 +1,12 @@
using Microsoft.AspNetCore.Authorization;
//namespace Distribution.Infra.Authorization;
namespace Distribution.Infra.Authorization;
//public class PermissionRequirement : IAuthorizationRequirement
//{
// public string PermissionCode { get; }
public class PermissionRequirement : IAuthorizationRequirement
{
public string PermissionCode { get; }
public PermissionRequirement(string permissionCode)
{
PermissionCode = permissionCode;
}
}
// public PermissionRequirement(string permissionCode)
// {
// PermissionCode = permissionCode;
// }
//}
+44
View File
@@ -0,0 +1,44 @@
using System.IdentityModel.Tokens.Jwt;
using System.Text;
using Microsoft.IdentityModel.Tokens;
namespace Distribution.Middleware;
public class AuthMiddleware(RequestDelegate next, IConfiguration config)
{
public async Task InvokeAsync(HttpContext context)
{
var token = context.Request.Headers.Authorization
.ToString()
.Replace("Bearer ", "", StringComparison.OrdinalIgnoreCase)
.Trim();
if (!string.IsNullOrEmpty(token))
{
var key = config["Jwt:Key"];
if (!string.IsNullOrEmpty(key))
{
try
{
var principal = new JwtSecurityTokenHandler().ValidateToken(token,
new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key)),
ValidateIssuer = true,
ValidIssuer = config["Jwt:Issuer"],
ValidateAudience = true,
ValidAudience = config["Jwt:Audience"],
ValidateLifetime = true,
ClockSkew = TimeSpan.FromMinutes(1)
}, out _);
context.User = principal;
}
catch { }
}
}
await next(context);
}
}
+20 -64
View File
@@ -1,6 +1,6 @@
using System.Security.Claims;
using Distribution.Data;
using Distribution.Features.Auth;
using Distribution.Middleware;
using Distribution.Features.Brand;
using Distribution.Features.Category;
using Distribution.Features.DeliveryRide;
@@ -34,10 +34,7 @@ using Distribution.Features.Stock;
using Distribution.Features.Warehouse;
using Distribution.Infra.Authorization;
using Distribution.Infra.UoW;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
var builder = WebApplication.CreateBuilder(args);
@@ -126,67 +123,26 @@ builder.Services.AddScoped<IPermissionRepository, PermissionRepository>();
builder.Services.AddScoped<IScopeRepository, ScopeRepository>();
builder.Services.AddScoped<IPermissionService, PermissionService>();
builder.Services.AddScoped<IScopeAccessService, ScopeAccessService>();
builder.Services.AddScoped<IAuthorizationHandler, PermissionAuthorizationHandler>();
builder.Services.AddSingleton<IAuthorizationPolicyProvider, PermissionPolicyProvider>();
// ── Auth Service connector ───────────────────────────────────────────────────
builder.Services.Configure<AuthServiceOptions>(
builder.Configuration.GetSection(AuthServiceOptions.SectionName));
var jwtSection = builder.Configuration.GetSection("Jwt");
var jwtPublicKeyXml = jwtSection["PublicKey"] ?? throw new InvalidOperationException("Jwt:PublicKey is not configured.");
var jwtSigningKey = new RsaSecurityKey(RsaXmlKeyReader.ReadPublicKey(jwtPublicKeyXml));
var authServiceBaseUrl = builder.Configuration[$"{AuthServiceOptions.SectionName}:BaseUrl"];
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = jwtSection["Issuer"],
ValidateAudience = true,
ValidAudience = jwtSection["Audience"],
ValidateIssuerSigningKey = true,
IssuerSigningKey = jwtSigningKey,
ValidateLifetime = true,
ClockSkew = TimeSpan.FromMinutes(1)
};
builder.Services.AddHttpClient<AuthServiceClient>(client =>
{
if (!string.IsNullOrWhiteSpace(authServiceBaseUrl))
client.BaseAddress = new Uri(authServiceBaseUrl);
});
// AuthHex issues the token and owns its claim shape; this app's authorization (permission
// policies, role-level checks) runs against the *local* Role/Permission tables, so every
// validated token is mapped back to a local User/Role once here, not duplicated per
// endpoint. A token for a user with no matching local User record is rejected outright —
// login via AuthHex does not implicitly provision a local user.
options.Events = new JwtBearerEvents
{
OnTokenValidated = async context =>
{
var subClaim = context.Principal?.FindFirst("sub")?.Value
?? context.Principal?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
builder.Services.AddScoped<IAuthServiceClient, AuthServiceClient>();
if (!Guid.TryParse(subClaim, out var userId))
{
context.Fail("Token is missing a valid user id claim.");
return;
}
var userRepo = context.HttpContext.RequestServices.GetRequiredService<IUserRepository>();
var user = await userRepo.GetByIdAsync(userId);
if (user is null || !user.IsActive)
{
context.Fail("User not found or inactive.");
return;
}
var identity = (ClaimsIdentity)context.Principal!.Identity!;
identity.AddClaim(new Claim("username", user.Username));
identity.AddClaim(new Claim("role_id", user.FK_RoleId.ToString()));
identity.AddClaim(new Claim("role_level", (user.Role?.Level ?? 0).ToString()));
}
};
});
builder.Services.AddAuthorization();
var allowedOrigins = builder.Configuration.GetSection("Cors:AllowedOrigins").Get<string[]>() ?? [];
builder.Services.AddCors(options =>
options.AddDefaultPolicy(policy =>
policy.WithOrigins(allowedOrigins)
.AllowAnyHeader()
.AllowAnyMethod()));
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
@@ -202,9 +158,9 @@ if (app.Environment.IsDevelopment())
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
//app.UseHttpsRedirection();
app.UseCors();
app.UseMiddleware<AuthMiddleware>();
app.MapControllers();
app.Run();
+4
View File
@@ -1,4 +1,5 @@
{
"Urls": "http://localhost:5089",
"ConnectionStrings": {
"DefaultConnection": "Server=187.127.102.190;Port=3306;Database=distribution;User=kalana;Password=kalana@hexdive"
},
@@ -9,6 +10,9 @@
}
},
"AllowedHosts": "*",
"Cors": {
"AllowedOrigins": [ "http://localhost:3000" ]
},
"Jwt": {
"PublicKey": "<RSAKeyValue><Modulus>1LlNkMBQNdpXJiDal7XMxkG/3ad+YBsMCuY9JD/abHMzniFXtQlovjfbeaaHJ0v1kvSo9731CJ0YC1qhPU5rPQwZwxOWZ9BOBZlMDghONdjOH/HyCUbb5Z18ibqc0QenFSnEYz+jkVZiayj8DV/+VUe+eKzpQTlU6aWtHvlbwfuXaDu+QvFlpLJ7/m8na+0s2nYhLX8Wfi4C/2AoNaYhFkIwYhMMGoaSHuIoQ5R6181Rh0gKvYopRW+IpTD5RV8bXV3AM6zOcoisOifBYROHwA5ZZpoHXuTvHYmPWW8kL8PKme7BwBldPi8KrJUroRE+WXA87aAA5Wtt1oxePcXvhQ==</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>",
"Audience": "AuthHexClient",
+17 -4
View File
@@ -64,16 +64,29 @@ When `FulfillImmediately = false`, no stock movement — order stays Pending for
**Feature folder:** `Features/Payment/`
### API Endpoints
### Sales API Endpoints
| Method | Route | Who | Description |
|---|---|---|---|
| POST | `/api/sales/{id}/payments` | Driver/Agent | Record a payment against a sale |
| GET | `/api/sales/{id}/payments` | Any | List all payments for a sale |
| GET | `/api/sales` | Any | List sales — supports `?rideId=`, `?page=`, `?pageSize=` query params |
| POST | `/api/sales/{saleId:guid}/payments` | Driver/Agent | Record a payment against a sale |
| GET | `/api/sales/{saleId:guid}/payments` | Any | List all payments for a sale |
**`GET /api/sales` filter params:**
| Param | Type | Description |
|---|---|---|
| `rideId` | `Guid?` | Filter to sales created on a specific ride |
| `page` | `int` | Page number (default 1) |
| `pageSize` | `int` | Items per page (default 20) |
Returns `PagedResponse<SaleResponse>`: `{ page, pageSize, total, items }`.
Scope rules still apply: non-global users only see sales for stores in their scope.
### Payment Creation Logic
On `POST /api/sales/{id}/payments`:
On `POST /api/sales/{saleId:guid}/payments`:
1. Validate sale exists and is not `Cancelled`.
2. Add `Payment` record (`FK_SaleId`, `FK_StoreId` from sale, `Amount`, `Method`, `CollectedAt`, `CollectedBy`).
3. Sum all payments for this sale. Recalculate `Sale.PaymentStatus`:
+4 -4
View File
@@ -100,8 +100,8 @@ One migration needed:
### New
| Method | Route | Permission | Notes |
|---|---|---|---|
| POST | /api/stores/{id}/kyc-documents | stores.update | Multipart upload, appends a KYC document |
| GET | /api/stores/{id}/kyc-documents | stores.view | Lists documents for a store |
| POST | /api/stores/{storeId:guid}/kyc-documents | stores.update | Multipart upload, appends a KYC document |
| GET | /api/stores/{storeId:guid}/kyc-documents | stores.view | Lists documents for a store |
| PUT | /api/stores/{id}/kyc/verify | stores.kyc.verify | Sets KycStatus, body carries `status` (Verified/Rejected) + optional `notes` |
| PUT | /api/stores/{id}/credit-limit | stores.credit.update | Requires Supervisor (roleLevel ≥ 20) or above |
| PUT | /api/stores/{id}/outstanding-balance | stores.credit.update | Manual adjustment until Finance owns this; same permission as credit-limit |
@@ -115,7 +115,7 @@ One migration needed:
## Request / Response Shapes
### POST `/api/stores/{id}/kyc-documents` (multipart/form-data)
### POST `/api/stores/{storeId:guid}/kyc-documents` (multipart/form-data)
Fields: `documentType` (string), `file` (binary).
Response:
@@ -247,7 +247,7 @@ ever belong to a Store and have no independent lifecycle.
### KYC upload logic (pseudo)
```
POST /api/stores/{id}/kyc-documents — multipart (documentType, file):
POST /api/stores/{storeId:guid}/kyc-documents — multipart (documentType, file):
1. Load Store by id, reject if missing/inactive or caller lacks store scope
2. Ensure wwwroot/uploads/kyc/{storeId}/ exists
3. Save file as {guid}.{ext}, record FileReference = "uploads/kyc/{storeId}/{guid}.{ext}"
+1 -1
View File
@@ -347,7 +347,7 @@ GET /api/schemes/eligible?storeId=...&orderId=...
**Apply it:**
```
POST /api/orders/{orderId}/apply-scheme
POST /api/orders/{id}/apply-scheme
{ "schemeId": "..." }
```
Adds an `OrderItem` line (`Soap-500ml`, qty 2, `UnitPrice = 0`) and a `SchemeRedemption` with
+1 -1
View File
@@ -46,7 +46,7 @@ home until this step. There is no invoice, payment, or GL entity for warehouse-f
| Function reference | Endpoint intent |
|---|---|
| `createInvoice(orderId)` | `POST /api/orders/{id}/invoice` — the warehouse-fulfilled counterpart to [06_sale_ride.md](./06_sale_ride.md)'s field invoice creation |
| `recordPayment(invoiceId, amount, method)` | `POST /api/invoices/{id}/payments` |
| `recordPayment(invoiceId, amount, method)` | Current implemented path is `POST /api/sales/{saleId:guid}/payments` for field-sale collections; `POST /api/invoices/{id}/payments` remains the target endpoint for this future invoice-based finance step |
| `postToGeneralLedger(transactionId)` | Internal — triggered on invoice/payment/credit-note events |
| `generateTaxInvoice(invoiceId, jurisdiction)` | `GET /api/invoices/{id}/tax-invoice?jurisdiction=...` |
| `getFinancialReport(entityId, period, reportType)` | `GET /api/finance/reports?entityId=...&period=...&reportType=...` |
+4 -4
View File
@@ -143,9 +143,9 @@ here — see `SRS_User_Management_Module.pdf`.
| Function | What it does | Status |
|---|---|---|
| `createStore(storeData, kycDocs)` | Onboards a new outlet with required KYC. | Partially implemented - store create exists, and KYC documents can now be uploaded/listed via separate `/api/stores/{id}/kyc-documents` endpoints rather than inline on create. |
| `uploadStoreKycDocument(storeId, documentType, file)` | Appends a KYC document to a store. | Implemented — `POST /api/stores/{id}/kyc-documents`. |
| `listStoreKycDocuments(storeId)` | Lists KYC documents for a store. | Implemented — `GET /api/stores/{id}/kyc-documents`. |
| `createStore(storeData, kycDocs)` | Onboards a new outlet with required KYC. | Partially implemented - store create exists, and KYC documents can now be uploaded/listed via separate `/api/stores/{storeId:guid}/kyc-documents` endpoints rather than inline on create. |
| `uploadStoreKycDocument(storeId, documentType, file)` | Appends a KYC document to a store. | Implemented — `POST /api/stores/{storeId:guid}/kyc-documents`. |
| `listStoreKycDocuments(storeId)` | Lists KYC documents for a store. | Implemented — `GET /api/stores/{storeId:guid}/kyc-documents`. |
| `verifyStoreKyc(storeId)` | Marks KYC as verified/rejected by an approver. | Implemented - `PUT /api/stores/{id}/kyc/verify` updates `Store.KycStatus`. |
| `updateStoreCreditLimit(storeId, newLimit, approverId)` | Changes a store's credit limit, subject to approval authority. | Implemented - `PUT /api/stores/{id}/credit-limit` updates `Store.CreditLimit` and requires the `stores.credit.update` permission plus role level >= 20. |
| `updateStoreOutstandingBalance(storeId, amount, reason?)` | Manually adjusts the cached outstanding balance. | Implemented — `PUT /api/stores/{id}/outstanding-balance`. |
@@ -281,7 +281,7 @@ here — see `SRS_User_Management_Module.pdf`.
| Function | What it does | Status |
|---|---|---|
| `createInvoice(orderId)` | Generates a formal invoice from a fulfilled order. | Not implemented — no `Invoice` entity exists; `Sale`/`Order` remain the billable record. |
| `recordPayment(invoiceId, amount, method)` | Logs a payment/collection against an invoice. | Implemented against `Sale` rather than an invoice — `POST /api/sales/{saleId}/payments` (`Features/Payment`). Each payment sums against `Sale.TotalAmount` and updates `Sale.PaymentStatus` (`Pending`/`PartiallyPaid`/`Paid`); rejects (400) if the sale is `Cancelled`. `Method` is `Cash` or `Credit` (`Models/Enums.cs`). |
| `recordPayment(invoiceId, amount, method)` | Logs a payment/collection against an invoice. | Implemented against `Sale` rather than an invoice — `POST /api/sales/{saleId:guid}/payments` (`Features/Payment`). Each payment sums against `Sale.TotalAmount` and updates `Sale.PaymentStatus` (`Pending`/`PartiallyPaid`/`Paid`); rejects (400) if the sale is `Cancelled`. `Method` is `Cash` or `Credit` (`Models/Enums.cs`). |
| `postToGeneralLedger(transactionId)` | Posts a financial transaction to the GL. | Not implemented — no GL/ledger entity exists. |
| `generateTaxInvoice(invoiceId, jurisdiction)` | Produces a tax-compliant invoice (GST/VAT e-invoicing). | Not implemented. |
| `getFinancialReport(entityId, period, reportType)` | Returns P&L/cash-flow reports scoped to a region/area. | Not implemented. |