feedback of UAT

This commit is contained in:
dibakor 2026-08-11 18:24:11 +06:00
parent 68cf33d5a6
commit 0529139358
18 changed files with 385 additions and 54 deletions

1
.gitignore vendored
View File

@ -21,3 +21,4 @@ obj
/App/ClientApp/node_modules
/Api/OnlineSalesAutoCrop.CoreAPI/.config/dotnet-tools.json
/App/ClientApp/.angular/cache/21.2.13/OnlineSalesAutoCropApp
/Deployment/App/browser

View File

@ -6,7 +6,7 @@ namespace OnlineSalesAutoCrop.CoreAPI.Models.Requests.Common;
public class AuthLoginRequest
{
[Required, NotNull, StringLength(maximumLength: 10, MinimumLength = 3, ErrorMessage = "Login Id must be between 3 and 10 characters.")]
[Required, NotNull, StringLength(maximumLength: 15, MinimumLength = 3, ErrorMessage = "Login Id must be between 3 and 15 characters.")]
public string LoginId { get; set; }
[Required, NotNull, StringLength(maximumLength: 20, MinimumLength = 5, ErrorMessage = "Password must be between 5 and 20 characters.")]

View File

@ -25,7 +25,6 @@ public class IntegrationEmployeeRequest
[Phone(ErrorMessage = "Mobile number is not a valid phone number.")]
public string MobileNo { get; set; }
[Required(ErrorMessage = "Email address is required.")]
[StringLength(241, ErrorMessage = "Email address cannot exceed 241 characters.")]
[EmailAddress(ErrorMessage = "Email address is not valid.")]
public string Mail { get; set; }

View File

@ -8,85 +8,85 @@ namespace OnlineSalesAutoCrop.CoreAPI.Models.Requests.MobileApp;
public class GetMarketHeirarchyByEmpRequest
{
[Required, NotNull, StringLength(maximumLength: 10, MinimumLength = 3, ErrorMessage = "EmployeeNumber must be between 3 and 10 characters.")]
[Required, NotNull, StringLength(maximumLength: 12, MinimumLength = 3, ErrorMessage = "EmployeeNumber must be between 3 and 12 characters.")]
public string EmployeeNumber { get; set; }
}
public class GetBrandsRequest : PagedRequest
{
[StringLength(6, MinimumLength = 1, ErrorMessage = "Brand code must be between 1 and 6 characters.")]
[StringLength(15, MinimumLength = 1, ErrorMessage = "Brand code must be between 1 and 15 characters.")]
public string? BrandCode { get; set; }
[StringLength(10, MinimumLength = 1, ErrorMessage = "Sales organization code must be between 1 and 10 characters.")]
[StringLength(15, MinimumLength = 1, ErrorMessage = "Sales organization code must be between 1 and 15 characters.")]
public string? SalesOrgCode { get; set; }
[StringLength(10, MinimumLength = 1, ErrorMessage = "Employee number must be between 1 and 10 characters.")]
[StringLength(15, MinimumLength = 1, ErrorMessage = "Employee number must be between 1 and 15 characters.")]
public string? EmployeeNumber { get; set; }
}
public class GetMaterialsRequest : PagedRequest
{
[StringLength(10, MinimumLength = 1, ErrorMessage = "Sales organization code must be between 1 and 10 characters.")]
[StringLength(15, MinimumLength = 1, ErrorMessage = "Sales organization code must be between 1 and 15 characters.")]
public string SalesOrgCode { get; set; }
[StringLength(6, MinimumLength = 1, ErrorMessage = "Brand code must be between 1 and 6 characters.")]
public string? BrandCode { get; set; }
[StringLength(10, MinimumLength = 1, ErrorMessage = "Material Code must be between 1 and 10 characters.")]
[StringLength(15, MinimumLength = 1, ErrorMessage = "Material Code must be between 1 and 15 characters.")]
public string? MaterialCode { get; set; }
[StringLength(10, MinimumLength = 1, ErrorMessage = "Employee number must be between 1 and 10 characters.")]
[StringLength(15, MinimumLength = 1, ErrorMessage = "Employee number must be between 1 and 15 characters.")]
public string? EmployeeNumber { get; set; }
}
public class GetMaterialStockRequest : PagedRequest
{
[StringLength(10, MinimumLength = 1, ErrorMessage = "Plant Code must be between 1 and 10 characters.")]
[StringLength(15, MinimumLength = 1, ErrorMessage = "Plant Code must be between 1 and 15 characters.")]
public string PlantCode { get; set; }
}
public class GetMaterialPriceRequest : PagedRequest
{
[StringLength(10, MinimumLength = 1, ErrorMessage = "Sales organization code must be between 1 and 10 characters.")]
[StringLength(15, MinimumLength = 1, ErrorMessage = "Sales organization code must be between 1 and 15 characters.")]
public string SalesOrgCode { get; set; }
[StringLength(6, MinimumLength = 1, ErrorMessage = "Brand code must be between 1 and 6 characters.")]
[StringLength(15, MinimumLength = 1, ErrorMessage = "Brand code must be between 1 and 15 characters.")]
public string? BrandCode { get; set; }
[StringLength(10, MinimumLength = 1, ErrorMessage = "Material Code must be between 1 and 10 characters.")]
[StringLength(15, MinimumLength = 1, ErrorMessage = "Material Code must be between 1 and 15 characters.")]
public string? MaterialCode { get; set; }
[StringLength(10, MinimumLength = 1, ErrorMessage = "Employee number must be between 1 and 10 characters.")]
[StringLength(15, MinimumLength = 1, ErrorMessage = "Employee number must be between 1 and 15 characters.")]
public string? EmployeeNumber { get; set; }
}
public class GetCustomersRequest : PagedRequest
{
[StringLength(10, MinimumLength = 3, ErrorMessage = "Sales organization code must be between 1 and 10 characters.")]
[StringLength(15, MinimumLength = 3, ErrorMessage = "Sales organization code must be between 1 and 15 characters.")]
public string SalesOrgCode { get; set; }
[StringLength(5, MinimumLength = 3, ErrorMessage = "Teritory Code must be between 1 and 5 characters.")]
public string? TeritoryCode { get; set; }
[StringLength(10, MinimumLength = 5, ErrorMessage = "Customer Code must be between 1 and 10 characters.")]
[StringLength(15, MinimumLength = 5, ErrorMessage = "Customer Code must be between 1 and 15 characters.")]
public string? CustomerCode { get; set; }
[StringLength(10, MinimumLength = 5, ErrorMessage = "Employee number must be between 1 and 10 characters.")]
[StringLength(15, MinimumLength = 5, ErrorMessage = "Employee number must be between 1 and 15 characters.")]
public string? EmployeeNumber { get; set; }
}
public class GetAttendanceByEmpRequest
{
[Required, NotNull, StringLength(maximumLength: 10, MinimumLength = 3, ErrorMessage = "EmployeeNumber must be between 3 and 10 characters.")]
[Required, NotNull, StringLength(maximumLength: 15, MinimumLength = 3, ErrorMessage = "EmployeeNumber must be between 3 and 15 characters.")]
public string EmployeeNumber { get; set; }
public DateTime? AttendanceDate { get; set; }
}
public class UpsertAttendanceByEmpRequest
{
[Required, NotNull, StringLength(maximumLength: 10, MinimumLength = 3, ErrorMessage = "EmployeeNumber must be between 3 and 10 characters.")]
[Required, NotNull, StringLength(maximumLength: 15, MinimumLength = 3, ErrorMessage = "EmployeeNumber must be between 3 and 15 characters.")]
public string EmployeeNumber { get; set; }
public TimeSpan? CheckInTime { get; set; }
public string? CheckInLatitude { get; set; }
@ -102,37 +102,37 @@ public class UpsertAttendanceByEmpRequest
public class GetDiscountRequest : PagedRequest
{
[StringLength(10, MinimumLength = 3, ErrorMessage = "Sales organization code must be between 1 and 10 characters.")]
[StringLength(15, MinimumLength = 3, ErrorMessage = "Sales organization code must be between 1 and 15 characters.")]
public string SalesOrgCode { get; set; }
[StringLength(10, MinimumLength = 5, ErrorMessage = "Customer Code must be between 1 and 10 characters.")]
[StringLength(15, MinimumLength = 5, ErrorMessage = "Customer Code must be between 1 and 15 characters.")]
public string? CustomerCode { get; set; }
[StringLength(10, MinimumLength = 5, ErrorMessage = "Employee number must be between 1 and 10 characters.")]
[StringLength(15, MinimumLength = 5, ErrorMessage = "Employee number must be between 1 and 15 characters.")]
public string? EmployeeNumber { get; set; }
[StringLength(10, MinimumLength = 3, ErrorMessage = "Material Code must be between 1 and 10 characters.")]
[StringLength(15, MinimumLength = 3, ErrorMessage = "Material Code must be between 1 and 15 characters.")]
public string? MaterialCode { get; set; }
[StringLength(10, MinimumLength = 1, ErrorMessage = "Discount Code must be between 1 and 10 characters.")]
[StringLength(15, MinimumLength = 1, ErrorMessage = "Discount Code must be between 1 and 15 characters.")]
public string? DiscountCode { get; set; }
}
public class GetDiscountMaterialRequest : PagedRequest
{
[StringLength(10, MinimumLength = 3, ErrorMessage = "Sales organization code must be between 1 and 10 characters.")]
[StringLength(15, MinimumLength = 3, ErrorMessage = "Sales organization code must be between 1 and 15 characters.")]
public string SalesOrgCode { get; set; }
[StringLength(10, MinimumLength = 5, ErrorMessage = "Customer Code must be between 1 and 10 characters.")]
[StringLength(15, MinimumLength = 5, ErrorMessage = "Customer Code must be between 1 and 15 characters.")]
public string? CustomerCode { get; set; }
[StringLength(10, MinimumLength = 5, ErrorMessage = "Employee number must be between 1 and 10 characters.")]
[StringLength(15, MinimumLength = 5, ErrorMessage = "Employee number must be between 1 and 15 characters.")]
public string? EmployeeNumber { get; set; }
[StringLength(10, MinimumLength = 3, ErrorMessage = "Material Code must be between 1 and 10 characters.")]
[StringLength(15, MinimumLength = 3, ErrorMessage = "Material Code must be between 1 and 15 characters.")]
public string? MaterialCode { get; set; }
[StringLength(10, MinimumLength = 1, ErrorMessage = "Discount Code must be between 1 and 10 characters.")]
[StringLength(15, MinimumLength = 1, ErrorMessage = "Discount Code must be between 1 and 15 characters.")]
public string? DiscountCode { get; set; }
}
@ -140,46 +140,46 @@ public class GetDiscountMaterialRequest : PagedRequest
public class GetFOCRequest : PagedRequest
{
[StringLength(10, MinimumLength = 3, ErrorMessage = "Sales organization code must be between 1 and 10 characters.")]
[StringLength(15, MinimumLength = 3, ErrorMessage = "Sales organization code must be between 1 and 15 characters.")]
public string SalesOrgCode { get; set; }
[StringLength(10, MinimumLength = 5, ErrorMessage = "Customer Code must be between 1 and 10 characters.")]
[StringLength(15, MinimumLength = 5, ErrorMessage = "Customer Code must be between 1 and 15 characters.")]
public string? CustomerCode { get; set; }
[StringLength(10, MinimumLength = 5, ErrorMessage = "Employee number must be between 1 and 10 characters.")]
[StringLength(15, MinimumLength = 5, ErrorMessage = "Employee number must be between 1 and 15 characters.")]
public string? EmployeeNumber { get; set; }
[StringLength(10, MinimumLength = 3, ErrorMessage = "Material Code must be between 1 and 10 characters.")]
[StringLength(15, MinimumLength = 3, ErrorMessage = "Material Code must be between 1 and 15 characters.")]
public string? MaterialCode { get; set; }
[StringLength(10, MinimumLength = 1, ErrorMessage = "Discount Code must be between 1 and 10 characters.")]
[StringLength(15, MinimumLength = 1, ErrorMessage = "Discount Code must be between 1 and 15 characters.")]
public string? FOCCode { get; set; }
}
public class GetFOCMaterialRequest : PagedRequest
{
[StringLength(10, MinimumLength = 3, ErrorMessage = "Sales organization code must be between 1 and 10 characters.")]
[StringLength(15, MinimumLength = 3, ErrorMessage = "Sales organization code must be between 1 and 15 characters.")]
public string SalesOrgCode { get; set; }
[StringLength(10, MinimumLength = 5, ErrorMessage = "Customer Code must be between 1 and 10 characters.")]
[StringLength(15, MinimumLength = 5, ErrorMessage = "Customer Code must be between 1 and 15 characters.")]
public string? CustomerCode { get; set; }
[StringLength(10, MinimumLength = 5, ErrorMessage = "Employee number must be between 1 and 10 characters.")]
[StringLength(15, MinimumLength = 5, ErrorMessage = "Employee number must be between 1 and 15 characters.")]
public string? EmployeeNumber { get; set; }
[StringLength(10, MinimumLength = 3, ErrorMessage = "Material Code must be between 1 and 10 characters.")]
[StringLength(15, MinimumLength = 3, ErrorMessage = "Material Code must be between 1 and 15 characters.")]
public string? MaterialCode { get; set; }
[StringLength(10, MinimumLength = 1, ErrorMessage = "Discount Code must be between 1 and 10 characters.")]
[StringLength(15, MinimumLength = 1, ErrorMessage = "Discount Code must be between 1 and 15 characters.")]
public string? FOCCode { get; set; }
}
public class GetPaymentTermRequest : PagedRequest
{
[StringLength(10, MinimumLength = 3, ErrorMessage = "Sales organization code must be between 1 and 10 characters.")]
[StringLength(15, MinimumLength = 3, ErrorMessage = "Sales organization code must be between 1 and 15 characters.")]
public string SalesOrgCode { get; set; }
[StringLength(10, MinimumLength = 3, ErrorMessage = "PayementTerm Code must be between 3 and 10 characters.")]
[StringLength(15, MinimumLength = 3, ErrorMessage = "PayementTerm Code must be between 3 and 15 characters.")]
public string? PaymentTermCode { get; set; }
}

View File

@ -41,6 +41,8 @@ public class GetSalesOrderResponse
public decimal? TotalVatValue { get; set; }
public decimal NetValue { get; set; }
public int OrderStatus { get; set; }
public string BillingNumber { get; set; }
public DateTime? BillingDate { get; set; }
public List<GetSalesOrderItemResponse> Items { get; set; } = new();
}
@ -91,6 +93,9 @@ public sealed class CustomerLookupDto
public int CustomerId { get; set; }
public string CustomerCode { get; set; } = string.Empty;
public string CustomerName { get; set; } = string.Empty;
public bool IsCreditCustomer { get; set; } = false;
public string CompanyCode { get; set; }
public decimal CreditLimit { get; set; }
}
public sealed class PaymentTermLookupDto

