PR item addition in existing PRNo
This commit is contained in:
parent
84bcc7aaa7
commit
d23347c299
@ -31,6 +31,8 @@ namespace CPRNIMS.Domain.Contracts.PR
|
|||||||
Task<List<PRDto>> GetApproverName(PRDto PRDto);
|
Task<List<PRDto>> GetApproverName(PRDto PRDto);
|
||||||
Task<List<PRDto>> GetApproverNameByPRNo(PRDto PRDto);
|
Task<List<PRDto>> GetApproverNameByPRNo(PRDto PRDto);
|
||||||
Task<List<ProjectCodes>> GetProjectCodes(PRDto pRDto);
|
Task<List<ProjectCodes>> GetProjectCodes(PRDto pRDto);
|
||||||
|
Task<List<RemovedPR>> GetRemovedPR(PRDto pRDto);
|
||||||
|
Task<List<ApprovedPR>> GetApprovedPR(PRDto pRDto);
|
||||||
Task<MessageResponse> PRItemRemoval(PRDto pRDto);
|
Task<MessageResponse> PRItemRemoval(PRDto pRDto);
|
||||||
Task<PRDetails> PostPRApproveReject(PRDto PRDto);
|
Task<PRDetails> PostPRApproveReject(PRDto PRDto);
|
||||||
Task<PRDetails> PostPutReceiving(PRDto PRDto);
|
Task<PRDetails> PostPutReceiving(PRDto PRDto);
|
||||||
@ -39,7 +41,6 @@ namespace CPRNIMS.Domain.Contracts.PR
|
|||||||
Task<PRDetails> PostPutDeniedItem(PRDto PRDto);
|
Task<PRDetails> PostPutDeniedItem(PRDto PRDto);
|
||||||
Task<AlternativeOfferDetails> PutSupplierAlterOffer(PRDto pRDto);
|
Task<AlternativeOfferDetails> PutSupplierAlterOffer(PRDto pRDto);
|
||||||
Task<ResponseObject> PostPutProjectCode(PRDto prDto);
|
Task<ResponseObject> PostPutProjectCode(PRDto prDto);
|
||||||
Task<List<RemovedPR>> GetRemovedPR(PRDto pRDto);
|
Task<ResponseObject> PostItemInPR(PRDto dto);
|
||||||
Task<List<ApprovedPR>> GetApprovedPR(PRDto pRDto);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -135,7 +135,8 @@ namespace CPRNIMS.Domain.Services.Items
|
|||||||
public async Task<List<ItemList>> GetItemList(ItemCodeDto itemCode)
|
public async Task<List<ItemList>> GetItemList(ItemCodeDto itemCode)
|
||||||
{
|
{
|
||||||
var allItems = await _dbContext.ItemList
|
var allItems = await _dbContext.ItemList
|
||||||
.FromSqlRaw($"EXEC GetItemList @UserId = '{itemCode.UserId}'")
|
.FromSqlRaw($"EXEC GetItemList @UserId",
|
||||||
|
new SqlParameter("@UserId",itemCode.UserId))
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
return allItems ?? new List<ItemList>();
|
return allItems ?? new List<ItemList>();
|
||||||
|
|||||||
@ -4,16 +4,16 @@ using CPRNIMS.Infrastructure.Dto.PR;
|
|||||||
using CPRNIMS.Infrastructure.Entities.Common;
|
using CPRNIMS.Infrastructure.Entities.Common;
|
||||||
using CPRNIMS.Infrastructure.Entities.Purchasing;
|
using CPRNIMS.Infrastructure.Entities.Purchasing;
|
||||||
using CPRNIMS.Infrastructure.Entities.SMTP;
|
using CPRNIMS.Infrastructure.Entities.SMTP;
|
||||||
|
using CPRNIMS.Infrastructure.Models.Common;
|
||||||
using Microsoft.Data.SqlClient;
|
using Microsoft.Data.SqlClient;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using static CPRNIMS.Domain.Services.OutputParamMessage;
|
using Newtonsoft.Json.Linq;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Newtonsoft.Json.Linq;
|
using static CPRNIMS.Domain.Services.OutputParamMessage;
|
||||||
using CPRNIMS.Infrastructure.Models.Common;
|
|
||||||
|
|
||||||
namespace CPRNIMS.Domain.Services.PR
|
namespace CPRNIMS.Domain.Services.PR
|
||||||
{
|
{
|
||||||
@ -25,6 +25,37 @@ namespace CPRNIMS.Domain.Services.PR
|
|||||||
_dbContext = dbContext;
|
_dbContext = dbContext;
|
||||||
}
|
}
|
||||||
#region Get
|
#region Get
|
||||||
|
public async Task<List<NotificationById>> GetNotificationById(PRDto PRDto)
|
||||||
|
{
|
||||||
|
var allItems = await _dbContext.NotificationByIds
|
||||||
|
.FromSqlRaw("EXEC GetNotificationById @UserId,@PRDetailsId,@AppsModuleId",
|
||||||
|
new SqlParameter("@UserId", PRDto.UserId),
|
||||||
|
new SqlParameter("@PRDetailsId", PRDto.PRDetailsId),
|
||||||
|
new SqlParameter("@AppsModuleId", PRDto.AppsModuleId))
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
return allItems ?? new List<NotificationById>();
|
||||||
|
}
|
||||||
|
public async Task<List<ProjectCodes>> GetProjectCodes(PRDto pRDto)
|
||||||
|
{
|
||||||
|
return await _dbContext.ProjectCodes.ToListAsync();
|
||||||
|
}
|
||||||
|
public async Task<List<RemovedPR>> GetRemovedPR(PRDto pRDto)
|
||||||
|
{
|
||||||
|
var allItems = await _dbContext.RemovedPRs
|
||||||
|
.FromSqlRaw("EXEC GetRemovedPR @UserId",
|
||||||
|
new SqlParameter("@UserId", pRDto.UserId)).ToListAsync();
|
||||||
|
|
||||||
|
return allItems ?? new List<RemovedPR>();
|
||||||
|
}
|
||||||
|
public async Task<List<ApprovedPR>> GetApprovedPR(PRDto pRDto)
|
||||||
|
{
|
||||||
|
var allItems = await _dbContext.ApprovedPrs
|
||||||
|
.FromSqlRaw("EXEC GetApprovedPR @UserId",
|
||||||
|
new SqlParameter("@UserId", pRDto.UserId)).ToListAsync();
|
||||||
|
|
||||||
|
return allItems ?? new List<ApprovedPR>();
|
||||||
|
}
|
||||||
public async Task<List<Infrastructure.Entities.Purchasing.PRList>> GetAllPR(PRDto PRDto)
|
public async Task<List<Infrastructure.Entities.Purchasing.PRList>> GetAllPR(PRDto PRDto)
|
||||||
{
|
{
|
||||||
var allItems = await _dbContext.PRLists
|
var allItems = await _dbContext.PRLists
|
||||||
@ -202,90 +233,58 @@ namespace CPRNIMS.Domain.Services.PR
|
|||||||
#region Post Put
|
#region Post Put
|
||||||
public async Task<PRDetails> PostPRApproveReject(PRDto PRDto)
|
public async Task<PRDetails> PostPRApproveReject(PRDto PRDto)
|
||||||
{
|
{
|
||||||
try
|
await _dbContext.Database
|
||||||
{
|
.ExecuteSqlRawAsync("EXEC PostPRApproveReject @UserId, @ItemNo, @Status, @PRDetailsId, @Remarks",
|
||||||
await _dbContext.Database
|
new SqlParameter("@ItemNo", PRDto.ItemNo != null ? PRDto.ItemNo : 0L),
|
||||||
.ExecuteSqlRawAsync("EXEC PostPRApproveReject @UserId, @ItemNo, @Status, @PRDetailsId, @Remarks",
|
new SqlParameter("@UserId", PRDto.UserId),
|
||||||
new SqlParameter("@ItemNo", PRDto.ItemNo != null ? PRDto.ItemNo : 0L),
|
new SqlParameter("@Status", PRDto.Status),
|
||||||
new SqlParameter("@UserId", PRDto.UserId),
|
new SqlParameter("@PRDetailsId", PRDto.PRDetailsId),
|
||||||
new SqlParameter("@Status", PRDto.Status),
|
new SqlParameter("@Remarks", PRDto.Remarks ?? "N/A"));
|
||||||
new SqlParameter("@PRDetailsId", PRDto.PRDetailsId),
|
return new PRDetails();
|
||||||
new SqlParameter("@Remarks", PRDto.Remarks ?? "N/A"));
|
|
||||||
return new PRDetails();
|
|
||||||
}
|
|
||||||
catch (SqlException ex)
|
|
||||||
{
|
|
||||||
ex.ToString();
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
public async Task<PRDetails> PutItemDetail(PRDto PRDto)
|
public async Task<PRDetails> PutItemDetail(PRDto PRDto)
|
||||||
{
|
{
|
||||||
try
|
await _dbContext.Database
|
||||||
{
|
.ExecuteSqlRawAsync($"EXEC PutPRItemDetail @UserId, @ItemLocalId, @UOMId, @ItemColorId," +
|
||||||
await _dbContext.Database
|
$"@Qty,@ItemCategoryId,@PRDetailsId,@Remarks,@ItemName,@ItemDescription",
|
||||||
.ExecuteSqlRawAsync($"EXEC PutPRItemDetail @UserId, @ItemLocalId, @UOMId, @ItemColorId," +
|
new SqlParameter("@PRDetailsId", PRDto.PRDetailsId != null ? PRDto.PRDetailsId : 0L),
|
||||||
$"@Qty,@ItemCategoryId,@PRDetailsId,@Remarks,@ItemName,@ItemDescription",
|
new SqlParameter("@ItemLocalId", PRDto.ItemLocalId),
|
||||||
new SqlParameter("@PRDetailsId", PRDto.PRDetailsId != null ? PRDto.PRDetailsId : 0L),
|
new SqlParameter("@UOMId", PRDto.UOMId),
|
||||||
new SqlParameter("@ItemLocalId", PRDto.ItemLocalId),
|
new SqlParameter("@ItemColorId", PRDto.ItemColorId),
|
||||||
new SqlParameter("@UOMId", PRDto.UOMId),
|
new SqlParameter("@UserId", PRDto.UserId),
|
||||||
new SqlParameter("@ItemColorId", PRDto.ItemColorId),
|
new SqlParameter("@Qty", PRDto.Qty),
|
||||||
new SqlParameter("@UserId", PRDto.UserId),
|
new SqlParameter("@ItemCategoryId", PRDto.ItemCategoryId),
|
||||||
new SqlParameter("@Qty", PRDto.Qty),
|
new SqlParameter("@Remarks", PRDto.Remarks),
|
||||||
new SqlParameter("@ItemCategoryId", PRDto.ItemCategoryId),
|
new SqlParameter("@ItemName", PRDto.ItemName),
|
||||||
new SqlParameter("@Remarks", PRDto.Remarks),
|
new SqlParameter("@ItemDescription", PRDto.ItemDescription));
|
||||||
new SqlParameter("@ItemName", PRDto.ItemName),
|
return new PRDetails();
|
||||||
new SqlParameter("@ItemDescription", PRDto.ItemDescription));
|
|
||||||
return new PRDetails();
|
|
||||||
}
|
|
||||||
catch (SqlException ex)
|
|
||||||
{
|
|
||||||
ex.ToString();
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
public async Task<PRDetails> PostPutDeniedItem(PRDto PRDto)
|
public async Task<PRDetails> PostPutDeniedItem(PRDto PRDto)
|
||||||
{
|
{
|
||||||
try
|
await _dbContext.Database
|
||||||
{
|
|
||||||
await _dbContext.Database
|
|
||||||
.ExecuteSqlRawAsync("EXEC PostPutDeniedItem @UserId,@PRDetailsId,@Remarks",
|
.ExecuteSqlRawAsync("EXEC PostPutDeniedItem @UserId,@PRDetailsId,@Remarks",
|
||||||
new SqlParameter("@UserId", PRDto.UserId),
|
new SqlParameter("@UserId", PRDto.UserId),
|
||||||
new SqlParameter("@PRDetailsId", PRDto.PRDetailsId),
|
new SqlParameter("@PRDetailsId", PRDto.PRDetailsId),
|
||||||
new SqlParameter("@Remarks", PRDto.Remarks ?? "N/A"));
|
new SqlParameter("@Remarks", PRDto.Remarks ?? "N/A"));
|
||||||
return new PRDetails();
|
return new PRDetails();
|
||||||
}
|
|
||||||
catch (SqlException ex)
|
|
||||||
{
|
|
||||||
ex.ToString();
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
public async Task<PRDetails> PostPutReceiving(PRDto PRDto)
|
public async Task<PRDetails> PostPutReceiving(PRDto PRDto)
|
||||||
{
|
{
|
||||||
try
|
await _dbContext.Database
|
||||||
{
|
.ExecuteSqlRawAsync($"EXEC PostPutReceiving @UserId, @PONo, @POTypeId, @EmailAddress, @DRNo, @DocTypeId, @QuantityReceived,@RRNo,@PRDetailsId,@Remarks,@ReceivedDate,@IsCompleted",
|
||||||
await _dbContext.Database
|
new SqlParameter("@UserId", PRDto.UserId),
|
||||||
.ExecuteSqlRawAsync($"EXEC PostPutReceiving @UserId, @PONo, @POTypeId, @EmailAddress, @DRNo, @DocTypeId, @QuantityReceived,@RRNo,@PRDetailsId,@Remarks,@ReceivedDate,@IsCompleted",
|
new SqlParameter("@PONo", PRDto.PONo),
|
||||||
new SqlParameter("@UserId", PRDto.UserId),
|
new SqlParameter("@POTypeId", PRDto.POTypeId),
|
||||||
new SqlParameter("@PONo", PRDto.PONo),
|
new SqlParameter("@EmailAddress", PRDto.EmailAddress),
|
||||||
new SqlParameter("@POTypeId", PRDto.POTypeId),
|
new SqlParameter("@DRNo", PRDto.DRNo),
|
||||||
new SqlParameter("@EmailAddress", PRDto.EmailAddress),
|
new SqlParameter("@DocTypeId", PRDto.DocTypeId),
|
||||||
new SqlParameter("@DRNo", PRDto.DRNo),
|
new SqlParameter("@QuantityReceived", PRDto.QuantityReceived),
|
||||||
new SqlParameter("@DocTypeId", PRDto.DocTypeId),
|
new SqlParameter("@RRNo", PRDto.RRNo),
|
||||||
new SqlParameter("@QuantityReceived", PRDto.QuantityReceived),
|
new SqlParameter("@PRDetailsId", PRDto.PRDetailsId),
|
||||||
new SqlParameter("@RRNo", PRDto.RRNo),
|
new SqlParameter("@Remarks", PRDto.Remarks ?? "N/A"),
|
||||||
new SqlParameter("@PRDetailsId", PRDto.PRDetailsId),
|
new SqlParameter("@ReceivedDate", PRDto.ReceivedDate),
|
||||||
new SqlParameter("@Remarks", PRDto.Remarks ?? "N/A"),
|
new SqlParameter("@IsCompleted", PRDto.IsCompleted));
|
||||||
new SqlParameter("@ReceivedDate", PRDto.ReceivedDate),
|
return new PRDetails();
|
||||||
new SqlParameter("@IsCompleted", PRDto.IsCompleted));
|
|
||||||
return new PRDetails();
|
|
||||||
}
|
|
||||||
catch (SqlException ex)
|
|
||||||
{
|
|
||||||
ex.ToString();
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
public async Task<PRDetails> PutPOClose(PRDto PRDto)
|
public async Task<PRDetails> PutPOClose(PRDto PRDto)
|
||||||
{
|
{
|
||||||
@ -310,21 +309,14 @@ namespace CPRNIMS.Domain.Services.PR
|
|||||||
new SqlParameter("@CanvassDetailId", pRDto.CanvassDetailId));
|
new SqlParameter("@CanvassDetailId", pRDto.CanvassDetailId));
|
||||||
return new AlternativeOfferDetails();
|
return new AlternativeOfferDetails();
|
||||||
}
|
}
|
||||||
|
private async Task<bool> IsUsingAsync(int projectCodeId)
|
||||||
public async Task<List<NotificationById>> GetNotificationById(PRDto PRDto)
|
|
||||||
{
|
{
|
||||||
var allItems = await _dbContext.NotificationByIds
|
return await (from pr in _dbContext.PRs
|
||||||
.FromSqlRaw("EXEC GetNotificationById @UserId,@PRDetailsId,@AppsModuleId",
|
join pod in _dbContext.PODetails on pr.PRNo equals pod.PRNo
|
||||||
new SqlParameter("@UserId", PRDto.UserId),
|
where pr.ProjectCodeId == projectCodeId
|
||||||
new SqlParameter("@PRDetailsId", PRDto.PRDetailsId),
|
&& !pod.IsRemoved
|
||||||
new SqlParameter("@AppsModuleId", PRDto.AppsModuleId))
|
&& pr.IsActive
|
||||||
.ToListAsync();
|
select pr).AnyAsync();
|
||||||
|
|
||||||
return allItems ?? new List<NotificationById>();
|
|
||||||
}
|
|
||||||
public async Task<List<ProjectCodes>> GetProjectCodes(PRDto pRDto)
|
|
||||||
{
|
|
||||||
return await _dbContext.ProjectCodes.ToListAsync();
|
|
||||||
}
|
}
|
||||||
public async Task<MessageResponse> PRItemRemoval(PRDto prDto)
|
public async Task<MessageResponse> PRItemRemoval(PRDto prDto)
|
||||||
{
|
{
|
||||||
@ -396,40 +388,27 @@ namespace CPRNIMS.Domain.Services.PR
|
|||||||
success = true
|
success = true
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
private async Task<bool> IsUsingAsync(int projectCodeId)
|
|
||||||
|
public async Task<ResponseObject> PostItemInPR(PRDto dto)
|
||||||
{
|
{
|
||||||
try
|
var (messCode, message) = CreateOutputParams();
|
||||||
|
|
||||||
|
await _dbContext.Database.ExecuteSqlRawAsync(
|
||||||
|
"EXEC PostItemInPR @UserId,@ItemNo,@Qty,@PRNo,@MessCode OUTPUT,@Message OUTPUT",
|
||||||
|
new SqlParameter("@UserId", dto.UserId),
|
||||||
|
new SqlParameter("@ItemNo", dto.ItemNo),
|
||||||
|
new SqlParameter("@Qty", dto.Qty),
|
||||||
|
new SqlParameter("@PRNo", dto.PRNo),
|
||||||
|
messCode,
|
||||||
|
message
|
||||||
|
);
|
||||||
|
|
||||||
|
var response = new ResponseObject
|
||||||
{
|
{
|
||||||
return await (from pr in _dbContext.PRs
|
message = message.Value?.ToString(),
|
||||||
join pod in _dbContext.PODetails on pr.PRNo equals pod.PRNo
|
messCode = Convert.ToByte(messCode.Value)
|
||||||
where pr.ProjectCodeId == projectCodeId
|
};
|
||||||
&& !pod.IsRemoved
|
return response;
|
||||||
&& pr.IsActive
|
|
||||||
select pr).AnyAsync();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
ex.ToString();
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<List<RemovedPR>> GetRemovedPR(PRDto pRDto)
|
|
||||||
{
|
|
||||||
var allItems = await _dbContext.RemovedPRs
|
|
||||||
.FromSqlRaw("EXEC GetRemovedPR @UserId",
|
|
||||||
new SqlParameter("@UserId", pRDto.UserId)).ToListAsync();
|
|
||||||
|
|
||||||
return allItems ?? new List<RemovedPR>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<List<ApprovedPR>> GetApprovedPR(PRDto pRDto)
|
|
||||||
{
|
|
||||||
var allItems = await _dbContext.ApprovedPrs
|
|
||||||
.FromSqlRaw("EXEC GetApprovedPR @UserId",
|
|
||||||
new SqlParameter("@UserId", pRDto.UserId)).ToListAsync();
|
|
||||||
|
|
||||||
return allItems ?? new List<ApprovedPR>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
@ -44,6 +44,7 @@ namespace CPRNIMS.Domain.UIContracts.PR
|
|||||||
Task<PRVM> ApprovedSelectedPRItem(User user, PRVM viewModel);
|
Task<PRVM> ApprovedSelectedPRItem(User user, PRVM viewModel);
|
||||||
Task<PRVM> PostPutProjectCode(User user, PRVM viewModel);
|
Task<PRVM> PostPutProjectCode(User user, PRVM viewModel);
|
||||||
Task<PRVM> PostPutAttachment(User user, PRVM prVM);
|
Task<PRVM> PostPutAttachment(User user, PRVM prVM);
|
||||||
|
Task<PRVM> PostItemInPR(User user, PRVM viewModel);
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,6 @@ using CPRNIMS.Infrastructure.ViewModel.PR;
|
|||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace CPRNIMS.Domain.UIServices.PR
|
namespace CPRNIMS.Domain.UIServices.PR
|
||||||
{
|
{
|
||||||
@ -286,6 +285,12 @@ namespace CPRNIMS.Domain.UIServices.PR
|
|||||||
return await SendPostApiRequest(user, prVM,
|
return await SendPostApiRequest(user, prVM,
|
||||||
_configuration["LLI:NonInvent:PRMgmt:PostPutAttachment"]);
|
_configuration["LLI:NonInvent:PRMgmt:PostPutAttachment"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<PRVM> PostItemInPR(User user, PRVM prVM)
|
||||||
|
{
|
||||||
|
return await SendPostApiRequest(user, prVM,
|
||||||
|
_configuration["LLI:NonInvent:PRMgmt:PostItemInPR"]);
|
||||||
|
}
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
21
CPRNIMS.Infrastructure/Dto/SMTP/EmailValidationResult.cs
Normal file
21
CPRNIMS.Infrastructure/Dto/SMTP/EmailValidationResult.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace CPRNIMS.Infrastructure.Dto.SMTP
|
||||||
|
{
|
||||||
|
public class EmailValidationResult
|
||||||
|
{
|
||||||
|
public string Email { get; set; } = string.Empty;
|
||||||
|
public bool IsValid { get; set; }
|
||||||
|
public string Reason { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public static EmailValidationResult Pass(string email) =>
|
||||||
|
new() { Email = email, IsValid = true, Reason = "OK" };
|
||||||
|
|
||||||
|
public static EmailValidationResult Fail(string email, string reason) =>
|
||||||
|
new() { Email = email, IsValid = false, Reason = reason };
|
||||||
|
}
|
||||||
|
}
|
||||||
176
CPRNIMS.Infrastructure/Helper/EmailValidationService.cs
Normal file
176
CPRNIMS.Infrastructure/Helper/EmailValidationService.cs
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
using CPRNIMS.Infrastructure.Dto.SMTP;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace CPRNIMS.Infrastructure.Helper
|
||||||
|
{
|
||||||
|
public class EmailValidationService
|
||||||
|
{
|
||||||
|
// In-memory cache for MX lookups { domain -> (isValid, timestamp) }
|
||||||
|
private static readonly Dictionary<string, (bool IsValid, DateTime CachedAt)> _mxCache = new();
|
||||||
|
private static readonly TimeSpan _mxCacheExpiry = TimeSpan.FromHours(24);
|
||||||
|
|
||||||
|
// Persistent bounce list - load from your DB or file
|
||||||
|
private static readonly HashSet<string> _bounceList = new(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
// Known disposable/fake email domains (expand this list as needed)
|
||||||
|
private static readonly HashSet<string> _disposableDomains = new(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
// Disposable/temp mail providers
|
||||||
|
"mailinator.com", "guerrillamail.com", "tempmail.com", "throwaway.email",
|
||||||
|
"yopmail.com", "sharklasers.com", "guerrillamailblock.com", "grr.la",
|
||||||
|
"guerrillamail.info", "spam4.me", "trashmail.com", "trashmail.me",
|
||||||
|
"trashmail.net", "trashmail.at", "trashmail.io", "trashmail.org",
|
||||||
|
"fakeinbox.com", "mailnull.com", "spamgourmet.com", "spamgourmet.net",
|
||||||
|
"spamgourmet.org", "maildrop.cc", "dispostable.com", "mailexpire.com",
|
||||||
|
"spamex.com", "deadaddress.com", "spamfree24.org", "no-spam.ws",
|
||||||
|
"discard.email", "despam.it", "emailsensei.com", "getonemail.com",
|
||||||
|
"spamfree.eu", "spaml.com", "spammotel.com", "spamspot.com",
|
||||||
|
"tempe-mail.com", "tempinbox.com", "temp-mail.org", "emailtemporanea.net",
|
||||||
|
"crazymailing.com", "dispostable.com", "spamgob.com", "0-mail.com",
|
||||||
|
"0815.ru", "0clickemail.com", "10minutemail.com", "20minutemail.com",
|
||||||
|
"filzmail.com", "getnada.com", "incognitomail.com", "jetable.fr.nf",
|
||||||
|
"lortemail.dk", "mytempemail.com", "noclickemail.com", "nowmymail.com",
|
||||||
|
"objectmail.com", "odaymail.com", "onewaymail.com", "pookmail.com",
|
||||||
|
"privacy.net", "proxymail.eu", "rcpt.at", "rklips.com",
|
||||||
|
"shortmail.net", "sogetthis.com", "spamgob.com", "spaml.de",
|
||||||
|
"speed.1s.fr", "supergreatmail.com", "suremail.info", "tempail.com",
|
||||||
|
"tempemail.net", "temporarioemail.com.br", "thanksnospam.info",
|
||||||
|
"thisisnotmyrealemail.com", "throwam.com", "trbvm.com", "truckmail.com",
|
||||||
|
"tyldd.com", "uggsrock.com", "veryrealemail.com", "vidchart.com",
|
||||||
|
"wegwerfmail.de", "wegwerfmail.net", "wegwerfmail.org", "wh4f.org",
|
||||||
|
"whyspam.me", "willselfdestruct.com", "wronghead.com", "wuzupmail.net",
|
||||||
|
"xagloo.com", "xemaps.com", "xents.com", "xmaily.com", "xoxy.net",
|
||||||
|
"yep.it", "yogamaven.com", "yuurok.com", "zehnminutenmail.de",
|
||||||
|
"zippymail.info", "zoaxe.com", "zoemail.net", "zoemail.org"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Known legitimate domains — skip MX lookup to save time
|
||||||
|
private static readonly HashSet<string> _trustedDomains = new(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
"gmail.com", "yahoo.com", "outlook.com", "hotmail.com", "live.com",
|
||||||
|
"icloud.com", "me.com", "mac.com", "msn.com", "aol.com",
|
||||||
|
"protonmail.com", "proton.me", "zoho.com", "yandex.com",
|
||||||
|
"fastmail.com", "hey.com", "tutanota.com"
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fast multi-layer email validation without SMTP handshake.
|
||||||
|
/// Average time per email: ~5-50ms (MX lookup only on unknown domains, cached after first hit).
|
||||||
|
/// </summary>
|
||||||
|
public async Task<EmailValidationResult> ValidateAsync(string email)
|
||||||
|
{
|
||||||
|
email = email.Trim().ToLowerInvariant();
|
||||||
|
|
||||||
|
// LAYER 1: Regex format check (instant)
|
||||||
|
if (!IsValidFormat(email))
|
||||||
|
return EmailValidationResult.Fail(email, "Invalid email format");
|
||||||
|
|
||||||
|
var domain = email.Split('@')[1];
|
||||||
|
|
||||||
|
// LAYER 2: Bounce list check (instant - known bad addresses from past sends)
|
||||||
|
if (_bounceList.Contains(email))
|
||||||
|
return EmailValidationResult.Fail(email, "Previously bounced email address");
|
||||||
|
|
||||||
|
// LAYER 3: Disposable/fake domain check (instant - local hashset lookup)
|
||||||
|
if (_disposableDomains.Contains(domain))
|
||||||
|
return EmailValidationResult.Fail(email, $"Disposable email domain: {domain}");
|
||||||
|
|
||||||
|
// LAYER 4: Trusted domain fast-pass (instant - skip DNS for known providers)
|
||||||
|
if (_trustedDomains.Contains(domain))
|
||||||
|
return EmailValidationResult.Pass(email);
|
||||||
|
|
||||||
|
// LAYER 5: MX Record check with caching (fast after first lookup)
|
||||||
|
bool hasMx = await HasValidMxRecordAsync(domain);
|
||||||
|
if (!hasMx)
|
||||||
|
return EmailValidationResult.Fail(email, $"No MX record found for domain: {domain}");
|
||||||
|
|
||||||
|
return EmailValidationResult.Pass(email);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Validates a batch of emails concurrently for performance.
|
||||||
|
/// </summary>
|
||||||
|
public async Task<List<EmailValidationResult>> ValidateBatchAsync(IEnumerable<string> emails)
|
||||||
|
{
|
||||||
|
var tasks = emails.Select(e => ValidateAsync(e));
|
||||||
|
var results = await Task.WhenAll(tasks);
|
||||||
|
return results.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Call this after a send attempt — records bounced addresses so they're
|
||||||
|
/// skipped automatically in future sends.
|
||||||
|
/// </summary>
|
||||||
|
public void RecordBounce(string email)
|
||||||
|
{
|
||||||
|
_bounceList.Add(email.Trim().ToLowerInvariant());
|
||||||
|
Console.WriteLine($"[BounceTracker] Recorded bounced email: {email}");
|
||||||
|
// TODO: Persist to your database here
|
||||||
|
// await _dbContext.BouncedEmails.AddAsync(new BouncedEmail { Address = email, Date = DateTime.UtcNow });
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsValidFormat(string email)
|
||||||
|
{
|
||||||
|
var regex = new Regex(
|
||||||
|
@"^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$",
|
||||||
|
RegexOptions.IgnoreCase
|
||||||
|
);
|
||||||
|
return regex.IsMatch(email);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<bool> HasValidMxRecordAsync(string domain)
|
||||||
|
{
|
||||||
|
// Return cached result if still fresh
|
||||||
|
if (_mxCache.TryGetValue(domain, out var cached))
|
||||||
|
{
|
||||||
|
if (DateTime.UtcNow - cached.CachedAt < _mxCacheExpiry)
|
||||||
|
return cached.IsValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var processInfo = new System.Diagnostics.ProcessStartInfo("nslookup")
|
||||||
|
{
|
||||||
|
Arguments = $"-type=MX {domain}",
|
||||||
|
RedirectStandardOutput = true,
|
||||||
|
UseShellExecute = false,
|
||||||
|
CreateNoWindow = true
|
||||||
|
};
|
||||||
|
|
||||||
|
using var process = System.Diagnostics.Process.Start(processInfo);
|
||||||
|
if (process == null)
|
||||||
|
{
|
||||||
|
_mxCache[domain] = (true, DateTime.UtcNow); // assume valid on error
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Timeout nslookup after 5 seconds
|
||||||
|
var outputTask = process.StandardOutput.ReadToEndAsync();
|
||||||
|
if (await Task.WhenAny(outputTask, Task.Delay(5000)) != outputTask)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[MX] DNS lookup timed out for {domain}, assuming valid.");
|
||||||
|
_mxCache[domain] = (true, DateTime.UtcNow);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
string output = await outputTask;
|
||||||
|
bool hasMx = Regex.IsMatch(output, @"mail exchanger", RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
|
_mxCache[domain] = (hasMx, DateTime.UtcNow);
|
||||||
|
Console.WriteLine($"[MX] {domain} → {(hasMx ? "Valid MX" : "No MX found")} (cached)");
|
||||||
|
return hasMx;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[MX] Lookup error for {domain}: {ex.Message}");
|
||||||
|
_mxCache[domain] = (true, DateTime.UtcNow); // assume valid on error
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,6 +3,7 @@ using Microsoft.Extensions.Configuration;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
using System.Net.Mail;
|
using System.Net.Mail;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -12,8 +13,10 @@ namespace CPRNIMS.Infrastructure.Helper
|
|||||||
public class SMTPHelper
|
public class SMTPHelper
|
||||||
{
|
{
|
||||||
private readonly IConfiguration _configuration;
|
private readonly IConfiguration _configuration;
|
||||||
public SMTPHelper(IConfiguration configuration)
|
private readonly EmailValidationService _emailValidator;
|
||||||
|
public SMTPHelper(EmailValidationService emailValidator, IConfiguration configuration)
|
||||||
{
|
{
|
||||||
|
_emailValidator = emailValidator;
|
||||||
_configuration = configuration;
|
_configuration = configuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,58 +52,104 @@ namespace CPRNIMS.Infrastructure.Helper
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 👇 Reads live from appsettings.json every time — picks up changes without restart
|
||||||
|
var excludedEmails = new HashSet<string>(
|
||||||
|
_configuration.GetSection("Canvass:EmailSettings:ExcludedEmails")
|
||||||
|
.Get<List<string>>() ?? new List<string>(),
|
||||||
|
StringComparer.OrdinalIgnoreCase
|
||||||
|
);
|
||||||
|
|
||||||
using (MailMessage myMessage = new MailMessage())
|
using (MailMessage myMessage = new MailMessage())
|
||||||
{
|
{
|
||||||
// Split the recipient string by semicolon and add each email address individually
|
var recipientList = emailMessageBody.Recipient
|
||||||
var recipients = emailMessageBody.Recipient.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
|
.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)
|
||||||
foreach (var recipient in recipients)
|
.Select(r => r.Trim())
|
||||||
|
.Where(r => !string.IsNullOrWhiteSpace(r))
|
||||||
|
.Where(r =>
|
||||||
|
{
|
||||||
|
if (excludedEmails.Contains(r))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[Excluded] Skipping: {r}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Validate remaining recipients concurrently
|
||||||
|
var validationResults = await _emailValidator.ValidateBatchAsync(recipientList);
|
||||||
|
|
||||||
|
foreach (var result in validationResults)
|
||||||
{
|
{
|
||||||
myMessage.To.Add(recipient.Trim());
|
if (result.IsValid)
|
||||||
|
myMessage.To.Add(result.Email);
|
||||||
|
else
|
||||||
|
Console.WriteLine($"[Skipped] {result.Reason}: {result.Email}");
|
||||||
}
|
}
|
||||||
// myMessage.To.Add(emailMessageBody.Recipient);
|
|
||||||
|
if (myMessage.To.Count == 0)
|
||||||
|
{
|
||||||
|
Console.WriteLine("No valid recipients after exclusion/validation. Aborting.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
myMessage.Sender = new MailAddress(emailMessageBody.SenderEmail);
|
myMessage.Sender = new MailAddress(emailMessageBody.SenderEmail);
|
||||||
myMessage.From = new MailAddress(emailMessageBody.SenderEmail, emailMessageBody.DisplayName);
|
myMessage.From = new MailAddress(emailMessageBody.SenderEmail, emailMessageBody.DisplayName);
|
||||||
var cc = emailMessageBody.CC.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
if(cc.Length > 0)
|
// CC with exclusion too
|
||||||
|
if (!string.IsNullOrWhiteSpace(emailMessageBody.CC))
|
||||||
{
|
{
|
||||||
foreach (var ccs in cc)
|
var ccList = emailMessageBody.CC
|
||||||
|
.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)
|
||||||
|
.Select(c => c.Trim())
|
||||||
|
.Where(c => !string.IsNullOrWhiteSpace(c))
|
||||||
|
.Where(c =>
|
||||||
|
{
|
||||||
|
if (excludedEmails.Contains(c))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[Excluded CC] Skipping: {c}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
var ccValidationResults = await _emailValidator.ValidateBatchAsync(ccList);
|
||||||
|
foreach (var result in ccValidationResults)
|
||||||
{
|
{
|
||||||
myMessage.CC.Add(ccs.Trim());
|
if (result.IsValid)
|
||||||
|
myMessage.CC.Add(result.Email);
|
||||||
|
else
|
||||||
|
Console.WriteLine($"[Skipped CC] {result.Reason}: {result.Email}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
myMessage.Subject = emailMessageBody.Subject;
|
|
||||||
|
|
||||||
// Ensure the email body is correctly formatted HTML
|
myMessage.Subject = emailMessageBody.Subject;
|
||||||
myMessage.Body = emailMessageBody.Message;
|
myMessage.Body = emailMessageBody.Message;
|
||||||
myMessage.IsBodyHtml = true;
|
myMessage.IsBodyHtml = true;
|
||||||
if (emailMessageBody.IsCanvass)
|
|
||||||
|
if (emailMessageBody.IsCanvass && File.Exists(emailMessageBody.AttachPath))
|
||||||
{
|
{
|
||||||
if (File.Exists(emailMessageBody.AttachPath))
|
Attachment pdfAttachment = new Attachment(emailMessageBody.AttachPath);
|
||||||
{
|
pdfAttachment.Name = Path.GetFileName(emailMessageBody.AttachPath);
|
||||||
Attachment pdfAttachment = new Attachment(emailMessageBody.AttachPath);
|
myMessage.Attachments.Add(pdfAttachment);
|
||||||
pdfAttachment.Name = Path.GetFileName(emailMessageBody.AttachPath);
|
|
||||||
myMessage.Attachments.Add(pdfAttachment);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
using (SmtpClient smtp = new SmtpClient(emailMessageBody.Server))
|
using (SmtpClient smtp = new SmtpClient(emailMessageBody.Server))
|
||||||
{
|
{
|
||||||
|
|
||||||
smtp.Port = emailMessageBody.OutGoingPort;
|
smtp.Port = emailMessageBody.OutGoingPort;
|
||||||
smtp.UseDefaultCredentials = emailMessageBody.IsSuccess;
|
smtp.UseDefaultCredentials = emailMessageBody.IsSuccess;
|
||||||
smtp.Credentials = new System.Net.NetworkCredential(emailMessageBody.UserName, emailMessageBody.NewPassword);
|
smtp.Credentials = new NetworkCredential(emailMessageBody.UserName, emailMessageBody.NewPassword);
|
||||||
smtp.EnableSsl = true;
|
smtp.EnableSsl = true;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await smtp.SendMailAsync(myMessage);
|
await smtp.SendMailAsync(myMessage);
|
||||||
|
Console.WriteLine($"Email sent successfully to {myMessage.To.Count} recipient(s).");
|
||||||
Console.WriteLine("Email sent successfully.");
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
var Message = ex.InnerException?.ToString() ?? ex.Message.ToString();
|
var message = ex.InnerException?.ToString() ?? ex.Message;
|
||||||
Console.WriteLine($"Error sending email: {Message}");
|
Console.WriteLine($"Error sending email: {message}");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -109,8 +158,8 @@ namespace CPRNIMS.Infrastructure.Helper
|
|||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
IsAuthError = true;
|
IsAuthError = true;
|
||||||
var Message = ex.InnerException?.ToString() ?? ex.Message.ToString();
|
var message = ex.InnerException?.ToString() ?? ex.Message;
|
||||||
Console.WriteLine($"Error in SendEmailAsync: {Message}");
|
Console.WriteLine($"Error in SendEmailAsync: {message}");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
14
CPRNIMS.Infrastructure/ViewModel/PR/PRItemListRequest.cs
Normal file
14
CPRNIMS.Infrastructure/ViewModel/PR/PRItemListRequest.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace CPRNIMS.Infrastructure.ViewModel.PR
|
||||||
|
{
|
||||||
|
public class PRItemListRequest
|
||||||
|
{
|
||||||
|
public List<long>? ItemNo { get; set; }
|
||||||
|
public List<decimal>? Qty { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -137,5 +137,6 @@ namespace CPRNIMS.Infrastructure.ViewModel.PR
|
|||||||
public int RemainingDays { get; set; }
|
public int RemainingDays { get; set; }
|
||||||
public ItemReceivingList? ItemList { get; set; }
|
public ItemReceivingList? ItemList { get; set; }
|
||||||
public PRList? PRList { get; set; }
|
public PRList? PRList { get; set; }
|
||||||
|
public PRItemListRequest? PRItemListRequest { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -161,6 +161,7 @@ namespace CPRNIMS.WebApi.Common
|
|||||||
services.AddScoped<IForgotPassword,Domain.Services.Account.ForgotPassword>();
|
services.AddScoped<IForgotPassword,Domain.Services.Account.ForgotPassword>();
|
||||||
services.AddScoped<IAccount, Account>();
|
services.AddScoped<IAccount, Account>();
|
||||||
services.AddScoped<SMTPHelper>();
|
services.AddScoped<SMTPHelper>();
|
||||||
|
services.AddScoped<EmailValidationService>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -38,6 +38,35 @@ namespace CPRNIMS.WebApi.Controllers.PR
|
|||||||
_item = item;
|
_item = item;
|
||||||
}
|
}
|
||||||
#region POST PUT
|
#region POST PUT
|
||||||
|
[HttpPost("PostItemInPR")]
|
||||||
|
public async Task<IActionResult> PostItemInPR([FromBody] PRVM PRDto)
|
||||||
|
{
|
||||||
|
var results = new ResponseObject();
|
||||||
|
|
||||||
|
if (PRDto.PRItemListRequest?.ItemNo?.Count > 0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < PRDto.PRItemListRequest.ItemNo.Count; i++)
|
||||||
|
{
|
||||||
|
var dto = new PRDto
|
||||||
|
{
|
||||||
|
ItemNo = PRDto.PRItemListRequest.ItemNo[i],
|
||||||
|
Qty = PRDto.PRItemListRequest.Qty[i],
|
||||||
|
UserId = PRDto.UserId,
|
||||||
|
PRNo = PRDto.PRNo,
|
||||||
|
};
|
||||||
|
var result = await _pRequest.PostItemInPR(dto);
|
||||||
|
results.messCode = result.messCode;
|
||||||
|
results.message = result.message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(new ResponseObject()
|
||||||
|
{
|
||||||
|
success = results.messCode == 1,
|
||||||
|
messCode = results.messCode,
|
||||||
|
message = results.message ?? "Operation completed successfully"
|
||||||
|
});
|
||||||
|
}
|
||||||
[HttpPost("PostPutAttachment")]
|
[HttpPost("PostPutAttachment")]
|
||||||
public async Task<IActionResult> PostPutAttachment([FromBody] PRVM PRDto)
|
public async Task<IActionResult> PostPutAttachment([FromBody] PRVM PRDto)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -148,6 +148,7 @@ namespace CPRNIMS.WebApps.Controllers.PR
|
|||||||
|
|
||||||
return GetResponse(response);
|
return GetResponse(response);
|
||||||
}
|
}
|
||||||
|
#region Mapper
|
||||||
private PRList MapToPRItemList(IEnumerable<PRList> prList)
|
private PRList MapToPRItemList(IEnumerable<PRList> prList)
|
||||||
{
|
{
|
||||||
if (prList == null || !prList.Any())
|
if (prList == null || !prList.Any())
|
||||||
@ -165,9 +166,28 @@ namespace CPRNIMS.WebApps.Controllers.PR
|
|||||||
ItemNo = prList.SelectMany(ic => ic.ItemNo).ToList()
|
ItemNo = prList.SelectMany(ic => ic.ItemNo).ToList()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
private PRItemListRequest MapToPRItemList(IEnumerable<PRItemListRequest> prItemListRequest)
|
||||||
|
{
|
||||||
|
if (prItemListRequest == null || !prItemListRequest.Any())
|
||||||
|
{
|
||||||
|
return new PRItemListRequest
|
||||||
|
{
|
||||||
|
ItemNo = new List<long>(),
|
||||||
|
Qty = new List<decimal>(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return new PRItemListRequest
|
||||||
|
{
|
||||||
|
ItemNo = prItemListRequest.SelectMany(ic => ic.ItemNo).ToList(),
|
||||||
|
Qty = prItemListRequest.SelectMany(ic => ic.Qty).ToList()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
#endregion
|
#endregion
|
||||||
#region POST PUT
|
#region POST PUT
|
||||||
public async Task<IActionResult?> UploadAttachment(IFormFile? file, [FromForm] string? oldFileName,
|
public async Task<IActionResult?> UploadAttachment(IFormFile? file, [FromForm]
|
||||||
|
string? oldFileName,
|
||||||
[FromForm] long prId)
|
[FromForm] long prId)
|
||||||
{
|
{
|
||||||
var uploadsPath = Path.Combine(
|
var uploadsPath = Path.Combine(
|
||||||
@ -326,6 +346,17 @@ namespace CPRNIMS.WebApps.Controllers.PR
|
|||||||
}
|
}
|
||||||
return Json(new { success = false, Response = postPutItem.Message });
|
return Json(new { success = false, Response = postPutItem.Message });
|
||||||
}
|
}
|
||||||
|
public async Task<IActionResult> PostItemInPR(PRVM viewModel, List<PRItemListRequest> PRItemList)
|
||||||
|
{
|
||||||
|
viewModel.PRItemListRequest = MapToPRItemList(PRItemList);
|
||||||
|
var postPutItem = await _pRequest.PostItemInPR(GetUser(), viewModel);
|
||||||
|
|
||||||
|
if (postPutItem.messCode != 0)
|
||||||
|
{
|
||||||
|
return Json(new { success = true, Response = postPutItem.Message });
|
||||||
|
}
|
||||||
|
return Json(new { success = false, Response = postPutItem.Message });
|
||||||
|
}
|
||||||
#endregion
|
#endregion
|
||||||
#region Views
|
#region Views
|
||||||
public IActionResult GetDashBoardById(int DashboardId)
|
public IActionResult GetDashBoardById(int DashboardId)
|
||||||
|
|||||||
@ -13,7 +13,7 @@
|
|||||||
<table id="ItemTable" class="row-border" cellspacing="0" width="100%">
|
<table id="ItemTable" class="row-border" cellspacing="0" width="100%">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>ItemCodeNo.</th>
|
<th>ItemNo</th>
|
||||||
<th>ItemName</th>
|
<th>ItemName</th>
|
||||||
<th>ItemSpecs</th>
|
<th>ItemSpecs</th>
|
||||||
<th>CategoryName</th>
|
<th>CategoryName</th>
|
||||||
|
|||||||
@ -24,6 +24,46 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Modal Add New Item in PR -->
|
||||||
|
<div class="modal fade custom-modal-backdrop" id="viewItemList"
|
||||||
|
tabindex="-1" aria-labelledby="addItemLabel" aria-hidden="true" data-bs-backdrop="static">
|
||||||
|
<div class="modal-dialog modal-xl">
|
||||||
|
<div class="modal-content border-0 shadow-lg">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h2 class="modal-title" id="addItemLabel">Item List</h2>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body p-4">
|
||||||
|
<div style="margin-bottom:5px">
|
||||||
|
<span class="fw-semibold">Selected Items:</span>
|
||||||
|
<span id="totalSelectedItem" class="badge bg-danger ms-2">0</span>
|
||||||
|
</div>
|
||||||
|
<table id="ItemTable" class="row-border" cellspacing="0" width="100%">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>All</th>
|
||||||
|
<th>ItemNo</th>
|
||||||
|
<th>ItemName</th>
|
||||||
|
<th>ItemSpecs</th>
|
||||||
|
<th>CategoryName</th>
|
||||||
|
<th>Qty</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer bg-light border-0 p-3">
|
||||||
|
<button type="button" class="btn btn-outline-secondary px-4" data-bs-dismiss="modal">
|
||||||
|
<i class="bi bi-x-circle me-2"></i>Cancel
|
||||||
|
</button>
|
||||||
|
<button type="button" id="btnConfirmUpdate" onclick="postItemInPR()" class="btn btn-success px-4">
|
||||||
|
<i class="bi bi-check-circle me-2"></i>Submit
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Modal addRemarksUpdate -->
|
<!-- Modal addRemarksUpdate -->
|
||||||
<div class="modal fade custom-modal-backdrop" id="addRemarksUpdate"
|
<div class="modal fade custom-modal-backdrop" id="addRemarksUpdate"
|
||||||
tabindex="-1" aria-labelledby="approveLabel" aria-hidden="true" data-bs-backdrop="static">
|
tabindex="-1" aria-labelledby="approveLabel" aria-hidden="true" data-bs-backdrop="static">
|
||||||
@ -76,7 +116,7 @@
|
|||||||
<link href="~/css/pr/ButtonStyleV2.css" rel="stylesheet" />
|
<link href="~/css/pr/ButtonStyleV2.css" rel="stylesheet" />
|
||||||
<link href="~/css/pr/PRTabs.css" rel="stylesheet" />
|
<link href="~/css/pr/PRTabs.css" rel="stylesheet" />
|
||||||
@await Html.PartialAsync("PagesView/PR/_PRTracking")
|
@await Html.PartialAsync("PagesView/PR/_PRTracking")
|
||||||
<script src="~/JsFunctions/PR/PRV8.js"></script>
|
<script src="~/JsFunctions/PR/PRV9.js"></script>
|
||||||
<script src="~/JsFunctions/PR/PRTabs.js"></script>
|
<script src="~/JsFunctions/PR/PRTabs.js"></script>
|
||||||
@await Html.PartialAsync("PagesView/PR/_PRScripts")
|
@await Html.PartialAsync("PagesView/PR/_PRScripts")
|
||||||
</body>
|
</body>
|
||||||
@ -24,7 +24,7 @@
|
|||||||
<script src="~/jsfunctions/po/POViewV4.js"></script>
|
<script src="~/jsfunctions/po/POViewV4.js"></script>
|
||||||
<script src="~/jsfunctions/po/PopulateDopdownV4.js"></script>
|
<script src="~/jsfunctions/po/PopulateDopdownV4.js"></script>
|
||||||
<script src="~/jsfunctions/po/POPutPostV3.js"></script>
|
<script src="~/jsfunctions/po/POPutPostV3.js"></script>
|
||||||
<script src="~/jsfunctions/po/rowCallBackV4.js"></script>
|
<script src="~/jsfunctions/po/rowCallBackV5.js"></script>
|
||||||
|
|
||||||
<script src="~/jsfunctions/utilities/NewStyle.js"></script>
|
<script src="~/jsfunctions/utilities/NewStyle.js"></script>
|
||||||
<script src="~/jsfunctions/utilities/utilsV3.js"></script>
|
<script src="~/jsfunctions/utilities/utilsV3.js"></script>
|
||||||
|
|||||||
@ -4,11 +4,11 @@
|
|||||||
<link href="~/css/common/rowhighlighter.css" rel="stylesheet" />
|
<link href="~/css/common/rowhighlighter.css" rel="stylesheet" />
|
||||||
|
|
||||||
<script src="~/jsfunctions/pr/PRColumnV8.js"></script>
|
<script src="~/jsfunctions/pr/PRColumnV8.js"></script>
|
||||||
<script src="~/jsfunctions/pr/PRViewV7.js"></script>
|
<script src="~/jsfunctions/pr/PRViewV8.js"></script>
|
||||||
<script src="~/jsfunctions/pr/PRPutPost.js"></script>
|
<script src="~/jsfunctions/pr/PRPostPut.js"></script>
|
||||||
<script src="~/jsfunctions/pr/PRButtonv3.js"></script>
|
<script src="~/jsfunctions/pr/PRButtonv3.js"></script>
|
||||||
<script src="~/jsfunctions/pr/PRVarV3.js"></script>
|
<script src="~/jsfunctions/pr/PRVarV3.js"></script>
|
||||||
<script src="~/jsfunctions/pr/Configv5.js"></script>
|
<script src="~/jsfunctions/pr/Configv6.js"></script>
|
||||||
<script src="~/jsfunctions/pr/populatedropdown.js"></script>
|
<script src="~/jsfunctions/pr/populatedropdown.js"></script>
|
||||||
<script src="~/jsfunctions/pr/prRowCallbackV3.js"></script>
|
<script src="~/jsfunctions/pr/prRowCallbackV3.js"></script>
|
||||||
<script src="~/jsfunctions/utilities/columnstyle.js"></script>
|
<script src="~/jsfunctions/utilities/columnstyle.js"></script>
|
||||||
|
|||||||
@ -327,6 +327,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Modal viewPRDetails -->
|
<!-- Modal viewPRDetails -->
|
||||||
<div class="modal fade custom-modal-backdrop" id="viewPRDetails"
|
<div class="modal fade custom-modal-backdrop" id="viewPRDetails"
|
||||||
tabindex="-1" aria-labelledby="ModalLabel" data-bs-backdrop="static">
|
tabindex="-1" aria-labelledby="ModalLabel" data-bs-backdrop="static">
|
||||||
@ -388,6 +389,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<button type="button"
|
||||||
|
id="btnAddNewItem"
|
||||||
|
onclick="viewItemList();"
|
||||||
|
class="btn btn-add">
|
||||||
|
➕ Add Item
|
||||||
|
</button>
|
||||||
|
|
||||||
<!-- SELECTION SUMMARY -->
|
<!-- SELECTION SUMMARY -->
|
||||||
<div class="d-flex align-items-center mb-3 gap-2">
|
<div class="d-flex align-items-center mb-3 gap-2">
|
||||||
<div>
|
<div>
|
||||||
@ -499,6 +507,29 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<style>
|
||||||
|
.btn-add {
|
||||||
|
background: linear-gradient(135deg, #009688, #00bfa5);
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 600;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 10px 20px;
|
||||||
|
margin-bottom:10px;
|
||||||
|
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-add:hover {
|
||||||
|
background: linear-gradient(135deg, #00bfa5, #00796b);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 6px 12px rgba(0,0,0,0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-add:active {
|
||||||
|
transform: scale(0.97);
|
||||||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<input hidden id="roleRights" value="@ViewBag.UserRoles" />
|
<input hidden id="roleRights" value="@ViewBag.UserRoles" />
|
||||||
Loading…
Reference in New Issue
Block a user