using CPRNIMS.Domain.Contracts.Common; using CPRNIMS.Domain.Contracts.Inventory; using CPRNIMS.Infrastructure.Database; using CPRNIMS.Infrastructure.Dto.Inventory; using CPRNIMS.Infrastructure.Dto.Inventory.Request; using CPRNIMS.Infrastructure.Entities.Inventory; using Microsoft.EntityFrameworkCore; namespace CPRNIMS.Domain.Services.Inventory { public class MRS : IMRS { private readonly NonInventoryDbContext _db; private readonly ITransactionFacade _transactionFacade; public MRS(NonInventoryDbContext db, ITransactionFacade transactionFacade) { _db = db; _transactionFacade = transactionFacade; } public async Task ApproveAsync(long mrsId, string approvedBy, CancellationToken ct) { var rms = await _db.MRS.FindAsync(mrsId) ?? throw new InvalidOperationException("MRS not found."); if (rms.Status != 0) throw new InvalidOperationException("Only Draft MRS records can be approved."); rms.Status = 1; // Approved rms.ApprovedBy = approvedBy; rms.ApprovedDate = DateTime.Now; await _db.SaveChangesAsync(ct); } public async Task CancelAsync(CancelMRSRequest request,string canceledBy, CancellationToken ct) { await _transactionFacade.ExecuteAsync(async () => { var mrs = await _db.MRS .Include(m => m.Inventory) .FirstOrDefaultAsync(m => m.MRSId == request.MRSId) ?? throw new InvalidOperationException("MRS not found."); if (mrs.Status == 2) throw new InvalidOperationException("MRS is already cancelled."); // Reverse the return: deduct qty back out mrs.Inventory.QtyOut = Math.Max(0m, mrs.Inventory.QtyOut) + mrs.QtyReturned; mrs.Inventory.QtyOnHand = mrs.Inventory.QtyIn - mrs.Inventory.QtyOut; mrs.Reason = request.Reason; mrs.Status = 2; mrs.CanceledDate = DateTime.Now; mrs.CanceledBy = canceledBy; }, ct); } public async Task CreateAsync(CreateMRSRequest dto, string createdBy, CancellationToken ct) { return await _transactionFacade.ExecuteAsync(async () => { var ris = await _db.RIS .Include(r => r.Inventory) .FirstOrDefaultAsync(r => r.RISId == dto.RISId, ct) ?? throw new InvalidOperationException("Referenced RIS not found."); if (dto.QtyReturned > ris.QtyIssued) throw new InvalidOperationException( $"Cannot return more than issued. Issued: {ris.QtyIssued}."); var mrsNo = await GenerateMRSNoAsync(); var mrs = new Infrastructure.Entities.Inventory.MRS { MRSNo = mrsNo, RISId = dto.RISId, InventoryId = ris.InventoryId, ReturnedBy = dto.ReturnedBy, QtyReturned = dto.QtyReturned, Condition = dto.Condition, Remarks = dto.Remarks, Status = 0, CreatedBy = createdBy, CreatedDate = DateTime.Now }; _db.MRS.Add(mrs); var inventory = ris.Inventory; inventory.QtyOut = Math.Max(0m, inventory.QtyOut - dto.QtyReturned); inventory.QtyOnHand = inventory.QtyIn - inventory.QtyOut; var trans = await _db.InventTrans .FirstOrDefaultAsync(t => t.InventoryId == ris.InventoryId && t.IsActive == true, ct)!; _db.InventTransDetails.Add(new InventTransDetail { InventTransId = trans.InventTransId, TransTypeId = 6, QtyIn = dto.QtyReturned, CreatedDate = DateTime.Now, Remarks = $"MRS: {mrsNo} — return from MRS: {ris.RISNo}", IsActive = true }); return mrs; }, ct); } public async Task GetByIdAsync(long mrsId, CancellationToken ct) => await _db.MRS .Include(r => r.Inventory) .Include(r => r.RIS) .FirstOrDefaultAsync(r => r.RISId == mrsId, ct); public async Task GetPagedAsync(MRSFilterDto filter, CancellationToken ct, int? departmentId = null, string? userName = "") { var allowedAllDeptUsers = new[] { "LSKRISUR24", "LSCYNDIZ25", "LSJONTAN25", "LHRIOCAS24" }; bool seeAllDepartments = !string.IsNullOrWhiteSpace(userName) && allowedAllDeptUsers.Contains(userName, StringComparer.OrdinalIgnoreCase); var q = _db.MRS .Include(m => m.RIS) .Include(m => m.Inventory) .AsQueryable(); if (departmentId.HasValue && !seeAllDepartments) { q = q.Where(itd => itd.Inventory.User.DepartmentId == departmentId.Value); } if (!string.IsNullOrWhiteSpace(filter.SearchMRSNo)) q = q.Where(m => m.MRSNo.Contains(filter.SearchMRSNo)); if (filter.RISId.HasValue) q = q.Where(m => m.RISId == filter.RISId.Value); if (filter.Status.HasValue) q = q.Where(m => m.Status == filter.Status.Value); if (filter.DateFrom.HasValue) q = q.Where(m => m.CreatedDate >= filter.DateFrom.Value); if (filter.DateTo.HasValue) q = q.Where(m => m.CreatedDate <= filter.DateTo.Value.AddDays(1)); var total = await q.CountAsync(ct); var data = await q .OrderByDescending(m => m.CreatedDate) .Skip((filter.PageNumber - 1) * filter.PageSize) .Take(filter.PageSize) .Select(m => new MRSPagedDto { MRSId = m.MRSId, MRSNo = m.MRSNo, RISId = m.RISId, RISNo = m.RIS.RISNo, InventoryId = m.InventoryId, ItemName = m.Inventory.InventTrans .SelectMany(t => t.InventTransDetails) .Select(d => d.PRDetails != null ? d.PRDetails.ItemName : "—") .FirstOrDefault() ?? "—", ReturnedBy = m.ReturnedBy, QtyReturned = m.QtyReturned, Condition = m.Condition, Remarks = m.Remarks, Status = m.Status, CreatedBy = m.CreatedBy, CreatedDate = m.CreatedDate, ApprovedBy = m.ApprovedBy, ApprovedDate = m.ApprovedDate }) .ToListAsync(ct); return new MRSPagedResult { Data = data, RecordsTotal = total }; } private async Task GenerateMRSNoAsync() { var year = DateTime.Now.Year; var month = DateTime.Now.Month.ToString("D2"); var count = await _db.MRS.CountAsync(m => m.CreatedDate.Year == year) + 1; return $"MRS-{year}{month}-{count:D4}"; } } }