View File

@ -171,7 +171,18 @@ public class IntegrationService : IIntegrationService
await AddUserAsync(tc, appUser);
}
else
{
AppUserCreateRequest appUser = new()
{
MobileNo = request.MobileNo,
EmailAddress = request.Mail,
UserRoleType = roleTypeId,
EmployeeNumber = request.EmployeeVendorCode
};
await UpdateUserRoleAsync(tc, appUser);
}
response.EmployeeVendorCode = request.EmployeeVendorCode;
response.CompanyCode = request.CompanyCode;
tc.End();
@ -820,6 +831,39 @@ public class IntegrationService : IIntegrationService
}
public async Task<bool> UpdateUserRoleAsync(TransactionContext tc, AppUserCreateRequest user)
{
bool returnValue;
try
{
int userId = 0;
user.Password = EncryptPassword(password: user.Password);
if (!string.IsNullOrEmpty(user.EmailAddress))
user.EmailAddress = user.EmailAddress.Replace(" ", "");
SqlParameter[] p =
[
SqlHelperExtension.CreateInParam(pName: "@MobileNo", pType: SqlDbType.VarChar, pValue: DataReader.GetNullValue(user.MobileNo), size: 20),
SqlHelperExtension.CreateInParam(pName: "@EmailAddress", pType: SqlDbType.VarChar, pValue: user.EmailAddress, size: 200),
SqlHelperExtension.CreateInParam(pName: "@UserRoleType", pType: SqlDbType.Int, pValue: DataReader.GetNullValue(user.UserRoleType)),
SqlHelperExtension.CreateInParam(pName: "@EmployeeNumber", pType: SqlDbType.VarChar, pValue: DataReader.GetNullValue(user.EmployeeNumber), size: 10),
SqlHelperExtension.CreateOutParam(pName:"@NewUserId", pType: SqlDbType.Int)
];
_ = tc.ExecuteNonQuerySp(spName: "dbo.UpdateUserRole", parameterValues: p);
returnValue = true;
}
catch (Exception e)
{
throw new InvalidOperationException(e.Message, e);
}
return returnValue;
}
private string EncryptPassword(string password)
{
try

View File

@ -275,6 +275,7 @@ public class MobileMasterDataService : IMobileMasterDataService
public async Task<PagedResult<GetCustomerResponse>> GetCustomersAsync(GetCustomersRequest request)
{
string companyCode = string.Empty;
string salesOrgCode = string.Empty;
decimal usedCreditBalance = 0;
PagedResult<GetCustomerResponse> response = new();
try
@ -307,7 +308,7 @@ public class MobileMasterDataService : IMobileMasterDataService
DivisionCode = dr.GetString(6),
DivisionName = dr.GetString(7),
MobileNumber = dr.GetString(8),
EmailAddress = dr.GetString(9),
EmailAddress = dr.IsDBNull(9) ? null : dr.GetString(9),
TaxNumber = dr.GetString(10),
SalesGroupCode = dr.IsDBNull(11)? null : dr.GetString(11),
SalesGroupName = dr.IsDBNull(12) ? null : dr.GetString(12),
@ -325,6 +326,7 @@ public class MobileMasterDataService : IMobileMasterDataService
CreditOverdue = dr.GetDecimal(24),
});
companyCode = dr.GetString(25);
salesOrgCode = dr.GetString(19);
}
dr.Close();
@ -335,7 +337,7 @@ public class MobileMasterDataService : IMobileMasterDataService
if (!string.IsNullOrWhiteSpace(request.CustomerCode))
{
usedCreditBalance = await GetCurrentApprovedOrderAmountByCustomerAsync(tc, request.CustomerCode, request.SalesOrgCode);
usedCreditBalance = await GetCurrentApprovedOrderAmountByCustomerAsync(tc, request.CustomerCode, salesOrgCode);
}
tc.End();
@ -349,7 +351,7 @@ public class MobileMasterDataService : IMobileMasterDataService
{
response.Items = response.Items.Select(x =>
{
x.CreditBalance = (customerLedger.CurrentBalance - usedCreditBalance);
x.CreditBalance = (customerLedger.CurrentBalance + usedCreditBalance);
x.CreditOverdue = customerLedger.OverdueAmount;
return x;
}).ToList();
@ -481,9 +483,9 @@ public class MobileMasterDataService : IMobileMasterDataService
response.Add(new DistibutionChannleResponse
{
CompanyCode = dr.GetString(0),
SalesOrgCode = dr.GetString(1),
DistributorChannelCode = dr.GetString(2),
DistributorChannelName = dr.GetString(3)
SalesOrgCode = dr.IsDBNull(1) ? null: dr.GetString(1),
DistributorChannelCode = dr.IsDBNull(2) ? null : dr.GetString(2),
DistributorChannelName = dr.IsDBNull(3) ? null : dr.GetString(3)
});
}
dr.Close();

