Add SetActiveStatusAsync, and update DTOs for active status management.
This commit is contained in:
@@ -1,3 +1,5 @@
|
|||||||
|
using Distribution.Models;
|
||||||
|
|
||||||
namespace Distribution.Features.Store;
|
namespace Distribution.Features.Store;
|
||||||
|
|
||||||
public interface IStoreRepository
|
public interface IStoreRepository
|
||||||
@@ -5,6 +7,8 @@ public interface IStoreRepository
|
|||||||
Task<Models.Store?> GetByIdAsync(Guid id);
|
Task<Models.Store?> GetByIdAsync(Guid id);
|
||||||
Task<Models.Store?> GetByIdWithRegionAsync(Guid id);
|
Task<Models.Store?> GetByIdWithRegionAsync(Guid id);
|
||||||
Task<List<Models.Store>> GetAllAsync();
|
Task<List<Models.Store>> GetAllAsync();
|
||||||
|
Task<List<Models.Store>> GetAllAsync(Guid? routeId, KycStatus? kycStatus, string? search, int page, int pageSize);
|
||||||
|
Task<int> GetCountAsync(Guid? routeId, KycStatus? kycStatus, string? search);
|
||||||
Task AddAsync(Models.Store entity);
|
Task AddAsync(Models.Store entity);
|
||||||
void Update(Models.Store entity);
|
void Update(Models.Store entity);
|
||||||
void Remove(Models.Store entity);
|
void Remove(Models.Store entity);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Distribution.Models;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace Distribution.Features.Store;
|
namespace Distribution.Features.Store;
|
||||||
|
|
||||||
@@ -7,8 +8,13 @@ namespace Distribution.Features.Store;
|
|||||||
public class StoreController(StoreService service) : ControllerBase
|
public class StoreController(StoreService service) : ControllerBase
|
||||||
{
|
{
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task<IActionResult> GetAll() =>
|
public async Task<IActionResult> GetAll(
|
||||||
Ok(await service.GetAllAsync());
|
[FromQuery] Guid? routeId,
|
||||||
|
[FromQuery] KycStatus? kycStatus,
|
||||||
|
[FromQuery] string? search,
|
||||||
|
[FromQuery] int page = 1,
|
||||||
|
[FromQuery] int pageSize = 20) =>
|
||||||
|
Ok(await service.GetAllAsync(routeId, kycStatus, search, page, pageSize));
|
||||||
|
|
||||||
[HttpGet("{id:guid}")]
|
[HttpGet("{id:guid}")]
|
||||||
public async Task<IActionResult> GetById(Guid id)
|
public async Task<IActionResult> GetById(Guid id)
|
||||||
@@ -100,4 +106,11 @@ public class StoreController(StoreService service) : ControllerBase
|
|||||||
var result = await service.VerifyKycAsync(id, new VerifyKycRequest(request.KycStatus, null), ct);
|
var result = await service.VerifyKycAsync(id, new VerifyKycRequest(request.KycStatus, null), ct);
|
||||||
return result is null ? NotFound() : Ok(result);
|
return result is null ? NotFound() : Ok(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPatch("{id:guid}/status")]
|
||||||
|
public async Task<IActionResult> PatchStatus(Guid id, PatchStoreStatusRequest request, CancellationToken ct)
|
||||||
|
{
|
||||||
|
var result = await service.SetActiveStatusAsync(id, request.IsActive, ct);
|
||||||
|
return result is null ? NotFound() : Ok(result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,8 @@ public record UpdateStoreRequest(
|
|||||||
decimal Latitude,
|
decimal Latitude,
|
||||||
decimal Longitude,
|
decimal Longitude,
|
||||||
decimal OneTimeAmount,
|
decimal OneTimeAmount,
|
||||||
decimal CreditAmount);
|
decimal CreditAmount,
|
||||||
|
bool IsActive = true);
|
||||||
|
|
||||||
public record StoreResponse(
|
public record StoreResponse(
|
||||||
Guid Id,
|
Guid Id,
|
||||||
@@ -55,4 +56,6 @@ public record SchemeEligibilityResponse(bool Eligible, string? Reason);
|
|||||||
|
|
||||||
public record PatchKycStatusRequest(KycStatus KycStatus);
|
public record PatchKycStatusRequest(KycStatus KycStatus);
|
||||||
|
|
||||||
|
public record PatchStoreStatusRequest(bool IsActive);
|
||||||
|
|
||||||
public record StoreDashboardResponse(Guid StoreId, string Period, int OrderCount, decimal TotalOrderValue, DateTime? LastOrderDate);
|
public record StoreDashboardResponse(Guid StoreId, string Period, int OrderCount, decimal TotalOrderValue, DateTime? LastOrderDate);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Distribution.Data;
|
using Distribution.Data;
|
||||||
|
using Distribution.Models;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace Distribution.Features.Store;
|
namespace Distribution.Features.Store;
|
||||||
@@ -16,6 +17,25 @@ public class StoreRepository(AppDBContext context) : IStoreRepository
|
|||||||
public async Task<List<Models.Store>> GetAllAsync() =>
|
public async Task<List<Models.Store>> GetAllAsync() =>
|
||||||
await context.Stores.ToListAsync();
|
await context.Stores.ToListAsync();
|
||||||
|
|
||||||
|
private static IQueryable<Models.Store> ApplyFilters(
|
||||||
|
IQueryable<Models.Store> query, Guid? routeId, KycStatus? kycStatus, string? search) =>
|
||||||
|
query
|
||||||
|
.Where(s => routeId == null || s.FK_RouteId == routeId)
|
||||||
|
.Where(s => kycStatus == null || s.KycStatus == kycStatus)
|
||||||
|
.Where(s => string.IsNullOrWhiteSpace(search)
|
||||||
|
|| s.StoreName.Contains(search)
|
||||||
|
|| s.OwnerName.Contains(search));
|
||||||
|
|
||||||
|
public async Task<List<Models.Store>> GetAllAsync(Guid? routeId, KycStatus? kycStatus, string? search, int page, int pageSize) =>
|
||||||
|
await ApplyFilters(context.Stores, routeId, kycStatus, search)
|
||||||
|
.OrderBy(s => s.StoreName)
|
||||||
|
.Skip((page - 1) * pageSize)
|
||||||
|
.Take(pageSize)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
public async Task<int> GetCountAsync(Guid? routeId, KycStatus? kycStatus, string? search) =>
|
||||||
|
await ApplyFilters(context.Stores, routeId, kycStatus, search).CountAsync();
|
||||||
|
|
||||||
public async Task AddAsync(Models.Store entity) =>
|
public async Task AddAsync(Models.Store entity) =>
|
||||||
await context.Stores.AddAsync(entity);
|
await context.Stores.AddAsync(entity);
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Distribution.Features.Scheme;
|
using Distribution.Features.Scheme;
|
||||||
|
using Distribution.Features.Stock;
|
||||||
using Distribution.Infra.Authorization;
|
using Distribution.Infra.Authorization;
|
||||||
using Distribution.Infra.UoW;
|
using Distribution.Infra.UoW;
|
||||||
using Distribution.Models;
|
using Distribution.Models;
|
||||||
@@ -23,21 +24,20 @@ public class StoreService(IStoreRepository repo, IUnitOfWork uow, ICurrentUserSe
|
|||||||
new(s.Id, s.FK_RouteId, s.OwnerName, s.StoreName, s.StoreAddress, s.Latitude, s.Longitude, s.OneTimeAmount, s.CreditAmount,
|
new(s.Id, s.FK_RouteId, s.OwnerName, s.StoreName, s.StoreAddress, s.Latitude, s.Longitude, s.OneTimeAmount, s.CreditAmount,
|
||||||
s.CreditLimit, s.OutstandingBalance, s.Segment, s.KycStatus, s.IsActive, s.CreatedAt);
|
s.CreditLimit, s.OutstandingBalance, s.Segment, s.KycStatus, s.IsActive, s.CreatedAt);
|
||||||
|
|
||||||
public async Task<List<StoreResponse>> GetAllAsync()
|
public async Task<PagedResponse<StoreResponse>> GetAllAsync(
|
||||||
|
Guid? routeId, KycStatus? kycStatus, string? search, int page, int pageSize)
|
||||||
{
|
{
|
||||||
var stores = (await repo.GetAllAsync()).Where(s => s.IsActive).ToList();
|
// Deactivated stores stay visible in the list showing Inactive — deactivate never
|
||||||
if (!HasGlobalScope && currentUser.UserId is not null)
|
// removes the row, only marks it (same rule as Orders, see testbug.md #42).
|
||||||
{
|
var total = await repo.GetCountAsync(routeId, kycStatus, search);
|
||||||
var accessibleStoreIds = (await scopeAccess.GetAccessibleStoreIdsAsync(currentUser.UserId.Value)).ToHashSet();
|
var stores = await repo.GetAllAsync(routeId, kycStatus, search, page, pageSize);
|
||||||
stores = stores.Where(s => accessibleStoreIds.Contains(s.Id)).ToList();
|
return new PagedResponse<StoreResponse>(page, pageSize, total, stores.Select(ToResponse).ToList());
|
||||||
}
|
|
||||||
return stores.Select(ToResponse).ToList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<StoreResponse?> GetByIdAsync(Guid id)
|
public async Task<StoreResponse?> GetByIdAsync(Guid id)
|
||||||
{
|
{
|
||||||
var store = await repo.GetByIdAsync(id);
|
var store = await repo.GetByIdAsync(id);
|
||||||
if (store is null || !store.IsActive) return null;
|
if (store is null) return null;
|
||||||
//if (!await HasStoreScopeAsync(id)) return null;
|
//if (!await HasStoreScopeAsync(id)) return null;
|
||||||
return ToResponse(store);
|
return ToResponse(store);
|
||||||
}
|
}
|
||||||
@@ -72,7 +72,7 @@ public class StoreService(IStoreRepository repo, IUnitOfWork uow, ICurrentUserSe
|
|||||||
public async Task<StoreResponse?> UpdateAsync(Guid id, UpdateStoreRequest request, CancellationToken ct = default)
|
public async Task<StoreResponse?> UpdateAsync(Guid id, UpdateStoreRequest request, CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
var store = await repo.GetByIdAsync(id);
|
var store = await repo.GetByIdAsync(id);
|
||||||
if (store is null || !store.IsActive) return null;
|
if (store is null) return null;
|
||||||
if (!await HasStoreScopeAsync(id)) return null;
|
if (!await HasStoreScopeAsync(id)) return null;
|
||||||
store.FK_RouteId = request.RouteId;
|
store.FK_RouteId = request.RouteId;
|
||||||
store.OwnerName = request.OwnerName;
|
store.OwnerName = request.OwnerName;
|
||||||
@@ -82,6 +82,7 @@ public class StoreService(IStoreRepository repo, IUnitOfWork uow, ICurrentUserSe
|
|||||||
store.Longitude = request.Longitude;
|
store.Longitude = request.Longitude;
|
||||||
store.OneTimeAmount = request.OneTimeAmount;
|
store.OneTimeAmount = request.OneTimeAmount;
|
||||||
store.CreditAmount = request.CreditAmount;
|
store.CreditAmount = request.CreditAmount;
|
||||||
|
store.IsActive = request.IsActive;
|
||||||
await uow.BeginAsync(ct);
|
await uow.BeginAsync(ct);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -205,10 +206,32 @@ public class StoreService(IStoreRepository repo, IUnitOfWork uow, ICurrentUserSe
|
|||||||
return ToResponse(store);
|
return ToResponse(store);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<StoreResponse?> SetActiveStatusAsync(Guid id, bool isActive, CancellationToken ct = default)
|
||||||
|
{
|
||||||
|
var store = await repo.GetByIdAsync(id);
|
||||||
|
if (store is null) return null;
|
||||||
|
if (!await HasStoreScopeAsync(id)) return null;
|
||||||
|
|
||||||
|
store.IsActive = isActive;
|
||||||
|
|
||||||
|
await uow.BeginAsync(ct);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
repo.Update(store);
|
||||||
|
await uow.CommitAsync(ct);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
await uow.RollbackAsync(ct);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
return ToResponse(store);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<List<StoreVisitHistoryItem>?> GetVisitHistoryAsync(Guid id)
|
public async Task<List<StoreVisitHistoryItem>?> GetVisitHistoryAsync(Guid id)
|
||||||
{
|
{
|
||||||
var store = await repo.GetByIdAsync(id);
|
var store = await repo.GetByIdAsync(id);
|
||||||
if (store is null || !store.IsActive) return null;
|
if (store is null) return null;
|
||||||
if (!await HasStoreScopeAsync(id)) return null;
|
if (!await HasStoreScopeAsync(id)) return null;
|
||||||
|
|
||||||
var visits = await routeRepo.GetVisitsByStoreIdAsync(id);
|
var visits = await routeRepo.GetVisitsByStoreIdAsync(id);
|
||||||
|
|||||||
Reference in New Issue
Block a user