feat: add cheque books and received cheques management
- Implemented ChequeBooksPage for viewing and filtering cheque books. - Added NewReceivedChequePage for recording received cheques. - Created ReceivedChequesPage for listing and managing received cheques. - Introduced API endpoints for bank accounts, cheque books, and received cheques. - Defined types for payments including bank accounts, cheque books, and received cheques.
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
using Distribution.Models;
|
||||
|
||||
namespace Distribution.Features.ReceivedCheque;
|
||||
|
||||
public interface IReceivedChequeRepository
|
||||
{
|
||||
Task<Models.ReceivedCheque?> GetByIdAsync(Guid id);
|
||||
Task<(List<Models.ReceivedCheque> Items, int TotalCount)> ListAsync(
|
||||
int? companyId, int? branchId, ReceivedChequeStatus? status, ReceivedFromType? receivedFromType,
|
||||
int page, int pageSize);
|
||||
Task AddAsync(Models.ReceivedCheque cheque);
|
||||
void Update(Models.ReceivedCheque cheque);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using Distribution.Models;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Distribution.Features.ReceivedCheque;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/received-cheques")]
|
||||
public class ReceivedChequeController(ReceivedChequeService service) : ControllerBase
|
||||
{
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> List(
|
||||
[FromQuery] int? companyId,
|
||||
[FromQuery] int? branchId,
|
||||
[FromQuery] ReceivedChequeStatus? status,
|
||||
[FromQuery] ReceivedFromType? receivedFromType,
|
||||
[FromQuery] int page = 1,
|
||||
[FromQuery] int pageSize = 50)
|
||||
=> Ok(await service.ListAsync(companyId, branchId, status, receivedFromType, page, pageSize));
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> Create(CreateReceivedChequeRequest request, CancellationToken ct)
|
||||
{
|
||||
try { return Ok(await service.CreateAsync(request, ct)); }
|
||||
catch (InvalidOperationException ex) { return BadRequest(new { error = ex.Message }); }
|
||||
}
|
||||
|
||||
[HttpPut("{id:guid}/status")]
|
||||
public async Task<IActionResult> UpdateStatus(Guid id, UpdateReceivedChequeStatusRequest request, CancellationToken ct)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await service.UpdateStatusAsync(id, request, ct);
|
||||
return result is null ? NotFound() : Ok(result);
|
||||
}
|
||||
catch (InvalidOperationException ex) { return BadRequest(new { error = ex.Message }); }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using Distribution.Features.ChequeBook;
|
||||
using Distribution.Models;
|
||||
|
||||
namespace Distribution.Features.ReceivedCheque;
|
||||
|
||||
public record ReceivedChequeResponse(
|
||||
Guid ReceivedChequeId,
|
||||
int CompanyId,
|
||||
int? BranchId,
|
||||
ReceivedFromType ReceivedFromType,
|
||||
int? ReceivedFromId,
|
||||
string ReceivedFromName,
|
||||
string? DrawerBankName,
|
||||
string? DrawerBankBranch,
|
||||
string? AccountHolderName,
|
||||
string ChequeNo,
|
||||
DateTime ChequeDate,
|
||||
decimal Amount,
|
||||
DateTime ReceivedDate,
|
||||
string? ReferenceType,
|
||||
int? ReferenceId,
|
||||
string? Notes,
|
||||
string? CreatedBy,
|
||||
ReceivedChequeStatus Status,
|
||||
Guid? DepositBankAccountId,
|
||||
DateTime? DepositDate);
|
||||
|
||||
public record CreateReceivedChequeRequest(
|
||||
int CompanyId,
|
||||
int? BranchId,
|
||||
ReceivedFromType ReceivedFromType,
|
||||
int? ReceivedFromId,
|
||||
string ReceivedFromName,
|
||||
string? DrawerBankName,
|
||||
string? DrawerBankBranch,
|
||||
string? AccountHolderName,
|
||||
string ChequeNo,
|
||||
DateTime ChequeDate,
|
||||
decimal Amount,
|
||||
DateTime ReceivedDate,
|
||||
string? ReferenceType,
|
||||
int? ReferenceId,
|
||||
string? Notes,
|
||||
string? CreatedBy);
|
||||
|
||||
public record UpdateReceivedChequeStatusRequest(
|
||||
ReceivedChequeStatusAction Action,
|
||||
Guid? DepositBankAccountId,
|
||||
DateTime? DepositDate,
|
||||
string? Notes,
|
||||
string? PerformedBy);
|
||||
@@ -0,0 +1,34 @@
|
||||
using Distribution.Data;
|
||||
using Distribution.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Distribution.Features.ReceivedCheque;
|
||||
|
||||
public class ReceivedChequeRepository(AppDBContext context) : IReceivedChequeRepository
|
||||
{
|
||||
public async Task<Models.ReceivedCheque?> GetByIdAsync(Guid id) =>
|
||||
await context.ReceivedCheques.FirstOrDefaultAsync(c => c.Id == id);
|
||||
|
||||
public async Task<(List<Models.ReceivedCheque> Items, int TotalCount)> ListAsync(
|
||||
int? companyId, int? branchId, ReceivedChequeStatus? status, ReceivedFromType? receivedFromType,
|
||||
int page, int pageSize)
|
||||
{
|
||||
var query = context.ReceivedCheques.AsQueryable();
|
||||
if (companyId is not null) query = query.Where(c => c.CompanyId == companyId);
|
||||
if (branchId is not null) query = query.Where(c => c.BranchId == branchId);
|
||||
if (status is not null) query = query.Where(c => c.Status == status);
|
||||
if (receivedFromType is not null) query = query.Where(c => c.ReceivedFromType == receivedFromType);
|
||||
|
||||
var total = await query.CountAsync();
|
||||
var items = await query
|
||||
.OrderByDescending(c => c.ReceivedDate)
|
||||
.Skip((page - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.ToListAsync();
|
||||
return (items, total);
|
||||
}
|
||||
|
||||
public async Task AddAsync(Models.ReceivedCheque cheque) => await context.ReceivedCheques.AddAsync(cheque);
|
||||
|
||||
public void Update(Models.ReceivedCheque cheque) => context.ReceivedCheques.Update(cheque);
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
using Distribution.Features.BankAccount;
|
||||
using Distribution.Features.ChequeBook;
|
||||
using Distribution.Infra.UoW;
|
||||
using Distribution.Models;
|
||||
|
||||
namespace Distribution.Features.ReceivedCheque;
|
||||
|
||||
public class ReceivedChequeService(
|
||||
IReceivedChequeRepository repo,
|
||||
IBankAccountRepository bankAccountRepo,
|
||||
IUnitOfWork uow)
|
||||
{
|
||||
private static ReceivedChequeResponse ToResponse(Models.ReceivedCheque c) =>
|
||||
new(c.Id, c.CompanyId, c.BranchId, c.ReceivedFromType, c.ReceivedFromId, c.ReceivedFromName,
|
||||
c.DrawerBankName, c.DrawerBankBranch, c.AccountHolderName,
|
||||
c.ChequeNo, c.ChequeDate, c.Amount, c.ReceivedDate,
|
||||
c.ReferenceType, c.ReferenceId, c.Notes, c.CreatedBy,
|
||||
c.Status, c.FK_DepositBankAccountId, c.DepositDate);
|
||||
|
||||
public async Task<PagedResult<ReceivedChequeResponse>> ListAsync(
|
||||
int? companyId, int? branchId, ReceivedChequeStatus? status, ReceivedFromType? receivedFromType,
|
||||
int page, int pageSize)
|
||||
{
|
||||
if (page < 1) page = 1;
|
||||
if (pageSize < 1 || pageSize > 200) pageSize = 50;
|
||||
|
||||
var (items, total) = await repo.ListAsync(companyId, branchId, status, receivedFromType, page, pageSize);
|
||||
return new PagedResult<ReceivedChequeResponse>(
|
||||
items.Select(ToResponse).ToList(), total, page, pageSize);
|
||||
}
|
||||
|
||||
public async Task<ReceivedChequeResponse> CreateAsync(CreateReceivedChequeRequest request, CancellationToken ct = default)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(request.ReceivedFromName))
|
||||
throw new InvalidOperationException("ReceivedFromName is required");
|
||||
if (string.IsNullOrWhiteSpace(request.ChequeNo))
|
||||
throw new InvalidOperationException("ChequeNo is required");
|
||||
if (request.Amount <= 0)
|
||||
throw new InvalidOperationException("Amount must be greater than zero");
|
||||
|
||||
var cheque = new Models.ReceivedCheque
|
||||
{
|
||||
CompanyId = request.CompanyId,
|
||||
BranchId = request.BranchId,
|
||||
ReceivedFromType = request.ReceivedFromType,
|
||||
ReceivedFromId = request.ReceivedFromId,
|
||||
ReceivedFromName = request.ReceivedFromName,
|
||||
DrawerBankName = request.DrawerBankName,
|
||||
DrawerBankBranch = request.DrawerBankBranch,
|
||||
AccountHolderName = request.AccountHolderName,
|
||||
ChequeNo = request.ChequeNo,
|
||||
ChequeDate = request.ChequeDate,
|
||||
Amount = request.Amount,
|
||||
ReceivedDate = request.ReceivedDate,
|
||||
ReferenceType = request.ReferenceType,
|
||||
ReferenceId = request.ReferenceId,
|
||||
Notes = request.Notes,
|
||||
CreatedBy = request.CreatedBy,
|
||||
Status = ReceivedChequeStatus.Received
|
||||
};
|
||||
|
||||
await repo.AddAsync(cheque);
|
||||
await uow.CommitAsync(ct);
|
||||
return ToResponse(cheque);
|
||||
}
|
||||
|
||||
public async Task<ReceivedChequeResponse?> UpdateStatusAsync(Guid id, UpdateReceivedChequeStatusRequest request, CancellationToken ct = default)
|
||||
{
|
||||
var cheque = await repo.GetByIdAsync(id);
|
||||
if (cheque is null) return null;
|
||||
|
||||
switch (request.Action)
|
||||
{
|
||||
case ReceivedChequeStatusAction.Deposit:
|
||||
if (cheque.Status != ReceivedChequeStatus.Received)
|
||||
throw new InvalidOperationException("Only received cheques can be deposited");
|
||||
if (request.DepositBankAccountId is null)
|
||||
throw new InvalidOperationException("DepositBankAccountId is required when depositing");
|
||||
if (request.DepositDate is null)
|
||||
throw new InvalidOperationException("DepositDate is required when depositing");
|
||||
var bankAccount = await bankAccountRepo.GetByIdAsync(request.DepositBankAccountId.Value);
|
||||
if (bankAccount is null || bankAccount.AccountType != BankAccountType.Bank)
|
||||
throw new InvalidOperationException("DepositBankAccountId does not reference a valid bank account");
|
||||
cheque.Status = ReceivedChequeStatus.Deposited;
|
||||
cheque.FK_DepositBankAccountId = request.DepositBankAccountId;
|
||||
cheque.DepositDate = request.DepositDate;
|
||||
break;
|
||||
|
||||
case ReceivedChequeStatusAction.Clear:
|
||||
if (cheque.Status != ReceivedChequeStatus.Deposited)
|
||||
throw new InvalidOperationException("Only deposited cheques can be cleared");
|
||||
cheque.Status = ReceivedChequeStatus.Cleared;
|
||||
break;
|
||||
|
||||
case ReceivedChequeStatusAction.Return:
|
||||
if (cheque.Status != ReceivedChequeStatus.Deposited)
|
||||
throw new InvalidOperationException("Only deposited cheques can be returned");
|
||||
cheque.Status = ReceivedChequeStatus.Returned;
|
||||
break;
|
||||
|
||||
case ReceivedChequeStatusAction.Cancel:
|
||||
if (cheque.Status != ReceivedChequeStatus.Received)
|
||||
throw new InvalidOperationException("Only received cheques can be cancelled");
|
||||
cheque.Status = ReceivedChequeStatus.Cancelled;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(request.Notes))
|
||||
cheque.Notes = request.Notes;
|
||||
|
||||
repo.Update(cheque);
|
||||
await uow.CommitAsync(ct);
|
||||
return ToResponse(cheque);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user