View File

@ -175,6 +175,21 @@ public class OrderService : IOrderService
});
}
decimal usedCreditBalance = await GetCurrentApprovedOrderAmountByCustomerAsync(tc, request.CustomerCode, request.SalesOrgCode);
if (request.OrderStatus == OrderStatusEnum.Approved && customer.IsCreditCustomer && request.PaymentTermCode != "0001")
{
var customerLedger = await _httpService.GetCustomerLedgerAsync(request.CustomerCode, customer.CompanyCode);
var creditBalance = customer.CreditLimit- (customerLedger.CurrentBalance + usedCreditBalance);
if(creditBalance < request.NetValue)
{
throw new Exception("Credit Limit is extend for this customer");
}
}
// 5. Insert or update
int orderId;
if (isUpdate)
@ -208,6 +223,33 @@ public class OrderService : IOrderService
return response;
}
private async Task<decimal> GetCurrentApprovedOrderAmountByCustomerAsync(TransactionContext tc, string customerCode, string salesOrgCode)
{
decimal totalApprovedAmount = 0;
try
{
SqlParameter[] p =
[
SqlHelperExtension.CreateInParam(pName: "@CustomerCode", pType: SqlDbType.NVarChar, pValue: customerCode),
SqlHelperExtension.CreateInParam(pName: "@SalesOrgCode", pType: SqlDbType.NVarChar, pValue: salesOrgCode),
];
using (IDataReader dr = await tc.ExecuteReaderSpAsync("dbo.GetCustomerApprovedAmount", p))
{
if (dr.Read())
{
totalApprovedAmount = dr.GetDecimal(0);
}
dr.Close();
}
}
catch (Exception)
{
throw;
}
return totalApprovedAmount;
}
private bool IsPermissiontoModify(OrderStatusEnum orderStatus, UserRoleTypeEnum roleId)
{
if( UserRoleTypeEnum.OrderCollector == roleId && ( OrderStatusEnum.Draft == orderStatus || OrderStatusEnum.InOrderValidation == orderStatus))
@ -308,7 +350,10 @@ public class OrderService : IOrderService
{
CustomerId = dr.GetInt32(0),
CustomerCode = dr.GetString(1),
CustomerName = dr.GetString(2)
CustomerName = dr.GetString(2),
IsCreditCustomer = dr.GetInt32(3) > 0,
CompanyCode = dr.GetString(4),
CreditLimit = dr.IsDBNull(5)? 0 : dr.GetDecimal(5)
};
}
@ -777,7 +822,9 @@ public class OrderService : IOrderService
GrossValue = dr.GetDecimal(13),
NetValue = dr.GetDecimal(14),
OrderStatus = dr.GetInt32(15),
TotalVatValue = dr.IsDBNull(17)? null: dr.GetDecimal(17)
TotalVatValue = dr.IsDBNull(17) ? null: dr.GetDecimal(17),
BillingNumber =dr.IsDBNull(18) ? null : dr.GetString(18),
BillingDate =dr.IsDBNull(19) ? null : dr.GetDateTime(19)
});
}

