261 lines
10 KiB
C#
261 lines
10 KiB
C#
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 user = await _userManager.FindByNameAsync(approvedBy);
|
|
//if (user != null)
|
|
//{
|
|
// bool isApprover2 = await _userManager.IsInRoleAsync(user, "APPROVER2");
|
|
|
|
// if (!isApprover2)
|
|
// {
|
|
// throw new Exception("You have no permission to approve this item.");
|
|
// }
|
|
//}
|
|
|
|
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)
|
|
{
|
|
//var user = await _userManager.FindByNameAsync(canceledBy);
|
|
//if (user != null)
|
|
//{
|
|
// bool isApprover2 = await _userManager.IsInRoleAsync(user, "APPROVER2");
|
|
|
|
// if (!isApprover2)
|
|
// {
|
|
// throw new Exception("You have no permission to cancel this item.");
|
|
// }
|
|
//}
|
|
|
|
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<Infrastructure.Entities.Inventory.MRS> 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 = 1,//Matic Approve for now
|
|
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<Infrastructure.Entities.Inventory.MRS?> 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<MRSPagedResult> 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<string> 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}";
|
|
}
|
|
public async Task<IReadOnlyList<RISSearchResultDto>> SearchRISForReturnAsync(string? risNoQuery, int? projectCodeId, CancellationToken ct)
|
|
{
|
|
var query = _db.RIS
|
|
.Where(r => r.Status == 1);
|
|
|
|
if (projectCodeId.HasValue)
|
|
query = query.Where(r => r.ProjectCodeId == projectCodeId.Value);
|
|
|
|
if (!string.IsNullOrWhiteSpace(risNoQuery))
|
|
query = query.Where(r => r.RISNo.Contains(risNoQuery));
|
|
|
|
return await query
|
|
.Select(r => new RISSearchResultDto
|
|
{
|
|
RISId = r.RISId,
|
|
RISNo = r.RISNo,
|
|
ProjectCodeId = r.ProjectCodeId,
|
|
ProjectCode = r.ProjectCodes.ProjectCode ?? "N/A",
|
|
ProjectName = r.ProjectCodes.ProjectName ?? "N/A",
|
|
DisciplineName = r.Discipline.DisciplineName,
|
|
QtyAvailableToReturn = r.QtyIssued - r.MaterialReturns.Sum(m => m.QtyReturned)
|
|
})
|
|
.Where(r => r.QtyAvailableToReturn > 0)
|
|
.OrderByDescending(r => r.RISId)
|
|
.Take(20)
|
|
.ToListAsync(ct);
|
|
}
|
|
|
|
public async Task<IReadOnlyList<ProjectCodeOptionDto>> GetProjectsWithOpenRISAsync(string? nameQuery, CancellationToken ct)
|
|
{
|
|
var query = _db.RIS
|
|
.Where(r => r.Status == 1 && r.QtyIssued > r.MaterialReturns.Sum(m => m.QtyReturned));
|
|
|
|
if (!string.IsNullOrWhiteSpace(nameQuery))
|
|
query = query.Where(r =>
|
|
r.ProjectCodes.ProjectName.Contains(nameQuery) ||
|
|
r.ProjectCodes.ProjectCode.Contains(nameQuery));
|
|
|
|
return await query
|
|
.Select(r => new ProjectCodeOptionDto
|
|
{
|
|
ProjectCodeId = r.ProjectCodeId,
|
|
ProjectCode = r.ProjectCodes.ProjectCode ?? "N/A",
|
|
ProjectName = r.ProjectCodes.ProjectName ?? "N/A"
|
|
})
|
|
.Distinct()
|
|
.OrderBy(p => p.ProjectName)
|
|
.Take(20)
|
|
.ToListAsync(ct);
|
|
}
|
|
}
|
|
}
|