diff --git a/Distribution/Features/Store/IStoreRepository.cs b/Distribution/Features/Store/IStoreRepository.cs index dc3f81b..296ce50 100644 --- a/Distribution/Features/Store/IStoreRepository.cs +++ b/Distribution/Features/Store/IStoreRepository.cs @@ -7,8 +7,8 @@ public interface IStoreRepository Task GetByIdAsync(Guid id); Task GetByIdWithRegionAsync(Guid id); Task> GetAllAsync(); - Task> GetAllAsync(Guid? routeId, KycStatus? kycStatus, string? search, int page, int pageSize); - Task GetCountAsync(Guid? routeId, KycStatus? kycStatus, string? search); + Task> GetAllAsync(Guid? routeId, string? search, int page, int pageSize); + Task GetCountAsync(Guid? routeId, string? search); Task AddAsync(Models.Store entity); void Update(Models.Store entity); void Remove(Models.Store entity); diff --git a/Distribution/Features/Store/Kyc/IStoreKycDocumentRepository.cs b/Distribution/Features/Store/Kyc/IStoreKycDocumentRepository.cs deleted file mode 100644 index b7169ec..0000000 --- a/Distribution/Features/Store/Kyc/IStoreKycDocumentRepository.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Distribution.Features.Store.Kyc; - -public interface IStoreKycDocumentRepository -{ - Task> GetByStoreIdAsync(Guid storeId); - Task AddAsync(Models.StoreKycDocument entity); -} diff --git a/Distribution/Features/Store/Kyc/StoreKycDocumentController.cs b/Distribution/Features/Store/Kyc/StoreKycDocumentController.cs deleted file mode 100644 index 0377f57..0000000 --- a/Distribution/Features/Store/Kyc/StoreKycDocumentController.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Microsoft.AspNetCore.Mvc; - -namespace Distribution.Features.Store.Kyc; - -[ApiController] -[Route("api/stores/{storeId:guid}/kyc-documents")] -public class StoreKycDocumentController(StoreKycDocumentService service) : ControllerBase -{ - [HttpGet] - public async Task GetByStoreId(Guid storeId) - { - var result = await service.GetByStoreIdAsync(storeId); - return result is null ? NotFound() : Ok(result); - } - - [HttpPost] - public async Task Upload(Guid storeId, [FromForm] UploadKycDocumentRequest request, CancellationToken ct) - { - var result = await service.UploadAsync(storeId, request, ct); - return result is null ? NotFound() : Ok(result); - } -} diff --git a/Distribution/Features/Store/Kyc/StoreKycDocumentDTOs.cs b/Distribution/Features/Store/Kyc/StoreKycDocumentDTOs.cs deleted file mode 100644 index 06ada8c..0000000 --- a/Distribution/Features/Store/Kyc/StoreKycDocumentDTOs.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Distribution.Features.Store.Kyc; - -public record UploadKycDocumentRequest(string DocumentType, IFormFile File); - -public record StoreKycDocumentResponse(Guid Id, Guid StoreId, string DocumentType, string FileReference, DateTime UploadedAt); diff --git a/Distribution/Features/Store/Kyc/StoreKycDocumentRepository.cs b/Distribution/Features/Store/Kyc/StoreKycDocumentRepository.cs deleted file mode 100644 index 8a4ae13..0000000 --- a/Distribution/Features/Store/Kyc/StoreKycDocumentRepository.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Distribution.Data; -using Microsoft.EntityFrameworkCore; - -namespace Distribution.Features.Store.Kyc; - -public class StoreKycDocumentRepository(AppDBContext context) : IStoreKycDocumentRepository -{ - public async Task> GetByStoreIdAsync(Guid storeId) => - await context.StoreKycDocuments.Where(d => d.FK_StoreId == storeId && d.IsActive).ToListAsync(); - - public async Task AddAsync(Models.StoreKycDocument entity) => - await context.StoreKycDocuments.AddAsync(entity); -} diff --git a/Distribution/Features/Store/Kyc/StoreKycDocumentService.cs b/Distribution/Features/Store/Kyc/StoreKycDocumentService.cs deleted file mode 100644 index 1470cd9..0000000 --- a/Distribution/Features/Store/Kyc/StoreKycDocumentService.cs +++ /dev/null @@ -1,71 +0,0 @@ -using Distribution.Infra.Authorization; -using Distribution.Infra.UoW; - -namespace Distribution.Features.Store.Kyc; - -public class StoreKycDocumentService( - IStoreKycDocumentRepository repo, - IStoreRepository storeRepo, - IUnitOfWork uow, - ICurrentUserService currentUser, - IScopeAccessService scopeAccess, - IWebHostEnvironment env) -{ - private const int GlobalScopeRoleLevel = 50; - - private bool HasGlobalScope => currentUser.RoleLevel >= GlobalScopeRoleLevel; - - private async Task HasStoreScopeAsync(Guid storeId) - { - if (HasGlobalScope || currentUser.UserId is null) return HasGlobalScope; - return await scopeAccess.HasStoreScopeAsync(currentUser.UserId.Value, storeId); - } - - private static StoreKycDocumentResponse ToResponse(Models.StoreKycDocument d) => - new(d.Id, d.FK_StoreId, d.DocumentType, d.FileReference, d.UploadedAt); - - public async Task?> GetByStoreIdAsync(Guid storeId) - { - if (!await HasStoreScopeAsync(storeId)) return null; - var docs = await repo.GetByStoreIdAsync(storeId); - return docs.Select(ToResponse).ToList(); - } - - public async Task UploadAsync(Guid storeId, UploadKycDocumentRequest request, CancellationToken ct = default) - { - var store = await storeRepo.GetByIdAsync(storeId); - if (store is null || !store.IsActive) return null; - if (!await HasStoreScopeAsync(storeId)) return null; - - var uploadsRoot = Path.Combine(env.ContentRootPath, "wwwroot", "uploads", "kyc", storeId.ToString()); - Directory.CreateDirectory(uploadsRoot); - var fileName = $"{Guid.NewGuid()}{Path.GetExtension(request.File.FileName)}"; - var filePath = Path.Combine(uploadsRoot, fileName); - await using (var stream = new FileStream(filePath, FileMode.Create)) - { - await request.File.CopyToAsync(stream, ct); - } - var fileReference = $"uploads/kyc/{storeId}/{fileName}"; - - var doc = new Models.StoreKycDocument - { - FK_StoreId = storeId, - DocumentType = request.DocumentType, - FileReference = fileReference, - UploadedAt = DateTime.Now - }; - - await uow.BeginAsync(ct); - try - { - await repo.AddAsync(doc); - await uow.CommitAsync(ct); - } - catch - { - await uow.RollbackAsync(ct); - throw; - } - return ToResponse(doc); - } -} diff --git a/Distribution/Features/Store/StoreController.cs b/Distribution/Features/Store/StoreController.cs index fd5a1b3..11235e2 100644 --- a/Distribution/Features/Store/StoreController.cs +++ b/Distribution/Features/Store/StoreController.cs @@ -10,11 +10,10 @@ public class StoreController(StoreService service) : ControllerBase [HttpGet] public async Task GetAll( [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)); + Ok(await service.GetAllAsync(routeId, search, page, pageSize)); [HttpGet("{id:guid}")] public async Task GetById(Guid id) @@ -65,13 +64,6 @@ public class StoreController(StoreService service) : ControllerBase return result is null ? NotFound() : Ok(result); } - [HttpPut("{id:guid}/kyc/verify")] - public async Task VerifyKyc(Guid id, VerifyKycRequest request, CancellationToken ct) - { - var result = await service.VerifyKycAsync(id, request, ct); - return result is null ? NotFound() : Ok(result); - } - [HttpPut("{id:guid}/segment")] public async Task ClassifySegment(Guid id, ClassifySegmentRequest request, CancellationToken ct) { @@ -100,13 +92,6 @@ public class StoreController(StoreService service) : ControllerBase return result is null ? NotFound() : Ok(result); } - [HttpPatch("{id:guid}/kyc-status")] - public async Task PatchKycStatus(Guid id, PatchKycStatusRequest request, CancellationToken ct) - { - var result = await service.VerifyKycAsync(id, new VerifyKycRequest(request.KycStatus, null), ct); - return result is null ? NotFound() : Ok(result); - } - [HttpPatch("{id:guid}/status")] public async Task PatchStatus(Guid id, PatchStoreStatusRequest request, CancellationToken ct) { diff --git a/Distribution/Features/Store/StoreDTOs.cs b/Distribution/Features/Store/StoreDTOs.cs index 3609c34..a9f0ffc 100644 --- a/Distribution/Features/Store/StoreDTOs.cs +++ b/Distribution/Features/Store/StoreDTOs.cs @@ -4,29 +4,34 @@ namespace Distribution.Features.Store; public record CreateStoreRequest( Guid RouteId, - string OwnerName, - string StoreName, - string StoreAddress, - decimal Latitude, - decimal Longitude, - decimal OneTimeAmount, - decimal CreditAmount); - -public record UpdateStoreRequest( - Guid RouteId, - string OwnerName, + string? OwnerName, string StoreName, string StoreAddress, decimal Latitude, decimal Longitude, decimal OneTimeAmount, decimal CreditAmount, - bool IsActive = true); + decimal CreditLimit = 0, + string? ContactNumber = null); + +public record UpdateStoreRequest( + Guid RouteId, + string? OwnerName, + string StoreName, + string StoreAddress, + decimal Latitude, + decimal Longitude, + decimal OneTimeAmount, + decimal CreditAmount, + bool IsActive = true, + decimal? CreditLimit = null, + string? ContactNumber = null); public record StoreResponse( Guid Id, Guid RouteId, - string OwnerName, + string? OwnerName, + string? ContactNumber, string StoreName, string StoreAddress, decimal Latitude, @@ -36,7 +41,6 @@ public record StoreResponse( decimal CreditLimit, decimal OutstandingBalance, string? Segment, - KycStatus KycStatus, bool IsActive, DateTime CreatedAt); @@ -44,8 +48,6 @@ public record UpdateCreditLimitRequest(decimal NewLimit, Guid ApproverId); public record UpdateOutstandingBalanceRequest(decimal NewBalance); -public record VerifyKycRequest(KycStatus Status, string? Notes); - public record ClassifySegmentRequest(string Segment); public record OutstandingBalanceResponse(Guid StoreId, decimal CreditLimit, decimal OutstandingBalance, decimal AvailableCredit); @@ -54,8 +56,6 @@ public record StoreVisitHistoryItem(Guid VisitId, DateTime VisitDate, Guid RepUs public record SchemeEligibilityResponse(bool Eligible, string? Reason); -public record PatchKycStatusRequest(KycStatus KycStatus); - public record PatchStoreStatusRequest(bool IsActive); public record StoreDashboardResponse(Guid StoreId, string Period, int OrderCount, decimal TotalOrderValue, DateTime? LastOrderDate); diff --git a/Distribution/Features/Store/StoreRepository.cs b/Distribution/Features/Store/StoreRepository.cs index db56e9a..a9fcff6 100644 --- a/Distribution/Features/Store/StoreRepository.cs +++ b/Distribution/Features/Store/StoreRepository.cs @@ -18,23 +18,22 @@ public class StoreRepository(AppDBContext context) : IStoreRepository await context.Stores.ToListAsync(); private static IQueryable ApplyFilters( - IQueryable query, Guid? routeId, KycStatus? kycStatus, string? search) => + IQueryable query, Guid? routeId, 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)); + || (s.OwnerName != null && s.OwnerName.Contains(search))); - public async Task> GetAllAsync(Guid? routeId, KycStatus? kycStatus, string? search, int page, int pageSize) => - await ApplyFilters(context.Stores, routeId, kycStatus, search) + public async Task> GetAllAsync(Guid? routeId, string? search, int page, int pageSize) => + await ApplyFilters(context.Stores, routeId, search) .OrderBy(s => s.StoreName) .Skip((page - 1) * pageSize) .Take(pageSize) .ToListAsync(); - public async Task GetCountAsync(Guid? routeId, KycStatus? kycStatus, string? search) => - await ApplyFilters(context.Stores, routeId, kycStatus, search).CountAsync(); + public async Task GetCountAsync(Guid? routeId, string? search) => + await ApplyFilters(context.Stores, routeId, search).CountAsync(); public async Task AddAsync(Models.Store entity) => await context.Stores.AddAsync(entity); diff --git a/Distribution/Features/Store/StoreService.cs b/Distribution/Features/Store/StoreService.cs index e7a2383..9d41b36 100644 --- a/Distribution/Features/Store/StoreService.cs +++ b/Distribution/Features/Store/StoreService.cs @@ -21,16 +21,16 @@ public class StoreService(IStoreRepository repo, IUnitOfWork uow, ICurrentUserSe } private static StoreResponse ToResponse(Models.Store s) => - 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); + new(s.Id, s.FK_RouteId, s.OwnerName, s.ContactNumber, s.StoreName, s.StoreAddress, s.Latitude, s.Longitude, s.OneTimeAmount, s.CreditAmount, + s.CreditLimit, s.OutstandingBalance, s.Segment, s.IsActive, s.CreatedAt); public async Task> GetAllAsync( - Guid? routeId, KycStatus? kycStatus, string? search, int page, int pageSize) + Guid? routeId, string? search, int page, int pageSize) { // Deactivated stores stay visible in the list showing Inactive — deactivate never // removes the row, only marks it (same rule as Orders, see testbug.md #42). - var total = await repo.GetCountAsync(routeId, kycStatus, search); - var stores = await repo.GetAllAsync(routeId, kycStatus, search, page, pageSize); + var total = await repo.GetCountAsync(routeId, search); + var stores = await repo.GetAllAsync(routeId, search, page, pageSize); return new PagedResponse(page, pageSize, total, stores.Select(ToResponse).ToList()); } @@ -48,12 +48,14 @@ public class StoreService(IStoreRepository repo, IUnitOfWork uow, ICurrentUserSe { FK_RouteId = request.RouteId, OwnerName = request.OwnerName, + ContactNumber = request.ContactNumber, StoreName = request.StoreName, StoreAddress = request.StoreAddress, Latitude = request.Latitude, Longitude = request.Longitude, OneTimeAmount = request.OneTimeAmount, - CreditAmount = request.CreditAmount + CreditAmount = request.CreditAmount, + CreditLimit = request.CreditLimit }; await uow.BeginAsync(ct); try @@ -76,12 +78,14 @@ public class StoreService(IStoreRepository repo, IUnitOfWork uow, ICurrentUserSe if (!await HasStoreScopeAsync(id)) return null; store.FK_RouteId = request.RouteId; store.OwnerName = request.OwnerName; + store.ContactNumber = request.ContactNumber; store.StoreName = request.StoreName; store.StoreAddress = request.StoreAddress; store.Latitude = request.Latitude; store.Longitude = request.Longitude; store.OneTimeAmount = request.OneTimeAmount; store.CreditAmount = request.CreditAmount; + if (request.CreditLimit is not null) store.CreditLimit = request.CreditLimit.Value; store.IsActive = request.IsActive; await uow.BeginAsync(ct); try @@ -165,27 +169,6 @@ public class StoreService(IStoreRepository repo, IUnitOfWork uow, ICurrentUserSe if (!await HasStoreScopeAsync(id)) return null; return new OutstandingBalanceResponse(store.Id, store.CreditLimit, store.OutstandingBalance, store.CreditLimit - store.OutstandingBalance); } - - public async Task VerifyKycAsync(Guid id, VerifyKycRequest request, CancellationToken ct = default) - { - var store = await repo.GetByIdAsync(id); - if (store is null || !store.IsActive) return null; - if (!await HasStoreScopeAsync(id)) return null; - store.KycStatus = request.Status; - await uow.BeginAsync(ct); - try - { - repo.Update(store); - await uow.CommitAsync(ct); - } - catch - { - await uow.RollbackAsync(ct); - throw; - } - return ToResponse(store); - } - public async Task ClassifySegmentAsync(Guid id, ClassifySegmentRequest request, CancellationToken ct = default) { var store = await repo.GetByIdAsync(id); diff --git a/Distribution/Models/Enums.cs b/Distribution/Models/Enums.cs index 2497221..b99ea98 100644 --- a/Distribution/Models/Enums.cs +++ b/Distribution/Models/Enums.cs @@ -4,7 +4,6 @@ namespace Distribution.Models { Warehouse, Vehicle, External } - public enum KycStatus { Pending, Verified, Rejected } public enum MovementType { Sale, CustomerReturn, Transfer, Damage, Adjustment, Load, PurchaseReceipt, PurchaseReturn } diff --git a/Distribution/Models/Store.cs b/Distribution/Models/Store.cs index 819ed9e..0339cd1 100644 --- a/Distribution/Models/Store.cs +++ b/Distribution/Models/Store.cs @@ -4,7 +4,8 @@ namespace Distribution.Models { public class Store : Template { - public String OwnerName { get; set; } + public String? OwnerName { get; set; } + public String? ContactNumber { get; set; } public String StoreName { get; set; } public String StoreAddress { get; set; } public decimal Latitude { get; set; } = 0; @@ -14,7 +15,6 @@ namespace Distribution.Models public decimal CreditLimit { get; set; } = 0; public decimal OutstandingBalance { get; set; } = 0; public String? Segment { get; set; } //Gold ,silver,key Account,high risk - public KycStatus KycStatus { get; set; } = KycStatus.Pending; public Guid FK_RouteId { get; set; } [ForeignKey("FK_RouteId")] diff --git a/Distribution/Models/StoreKycDocument.cs b/Distribution/Models/StoreKycDocument.cs deleted file mode 100644 index 54a1407..0000000 --- a/Distribution/Models/StoreKycDocument.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.ComponentModel.DataAnnotations.Schema; - -namespace Distribution.Models -{ - public class StoreKycDocument : Template - { - public Guid FK_StoreId { get; set; } - [ForeignKey("FK_StoreId")] - public Store Store { get; set; } - public String DocumentType { get; set; } - public String FileReference { get; set; } - public DateTime UploadedAt { get; set; } = DateTime.Now; - } -} diff --git a/distribution_fe/lib/api/stores.ts b/distribution_fe/lib/api/stores.ts index c086c77..e4e33ac 100644 --- a/distribution_fe/lib/api/stores.ts +++ b/distribution_fe/lib/api/stores.ts @@ -2,11 +2,9 @@ import { api } from '@/lib/api' import type { PaginatedResponse, StoreResponse, - KycDocumentResponse, OutstandingBalanceResponse, StoreDashboardResponse, StoreVisitHistoryItem, - KycStatus, } from '@/types/api' import type { PageParams } from './master' @@ -15,7 +13,8 @@ import type { PageParams } from './master' export interface StoreBody { routeId: string - ownerName: string + ownerName?: string | null + contactNumber?: string | null storeName: string storeAddress: string latitude: number @@ -28,7 +27,6 @@ export interface StoreBody { export interface StoreListParams extends PageParams { routeId?: string - kycStatus?: KycStatus search?: string } @@ -53,16 +51,6 @@ export const stores = { dashboard: (id: string, period?: string) => api.get(`/stores/${id}/dashboard`, { params: { period } }).then(r => r.data), - - kycDocuments: (id: string) => - api.get(`/stores/${id}/kyc-documents`).then(r => r.data), - - uploadKycDocument: (id: string, documentType: string, fileReference: string) => - api.post(`/stores/${id}/kyc-documents`, { documentType, fileReference }).then(r => r.data), - - updateKycStatus: (id: string, kycStatus: KycStatus) => - api.patch(`/stores/${id}/kyc-status`, { kycStatus }).then(r => r.data), - visitHistory: (id: string) => api.get(`/stores/${id}/visit-history`).then(r => r.data), }