View File

@ -1,6 +1,7 @@
using System;
using System.DirectoryServices;
using System.IdentityModel.Tokens.Jwt;
using System.IO;
using System.Runtime.Versioning;
using System.Security.Claims;
using System.Text;
@ -309,6 +310,25 @@ namespace OnlineSalesAutoCrop.CoreAPI.Controllers
return Ok();
}
[AllowAnonymous]
[HttpGet("download")]
public IActionResult Download()
{
string fileName = "AutoCropCare.apk";
var filePath = Path.Combine(@"D:\Local", fileName);
if (!System.IO.File.Exists(filePath))
return NotFound();
return PhysicalFile(
filePath,
"application/octet-stream",
fileName,
enableRangeProcessing: true
);
}
/// <summary>
///
/// </summary>

View File

@ -611,6 +611,7 @@ namespace OnlineSalesAutoCrop.CoreAPI.Controllers.Web
/// <returns></returns>
[ValidateSession]
[HttpPost("uploadPasswords")]
[AllowAnonymous]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(BulkPasswordUploadResponse))]
public async Task<IActionResult> UploadPasswords([FromForm] BulkPasswordUploadRequest request)
{

View File

@ -17,7 +17,7 @@ public class NullableDecimalConverter : JsonConverter<decimal?>
if (reader.TokenType == JsonTokenType.Number)
{
var value = reader.GetInt32();
var value = reader.GetDecimal();
return value == 0 ? null : value;
}

View File

@ -12,6 +12,7 @@ using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
@ -462,11 +463,11 @@ namespace OnlineSalesAutoCrop.CoreAPI
app.UseStaticFiles();
#endregion
#endregion
#region Routing
#region Routing
app.UseRouting();
app.UseRouting();
#endregion

View File

@ -19,6 +19,7 @@ const routes: Routes = [
{ path: 'newuser', loadComponent: () => import('./users/newuser.component').then(m => m.NewUserComponent), canActivate: [AuthGuard], data: { permissionKey: 'ELIT.1.2.2_1' } },
{ path: 'edituser', loadComponent: () => import('./users/edituser.component').then(m => m.EditUserComponent), canActivate: [AuthGuard], data: { permissionKey: 'ELIT.1.2.2_2' } },
{ path: 'resetPwd', loadComponent: () => import('./users/resetpwd.component').then(m => m.ResetPwdComponent), canActivate: [AuthGuard], data: { permissionKey: 'ELIT.1.2.3,ELIT.1.2.3_1,ELIT.1.2.3_2' } },
{ path: 'uploadPwd', loadComponent: () => import('./users/uploadpwd.component').then(m => m.UploadPwdComponent), canActivate: [AuthGuard], data: { permissionKey: 'ELIT.1.2.3,ELIT.1.2.3_2' } },
{ path: 'forcelogout', loadComponent: () => import('./forcelogout/forcelogout.component').then(m => m.ForceLogoutComponent), canActivate: [AuthGuard], data: { permissionKey: 'ELIT.1.2.4,ELIT.1.2.4_1,ELIT.1.2.4_2,ELIT.1.2.4_3' } },
//Landing Page

View File

@ -50,6 +50,7 @@ export const MENU_DEFINITION: MenuNode[] =
[
{ moduleId: 'ELIT.1.2.1', label: 'Groups', icon: 'fa fa-address-book-o', routerLink: 'groups' },
{ moduleId: 'ELIT.1.2.2', label: 'Users', icon: 'fa fa-address-card-o', routerLink: 'users' },
{ moduleId: 'ELIT.1.2.3', label: 'Upload Passwords', icon: 'fa fa-upload', routerLink: 'uploadPwd' },
{ moduleId: 'ELIT.1.2.4', label: 'Force Logout', icon: 'fa fa-address-card-o', routerLink: 'forcelogout' }
]
},

View File

@ -0,0 +1,58 @@
<form [formGroup]="uploadPwdForm" (ngSubmit)="onSubmit()">
<div class="mb-2">
<span>Excel file must have <b>EmployeeCode</b> and <b>Password</b> columns in the first row.</span>
</div>
<div class="mb-2 row g-0">
<span class="required">Excel File:</span>
<br />
<div class="col-lg-4 col-md-8 col-sm-12">
<div class="custom-file">
<input type="file" class="custom-file-input" accept="{{acceptFileType}}" formControlName="uploadFile" name="uploadFile" id="uploadFile" (change)="onFileSelected($event)" [ngClass]="{ 'is-invalid': submitted && f.fileName.errors }">
<label class="custom-file-label" #uploadFileLabel for="uploadFile" data-browse="Browse"><i class="fa fa-search button-search"></i>&nbsp;Choose file</label>
</div>
@if (submitted && f.fileName.errors)
{
<div class="invalid-feedback d-block">
<span>Please select an excel file to upload.</span>
</div>
}
</div>
</div>
<div class="mb-2 col-lg-4 col-md-8 col-sm-12">
<div class="form-check">
<input type="checkbox" class="form-check-input" id="forcePasswordChange" formControlName="forcePasswordChange">
<label class="form-check-label" for="forcePasswordChange">Force user to change password at next login</label>
</div>
</div>
@if (canSave)
{
<div class="mb-2">
<hr class="osds" />
<button class="ui-button ui-widget ui-state-default ui-corner-all ui-button-raised ui-button-text-only" [disabled]="loading">
<span class="ui-button-text-only ui-c"><i class="fa fa-upload button-edit" aria-hidden="true"></i>&nbsp;&nbsp;Upload &amp; Update</span>
</button>
</div>
}
@if (totalRows > 0)
{
<div class="mb-2">
<span>Total Row(s): <b>{{totalRows}}</b> | Updated: <b>{{successCount}}</b> | Failed: <b>{{failedCount}}</b></span>
<br />
<kendo-grid id="grdUploadPwd"
[data]="items"
[resizable]="true"
[loading]="loading"
[rowClass]="rowCallback"
class="grid-font-900">
<kendo-grid-column field="rowNo" title="Row" [width]="70" class="cell-padding" [headerStyle]="{'font-weight':'bold'}" [style]="{'text-align':'center'}"></kendo-grid-column>
<kendo-grid-column field="employeeCode" title="Employee Code" [width]="150" class="cell-padding" [headerStyle]="{'font-weight':'bold'}"></kendo-grid-column>
<kendo-grid-column field="loginId" title="Login Id" [width]="150" class="cell-padding" [headerStyle]="{'font-weight':'bold'}"></kendo-grid-column>
<kendo-grid-column field="message" title="Remarks" class="cell-padding" [headerStyle]="{'font-weight':'bold'}"></kendo-grid-column>
</kendo-grid>
</div>
}
</form>

View File

@ -0,0 +1,143 @@
import { Router } from '@angular/router';
import { NgClass } from '@angular/common';
import { KENDO_GRID } from '@progress/kendo-angular-grid';
import { Component, ElementRef, OnInit, ViewChild, inject } from '@angular/core';
import { FormBuilder, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
import { AlertService } from '../components/alert';
import { BreadcrumbService } from '../breadcrumb.service';
import { UserService } from '../../providers/user/user.service.';
@Component({
standalone: true,
selector: 'user-upload-pwd',
templateUrl: './uploadpwd.component.html',
styles: ['#grdUploadPwd tr.failed{color:#f00;}'],
imports: [FormsModule, ReactiveFormsModule, NgClass, KENDO_GRID]
})
export class UploadPwdComponent implements OnInit
{
private readonly router = inject(Router);
private readonly service = inject(UserService);
private readonly formBuilder = inject(FormBuilder);
private readonly alertService = inject(AlertService);
@ViewChild('uploadFileLabel') private uploadFileLabel: ElementRef;
public uploadPwdForm: FormGroup;
public loading: boolean = false;
public canSave: boolean = false;
public submitted: boolean = false;
public items: any[] = [];
public totalRows: number = 0;
public successCount: number = 0;
public failedCount: number = 0;
public readonly acceptFileType: string = '.xls,.xlsx';
private formData: FormData;
constructor()
{
const bcService = inject(BreadcrumbService);
this.alertService.clear();
bcService.setItems([{ label: 'System Setup' }, { label: 'User Management' }, { label: 'Upload Passwords', routerLink: ['/uploadPwd'] }]);
if (!this.service.currentUserValue)
{
this.router.navigate(['/login']);
return;
}
this.canSave = this.service.currentUserValue?.moduleIds?.includes('ELIT.1.2.3_2');
}
ngOnInit()
{
this.uploadPwdForm = this.formBuilder.group(
{
uploadFile: '',
fileName: ['', Validators.required],
forcePasswordChange: false
});
}
get f() { return this.uploadPwdForm.controls; }
public rowCallback = (context: any) => { return { failed: !context.dataItem.updated }; };
public onFileSelected(event: any): void
{
this.clearData();
const selectedFile = event.target.files[0];
if (!selectedFile)
{
this.uploadPwdForm.patchValue({ fileName: '' });
return;
}
const fileName = selectedFile.name;
const extension = fileName.substring(fileName.lastIndexOf('.')).toLowerCase();
if (extension !== '.xls' && extension !== '.xlsx')
{
this.uploadPwdForm.patchValue({ fileName: '' });
this.alertService.error('Only excel (.xls/.xlsx) file is allowed to process.');
return;
}
this.uploadFileLabel.nativeElement.innerText = fileName;
this.uploadPwdForm.patchValue({ fileName: fileName });
this.formData = new FormData();
this.formData.append('fileName', fileName);
this.formData.append('fileData', selectedFile);
}
public clearData(): void
{
this.items = [];
this.totalRows = this.successCount = this.failedCount = 0;
}
public onSubmit(): void
{
this.submitted = true;
this.alertService.clear();
if (this.uploadPwdForm.invalid || !this.formData)
return;
this.clearData();
this.loading = true;
this.formData.set('forcePasswordChange', `${this.f.forcePasswordChange.value === true}`);
this.service.uploadPasswords(this.formData).subscribe(
{
next: (resp: any) =>
{
this.loading = false;
this.items = resp.items ?? [];
this.totalRows = resp.totalRows ?? 0;
this.failedCount = resp.failedCount ?? 0;
this.successCount = resp.successCount ?? 0;
if (resp.returnStatus === 200)
{
if (this.failedCount > 0)
this.alertService.info(resp.returnMessage);
else
this.alertService.success(resp.returnMessage);
this.submitted = false;
}
else
{
this.alertService.error(resp.returnMessage);
}
},
error: (errorMessage: string) =>
{
this.loading = false;
this.alertService.error(errorMessage);
}
});
}
}

View File

@ -114,6 +114,11 @@ export class UserService extends Base
return this.api.post('v1/users/resetPassword', params);
}
public uploadPasswords(formdata: FormData)
{
return this.api.uplaodFormData('v1/users/uploadPasswords', formdata);
}
public findAccount(accountId: string)
{
const params: any = { accountId: accountId };

View File

@ -0,0 +1,3 @@
{
"routes": {}
}