leave report
This commit is contained in:
parent
3ae9be5869
commit
8001dd0c11
12 changed files with 678 additions and 244 deletions
|
|
@ -88,7 +88,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
|
||||||
.Include(x => x.LeaveType)
|
.Include(x => x.LeaveType)
|
||||||
.FirstOrDefaultAsync(x => x.LeaveYear == year && x.LeaveTypeId == typeId && x.ProfileId == pf.Id);
|
.FirstOrDefaultAsync(x => x.LeaveYear == year && x.LeaveTypeId == typeId && x.ProfileId == pf.Id);
|
||||||
|
|
||||||
if(data == null)
|
if (data == null)
|
||||||
{
|
{
|
||||||
throw new Exception(GlobalMessages.DataNotFound);
|
throw new Exception(GlobalMessages.DataNotFound);
|
||||||
}
|
}
|
||||||
|
|
@ -165,5 +165,164 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<LeaveBeginning?> GetByYearAndTypeIdForUser2Async(int year, Guid typeId, Guid userId)
|
||||||
|
{
|
||||||
|
var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken);
|
||||||
|
if (pf == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var govAge = (pf?.DateStart?.Date ?? DateTime.Now.Date).DiffDay(DateTime.Now.Date);
|
||||||
|
|
||||||
|
var leaveType = await _dbContext.Set<LeaveType>().FirstOrDefaultAsync(x => x.Id == typeId);
|
||||||
|
|
||||||
|
var data = await _dbContext.Set<LeaveBeginning>()
|
||||||
|
.Include(x => x.LeaveType)
|
||||||
|
.FirstOrDefaultAsync(x => x.LeaveYear == year && x.LeaveTypeId == typeId && x.ProfileId == pf.Id);
|
||||||
|
|
||||||
|
if (data == null)
|
||||||
|
{
|
||||||
|
var limit = 0.0;
|
||||||
|
|
||||||
|
var prev = await _dbContext.Set<LeaveBeginning>()
|
||||||
|
.Include(x => x.LeaveType)
|
||||||
|
.FirstOrDefaultAsync(x => x.LeaveYear == year - 1 && x.LeaveTypeId == typeId && x.ProfileId == pf.Id);
|
||||||
|
|
||||||
|
var prevRemain = 0.0;
|
||||||
|
if (prev != null)
|
||||||
|
{
|
||||||
|
prevRemain = prev.LeaveDays - prev.LeaveDaysUsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (govAge >= 180)
|
||||||
|
{
|
||||||
|
if (govAge >= 3650)
|
||||||
|
{
|
||||||
|
limit = 10 + prevRemain;
|
||||||
|
if (limit > 30) limit = 30;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
limit = 10 + prevRemain;
|
||||||
|
if (limit > 20) limit = 20;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
limit = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = new LeaveBeginning
|
||||||
|
{
|
||||||
|
LeaveYear = year,
|
||||||
|
LeaveTypeId = typeId,
|
||||||
|
ProfileId = pf.Id,
|
||||||
|
Prefix = pf.Prefix,
|
||||||
|
FirstName = pf.FirstName,
|
||||||
|
LastName = pf.LastName,
|
||||||
|
LeaveDaysUsed = 0,
|
||||||
|
LeaveDays = leaveType?.Code == "LV-005" ? limit : 0
|
||||||
|
};
|
||||||
|
|
||||||
|
_dbContext.Set<LeaveBeginning>().Add(data);
|
||||||
|
await _dbContext.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<LeaveBeginning>> GetAllByYearAndTypeAsync(int year, Guid typeId, List<ProfileData> userIdList)
|
||||||
|
{
|
||||||
|
var updateList = new List<LeaveBeginning>();
|
||||||
|
var result = new List<LeaveBeginning>();
|
||||||
|
|
||||||
|
var beginningList = await _dbContext.Set<LeaveBeginning>()
|
||||||
|
.Include(x => x.LeaveType)
|
||||||
|
.Where(x => x.LeaveYear == year && x.LeaveTypeId == typeId)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
foreach (var pf in userIdList)
|
||||||
|
{
|
||||||
|
//var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(id, AccessToken);
|
||||||
|
//if (pf == null)
|
||||||
|
//{
|
||||||
|
// continue; // Goto Next Id
|
||||||
|
//}
|
||||||
|
|
||||||
|
var govAge = (pf?.DateStart?.Date ?? DateTime.Now.Date).DiffDay(DateTime.Now.Date);
|
||||||
|
|
||||||
|
var leaveType = await _dbContext.Set<LeaveType>().FirstOrDefaultAsync(x => x.Id == typeId);
|
||||||
|
|
||||||
|
var data = beginningList.FirstOrDefault(x => x.ProfileId == pf.Id);
|
||||||
|
|
||||||
|
if (data == null)
|
||||||
|
{
|
||||||
|
var limit = 0.0;
|
||||||
|
|
||||||
|
var prev = await _dbContext.Set<LeaveBeginning>()
|
||||||
|
.Include(x => x.LeaveType)
|
||||||
|
.FirstOrDefaultAsync(x => x.LeaveYear == year - 1 && x.LeaveTypeId == typeId && x.ProfileId == pf.Id);
|
||||||
|
|
||||||
|
var prevRemain = 0.0;
|
||||||
|
if (prev != null)
|
||||||
|
{
|
||||||
|
prevRemain = prev.LeaveDays - prev.LeaveDaysUsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (govAge >= 180)
|
||||||
|
{
|
||||||
|
if (govAge >= 3650)
|
||||||
|
{
|
||||||
|
limit = 10 + prevRemain;
|
||||||
|
if (limit > 30) limit = 30;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
limit = 10 + prevRemain;
|
||||||
|
if (limit > 20) limit = 20;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
limit = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = new LeaveBeginning
|
||||||
|
{
|
||||||
|
LeaveYear = year,
|
||||||
|
LeaveTypeId = typeId,
|
||||||
|
ProfileId = pf.Id,
|
||||||
|
Prefix = pf.Prefix,
|
||||||
|
FirstName = pf.FirstName,
|
||||||
|
LastName = pf.LastName,
|
||||||
|
LeaveDaysUsed = 0,
|
||||||
|
LeaveDays = leaveType?.Code == "LV-005" ? limit : 0
|
||||||
|
};
|
||||||
|
|
||||||
|
updateList.Add(data);
|
||||||
|
}
|
||||||
|
result.Add(data);
|
||||||
|
}
|
||||||
|
if (!updateList.Any())
|
||||||
|
{
|
||||||
|
await _dbContext.Set<LeaveBeginning>().AddRangeAsync(updateList);
|
||||||
|
await _dbContext.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ProfileData
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; } = Guid.Empty;
|
||||||
|
|
||||||
|
public string Prefix { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string FirstName { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string LastName { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public DateTime? DateStart { get; set; } = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1454,7 +1454,8 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests
|
||||||
KeycloakUserId = grp.Key.KeycloakUserId,
|
KeycloakUserId = grp.Key.KeycloakUserId,
|
||||||
LeaveTypeId = grp.Key.LeaveTypeId,
|
LeaveTypeId = grp.Key.LeaveTypeId,
|
||||||
LeaveTypeCode = grp.Key.LeaveTypeCode,
|
LeaveTypeCode = grp.Key.LeaveTypeCode,
|
||||||
SumLeaveDay = grp.Sum(x => x.LeaveTotal)
|
SumLeaveDay = grp.Sum(x => x.LeaveTotal),
|
||||||
|
CountLeaveDay = grp.Count()
|
||||||
})
|
})
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@
|
||||||
|
|
||||||
public string LeaveTypeCode { get; set; } = string.Empty;
|
public string LeaveTypeCode { get; set; } = string.Empty;
|
||||||
|
|
||||||
public double SumLeaveDay { get; set; }
|
public double SumLeaveDay { get; set; } = 0.0;
|
||||||
|
|
||||||
|
public int CountLeaveDay { get; set; } = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,5 +22,9 @@ namespace BMA.EHR.Application.Responses.Profiles
|
||||||
public string? OrgChild3Id { get; set; }
|
public string? OrgChild3Id { get; set; }
|
||||||
public string? OrgChild4Id { get; set; }
|
public string? OrgChild4Id { get; set; }
|
||||||
|
|
||||||
|
public DateTime? DateStart { get; set; }
|
||||||
|
|
||||||
|
public DateTime? DateAppoint { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
|
|
@ -41,7 +41,7 @@
|
||||||
<PackageReference Include="Serilog.Exceptions" Version="8.4.0" />
|
<PackageReference Include="Serilog.Exceptions" Version="8.4.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.Debug" Version="2.0.0" />
|
<PackageReference Include="Serilog.Sinks.Debug" Version="2.0.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.Elasticsearch" Version="9.0.3" />
|
<PackageReference Include="Serilog.Sinks.Elasticsearch" Version="9.0.3" />
|
||||||
<PackageReference Include="SocketIoClientDotNet" Version="0.9.13" />
|
<PackageReference Include="SocketIOClient" Version="3.0.8" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.5.0" />
|
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.5.0" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
||||||
16
BMA.EHR.Insignia/Configuration/WebSocketConfiguration.cs
Normal file
16
BMA.EHR.Insignia/Configuration/WebSocketConfiguration.cs
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
namespace BMA.EHR.Insignia.Service.Configuration
|
||||||
|
{
|
||||||
|
public class WebSocketConfiguration
|
||||||
|
{
|
||||||
|
public const string SectionName = "WebSocket";
|
||||||
|
|
||||||
|
public string Url { get; set; } = "https://bma-ehr.frappet.synology.me";
|
||||||
|
public string Path { get; set; } = "/api/v1/org-socket";
|
||||||
|
public string DefaultUserId { get; set; } = "4064c2b2-0414-464a-97c6-4a47c325b9a3";
|
||||||
|
public int ReconnectionDelay { get; set; } = 1000;
|
||||||
|
public int ReconnectionAttempts { get; set; } = 5;
|
||||||
|
public int Timeout { get; set; } = 20000;
|
||||||
|
public bool AutoReconnect { get; set; } = true;
|
||||||
|
public int TaskDelayOnError { get; set; } = 5000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,71 +1,63 @@
|
||||||
using Microsoft.Extensions.Hosting;
|
using BMA.EHR.Insignia.Service.Configuration;
|
||||||
using Quobject.SocketIoClientDotNet.Client;
|
using SocketIOClient;
|
||||||
using Sentry;
|
using System.Security.Claims;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace BMA.EHR.Insignia.Service.Services;
|
namespace BMA.EHR.Insignia.Service.Services;
|
||||||
|
|
||||||
public class InsigniaRequestProcessService : BackgroundService
|
public class InsigniaRequestProcessService : BackgroundService
|
||||||
{
|
{
|
||||||
private readonly IBackgroundTaskQueue _queue;
|
private readonly IBackgroundTaskQueue _queue;
|
||||||
private Socket _socket;
|
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||||
private bool _isConnected = false;
|
|
||||||
|
|
||||||
public InsigniaRequestProcessService(IBackgroundTaskQueue queue)
|
public InsigniaRequestProcessService(
|
||||||
|
IBackgroundTaskQueue queue,
|
||||||
|
IHttpContextAccessor httpContextAccessor)
|
||||||
{
|
{
|
||||||
_queue = queue;
|
_queue = queue;
|
||||||
|
_httpContextAccessor = httpContextAccessor;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Task StartAsync(CancellationToken cancellationToken)
|
#region " Properties "
|
||||||
|
|
||||||
|
protected string? UserId => _httpContextAccessor?.HttpContext?.User?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
||||||
|
|
||||||
|
protected string? FullName => _httpContextAccessor?.HttpContext?.User?.FindFirst("name")?.Value;
|
||||||
|
|
||||||
|
protected bool? IsPlacementAdmin => _httpContextAccessor?.HttpContext?.User?.IsInRole("placement1");
|
||||||
|
|
||||||
|
protected string? AccessToken => _httpContextAccessor?.HttpContext?.Request.Headers["Authorization"];
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public override async Task StartAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
try
|
var client = new SocketIO("https://bma-ehr.frappet.synology.me/api/v1/org-socket",
|
||||||
|
new SocketIOOptions
|
||||||
|
{
|
||||||
|
// เพิ่ม token ใน handshake.auth
|
||||||
|
Auth = new { token = AccessToken ?? "" }
|
||||||
|
});
|
||||||
|
|
||||||
|
client.OnConnected += async (sender, e) =>
|
||||||
{
|
{
|
||||||
_socket = IO.Socket("https://bma-ehr.frappet.synology.me", new IO.Options
|
Console.WriteLine("Connected to Socket.IO server");
|
||||||
{
|
await client.EmitAsync("eventName", "Hello from .NET client");
|
||||||
Path = "/api/v1/org-socket",
|
};
|
||||||
//Query = new Dictionary<string, string>
|
|
||||||
//{
|
|
||||||
// { "EIO", "4" },
|
|
||||||
// { "transport", "polling" },
|
|
||||||
// { "t", "tkitfptn" }
|
|
||||||
//}
|
|
||||||
});
|
|
||||||
|
|
||||||
_socket.On(Socket.EVENT_CONNECT, () =>
|
await client.ConnectAsync();
|
||||||
{
|
|
||||||
_isConnected = true;
|
|
||||||
Console.WriteLine("Connected to WebSocket server at: https://bma-ehr.frappet.synology.me/api/v1/org-socket");
|
|
||||||
});
|
|
||||||
|
|
||||||
_socket.On(Socket.EVENT_DISCONNECT, () =>
|
await base.StartAsync(cancellationToken);
|
||||||
{
|
|
||||||
_isConnected = false;
|
|
||||||
Console.WriteLine("Disconnected from WebSocket server");
|
|
||||||
});
|
|
||||||
|
|
||||||
_socket.On(Socket.EVENT_ERROR, (data) =>
|
|
||||||
{
|
|
||||||
_isConnected = false;
|
|
||||||
Console.WriteLine($"WebSocket error: {data}");
|
|
||||||
});
|
|
||||||
|
|
||||||
_socket.Connect();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_isConnected = false;
|
|
||||||
Console.WriteLine($"Failed to initialize WebSocket connection: {ex.Message}");
|
|
||||||
}
|
|
||||||
|
|
||||||
return base.StartAsync(cancellationToken);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
{
|
{
|
||||||
var userId = "4064c2b2-0414-464a-97c6-4a47c325b9a3";
|
|
||||||
|
|
||||||
while (!stoppingToken.IsCancellationRequested)
|
while (!stoppingToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
|
|
@ -74,80 +66,34 @@ public class InsigniaRequestProcessService : BackgroundService
|
||||||
var workItem = await _queue.DequeueAsync(stoppingToken);
|
var workItem = await _queue.DequeueAsync(stoppingToken);
|
||||||
if (workItem != null)
|
if (workItem != null)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Starting background task at: {DateTime.Now}");
|
var startTime = DateTime.Now;
|
||||||
await workItem(stoppingToken);
|
|
||||||
Console.WriteLine($"Finished background task at: {DateTime.Now}");
|
await workItem(stoppingToken);
|
||||||
|
|
||||||
|
var endTime = DateTime.Now;
|
||||||
|
var duration = endTime - startTime;
|
||||||
|
|
||||||
// Send notification to WebSocket after task completion
|
|
||||||
if (_socket != null && _isConnected)
|
|
||||||
{
|
|
||||||
_socket.Emit("send-command-notification",
|
|
||||||
new
|
|
||||||
{
|
|
||||||
success = true,
|
|
||||||
message = "Background Task Completed Successfully",
|
|
||||||
payload = new {
|
|
||||||
completedAt = DateTime.Now,
|
|
||||||
taskType = "background_processing"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new
|
|
||||||
{
|
|
||||||
userId = userId
|
|
||||||
});
|
|
||||||
|
|
||||||
Console.WriteLine("WebSocket notification sent successfully");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Console.WriteLine("WebSocket is not connected. Unable to send notification.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Error processing background task: {ex.Message}");
|
// รอสักครู่ก่อนประมวลผล task ถัดไป เพื่อป้องกันการวนลูปข้อผิดพลาด
|
||||||
|
await Task.Delay(1000, stoppingToken);
|
||||||
// Send error notification to WebSocket
|
|
||||||
if (_socket != null && _isConnected)
|
|
||||||
{
|
|
||||||
_socket.Emit("send-command-notification",
|
|
||||||
new
|
|
||||||
{
|
|
||||||
success = false,
|
|
||||||
message = "Background Task Failed",
|
|
||||||
payload = new {
|
|
||||||
error = ex.Message,
|
|
||||||
failedAt = DateTime.Now,
|
|
||||||
taskType = "background_processing"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new
|
|
||||||
{
|
|
||||||
userId = userId
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Task StopAsync(CancellationToken cancellationToken)
|
public override async Task StopAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
try
|
|
||||||
{
|
|
||||||
if (_socket != null)
|
await base.StopAsync(cancellationToken);
|
||||||
{
|
|
||||||
Console.WriteLine("Disconnecting from WebSocket server...");
|
|
||||||
_socket.Disconnect();
|
|
||||||
_isConnected = false;
|
|
||||||
_socket = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Error disconnecting WebSocket: {ex.Message}");
|
|
||||||
}
|
|
||||||
|
|
||||||
return base.StopAsync(cancellationToken);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
97
BMA.EHR.Insignia/WebSocket-Improvements.md
Normal file
97
BMA.EHR.Insignia/WebSocket-Improvements.md
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
# WebSocket Connection Improvements for InsigniaRequestProcessService
|
||||||
|
|
||||||
|
## ???????????????????
|
||||||
|
|
||||||
|
### 1. **????????? Configuration**
|
||||||
|
- ????? `WebSocketConfiguration` class ?????????????????????
|
||||||
|
- ?????????????????????? ???? appsettings.json
|
||||||
|
- ?? default values ???????????????????????
|
||||||
|
|
||||||
|
### 2. **??????????????????????? WebSocket**
|
||||||
|
- ??? `ImmutableList.Create()` ?????? Transports
|
||||||
|
- ????? event handlers ????????????????????????
|
||||||
|
- ?????? reconnection ?????????
|
||||||
|
- Thread-safe ???? lock mechanism
|
||||||
|
|
||||||
|
### 3. **??????????????? Logging**
|
||||||
|
- ??? `ILogger<T>` ??? Console.WriteLine
|
||||||
|
- ????? emoji ??????????????? log ???????????
|
||||||
|
- Log ??????????????????????????
|
||||||
|
|
||||||
|
### 4. **???????????????????**
|
||||||
|
- Proper exception handling ????????
|
||||||
|
- Graceful handling ??? cancellation
|
||||||
|
- Delay ??????????????????????????????? busy loop
|
||||||
|
|
||||||
|
### 5. **????????????????? Notification**
|
||||||
|
- ???????????????????????????????
|
||||||
|
- ????????????????????????????????
|
||||||
|
- ??????????? retry ?????????
|
||||||
|
|
||||||
|
## ?????????
|
||||||
|
|
||||||
|
### ???????????? appsettings.json
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"WebSocket": {
|
||||||
|
"Url": "https://bma-ehr.frappet.synology.me",
|
||||||
|
"Path": "/api/v1/org-socket",
|
||||||
|
"DefaultUserId": "4064c2b2-0414-464a-97c6-4a47c325b9a3",
|
||||||
|
"ReconnectionDelay": 1000,
|
||||||
|
"ReconnectionAttempts": 5,
|
||||||
|
"Timeout": 20000,
|
||||||
|
"AutoReconnect": true,
|
||||||
|
"TaskDelayOnError": 5000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Log Messages ????????
|
||||||
|
- ?? = Service started
|
||||||
|
- ?? = Task processing
|
||||||
|
- ? = Success operations
|
||||||
|
- ? = Error/Failure
|
||||||
|
- ?? = Reconnection attempts
|
||||||
|
- ?? = WebSocket notifications
|
||||||
|
- ?? = Service stopping
|
||||||
|
|
||||||
|
## ???????????????????
|
||||||
|
|
||||||
|
1. **Reliability**: ???????????? WebSocket ???????????????????
|
||||||
|
2. **Observability**: ???????????? log ???????
|
||||||
|
3. **Configurability**: ??????????????????????? configuration
|
||||||
|
4. **Maintainability**: ???????????????????????? ??????????
|
||||||
|
5. **Resilience**: ?????????????????????????
|
||||||
|
|
||||||
|
## WebSocket Events ?????????
|
||||||
|
|
||||||
|
- `EVENT_CONNECT`: ???????????????
|
||||||
|
- `EVENT_DISCONNECT`: ????????????????
|
||||||
|
- `EVENT_ERROR`: ????????????????
|
||||||
|
- `EVENT_CONNECT_ERROR`: ????????????????????????
|
||||||
|
- `EVENT_RECONNECT`: ???????????????????
|
||||||
|
- `EVENT_RECONNECT_ERROR`: ????????????????????????????
|
||||||
|
- `EVENT_RECONNECT_FAILED`: ?????????????????????????
|
||||||
|
|
||||||
|
## Message Format ??????????? WebSocket
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"success": true/false,
|
||||||
|
"message": "Task Finish" ???? "Task Failed",
|
||||||
|
"payload": {
|
||||||
|
"completedAt": "timestamp",
|
||||||
|
"taskType": "insignia_background_processing",
|
||||||
|
"duration": 1234.56, // milliseconds
|
||||||
|
"status": "success" ???? "failed"
|
||||||
|
// ?????????? error ???? error ??? stackTrace ?????
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
???????? user data:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"userId": "4064c2b2-0414-464a-97c6-4a47c325b9a3"
|
||||||
|
}
|
||||||
|
```
|
||||||
12
BMA.EHR.Insignia/appsettings.websocket.example.json
Normal file
12
BMA.EHR.Insignia/appsettings.websocket.example.json
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"WebSocket": {
|
||||||
|
"Url": "https://bma-ehr.frappet.synology.me",
|
||||||
|
"Path": "/api/v1/org-socket",
|
||||||
|
"DefaultUserId": "4064c2b2-0414-464a-97c6-4a47c325b9a3",
|
||||||
|
"ReconnectionDelay": 1000,
|
||||||
|
"ReconnectionAttempts": 5,
|
||||||
|
"Timeout": 20000,
|
||||||
|
"AutoReconnect": true,
|
||||||
|
"TaskDelayOnError": 5000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -53,6 +53,7 @@
|
||||||
<PackageReference Include="Serilog.Exceptions" Version="8.4.0" />
|
<PackageReference Include="Serilog.Exceptions" Version="8.4.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.Debug" Version="2.0.0" />
|
<PackageReference Include="Serilog.Sinks.Debug" Version="2.0.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.Elasticsearch" Version="9.0.3" />
|
<PackageReference Include="Serilog.Sinks.Elasticsearch" Version="9.0.3" />
|
||||||
|
<PackageReference Include="SocketIOClient" Version="3.1.2" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.5.0" />
|
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.5.0" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||||
<PackageReference Include="System.Threading.Channels" Version="8.0.0" />
|
<PackageReference Include="System.Threading.Channels" Version="8.0.0" />
|
||||||
|
|
|
||||||
|
|
@ -6,29 +6,17 @@ using BMA.EHR.Application.Repositories.MetaData;
|
||||||
using BMA.EHR.Application.Responses.Profiles;
|
using BMA.EHR.Application.Responses.Profiles;
|
||||||
using BMA.EHR.Domain.Common;
|
using BMA.EHR.Domain.Common;
|
||||||
using BMA.EHR.Domain.Extensions;
|
using BMA.EHR.Domain.Extensions;
|
||||||
using BMA.EHR.Domain.Models.Leave.Commons;
|
|
||||||
using BMA.EHR.Domain.Models.Leave.Requests;
|
using BMA.EHR.Domain.Models.Leave.Requests;
|
||||||
using BMA.EHR.Domain.Models.MetaData;
|
|
||||||
using BMA.EHR.Domain.ModelsExam.Candidate;
|
|
||||||
using BMA.EHR.Domain.Shared;
|
using BMA.EHR.Domain.Shared;
|
||||||
using BMA.EHR.Leave.Service.DTOs.Reports;
|
using BMA.EHR.Leave.Service.DTOs.Reports;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.OpenApi.Any;
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using Org.BouncyCastle.Asn1.Pkcs;
|
using OfficeOpenXml;
|
||||||
using Org.BouncyCastle.Ocsp;
|
|
||||||
using Sentry;
|
|
||||||
using Swashbuckle.AspNetCore.Annotations;
|
using Swashbuckle.AspNetCore.Annotations;
|
||||||
using System.Diagnostics.Eventing.Reader;
|
|
||||||
using System.Diagnostics.Metrics;
|
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using static Nest.JoinField;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using OfficeOpenXml;
|
|
||||||
using Microsoft.AspNetCore.Routing.Template;
|
|
||||||
namespace BMA.EHR.Leave.Service.Controllers
|
namespace BMA.EHR.Leave.Service.Controllers
|
||||||
{
|
{
|
||||||
[Route("api/v{version:apiVersion}/leave/report")]
|
[Route("api/v{version:apiVersion}/leave/report")]
|
||||||
|
|
@ -1000,6 +988,8 @@ namespace BMA.EHR.Leave.Service.Controllers
|
||||||
var leaveDays = await _leaveRequestRepository.GetSumApproveLeaveByTypeAndRange(req.StartDate, req.EndDate);
|
var leaveDays = await _leaveRequestRepository.GetSumApproveLeaveByTypeAndRange(req.StartDate, req.EndDate);
|
||||||
var leaveTypes = await _leaveTypeRepository.GetAllAsync();
|
var leaveTypes = await _leaveTypeRepository.GetAllAsync();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var count = 1;
|
var count = 1;
|
||||||
var employees = new List<dynamic>();
|
var employees = new List<dynamic>();
|
||||||
// กรองตามที่ fe ส่งมา
|
// กรองตามที่ fe ส่งมา
|
||||||
|
|
@ -1009,122 +999,327 @@ namespace BMA.EHR.Leave.Service.Controllers
|
||||||
.Where(x => req.node == 4 ? x.OrgChild4Id == req.nodeId : req.node == 3 ? x.OrgChild3Id == req.nodeId : req.node == 2 ? x.OrgChild2Id == req.nodeId : req.node == 1 ? x.OrgChild1Id == req.nodeId : req.node == 0 ? x.OrgRootId == req.nodeId : true)
|
.Where(x => req.node == 4 ? x.OrgChild4Id == req.nodeId : req.node == 3 ? x.OrgChild3Id == req.nodeId : req.node == 2 ? x.OrgChild2Id == req.nodeId : req.node == 1 ? x.OrgChild1Id == req.nodeId : req.node == 0 ? x.OrgRootId == req.nodeId : true)
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var reportType = req!.Type!.Trim().ToUpper();
|
||||||
|
var year = req.EndDate.Year;
|
||||||
|
var profileList = profile.Select(x => new ProfileData
|
||||||
|
{
|
||||||
|
Id = x.Id,
|
||||||
|
Prefix = x.Prefix ?? "",
|
||||||
|
FirstName = x.FirstName ?? "",
|
||||||
|
LastName = x.LastName ?? "",
|
||||||
|
DateStart = x.DateStart ?? x.DateAppoint,
|
||||||
|
}).Distinct().ToList();
|
||||||
|
|
||||||
|
var beginningData = await _leaveBeginningRepository.GetAllByYearAsync(year);
|
||||||
|
|
||||||
|
// sum all user
|
||||||
|
//var sickDaySumALL = await _leaveBeginningRepository.GetAllByYearAndTypeAsync(year, leaveTypes.FirstOrDefault(x => x.Code == "LV-001")?.Id ?? Guid.Empty, profileList!);
|
||||||
|
//var personalDaySumALL = await _leaveBeginningRepository.GetAllByYearAndTypeAsync(year, leaveTypes.FirstOrDefault(x => x.Code == "LV-002")?.Id ?? Guid.Empty, profileList!);
|
||||||
|
//var maternityDaySumALL = await _leaveBeginningRepository.GetAllByYearAndTypeAsync(year, leaveTypes.FirstOrDefault(x => x.Code == "LV-003")?.Id ?? Guid.Empty, profileList!);
|
||||||
|
//var wifeDaySumALL = await _leaveBeginningRepository.GetAllByYearAndTypeAsync(year, leaveTypes.FirstOrDefault(x => x.Code == "LV-004")?.Id ?? Guid.Empty, profileList!);
|
||||||
|
//var restDaySumALL = await _leaveBeginningRepository.GetAllByYearAndTypeAsync(year, leaveTypes.FirstOrDefault(x => x.Code == "LV-005")?.Id ?? Guid.Empty, profileList!);
|
||||||
|
//var ordainDaySumALL = await _leaveBeginningRepository.GetAllByYearAndTypeAsync(year, leaveTypes.FirstOrDefault(x => x.Code == "LV-006")?.Id ?? Guid.Empty, profileList!);
|
||||||
|
//var absentDaySumALL = await _leaveBeginningRepository.GetAllByYearAndTypeAsync(year, leaveTypes.FirstOrDefault(x => x.Code == "LV-007")?.Id ?? Guid.Empty, profileList!);
|
||||||
|
//var studyDaySumALL = await _leaveBeginningRepository.GetAllByYearAndTypeAsync(year, leaveTypes.FirstOrDefault(x => x.Code == "LV-008")?.Id ?? Guid.Empty, profileList!);
|
||||||
|
//var agencyDaySumALL = await _leaveBeginningRepository.GetAllByYearAndTypeAsync(year, leaveTypes.FirstOrDefault(x => x.Code == "LV-009")?.Id ?? Guid.Empty, profileList!);
|
||||||
|
//var coupleDaySumALL = await _leaveBeginningRepository.GetAllByYearAndTypeAsync(year, leaveTypes.FirstOrDefault(x => x.Code == "LV-010")?.Id ?? Guid.Empty, profileList!);
|
||||||
|
//var therapyDaySumALL = await _leaveBeginningRepository.GetAllByYearAndTypeAsync(year, leaveTypes.FirstOrDefault(x => x.Code == "LV-011")?.Id ?? Guid.Empty, profileList!);
|
||||||
|
|
||||||
foreach (var p in profile)
|
foreach (var p in profile)
|
||||||
{
|
{
|
||||||
var keycloakUserId = p.Keycloak ?? Guid.Empty;
|
var keycloakUserId = p.Keycloak ?? Guid.Empty;
|
||||||
|
|
||||||
var sickDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-001");
|
if (reportType == "FULL")
|
||||||
var sickDayCount = sickDay != null ? sickDay.SumLeaveDay : 0;
|
|
||||||
|
|
||||||
var personalDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-002");
|
|
||||||
var personalDayCount = personalDay != null ? personalDay.SumLeaveDay : 0;
|
|
||||||
|
|
||||||
var maternityDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-003");
|
|
||||||
var maternityDayCount = maternityDay != null ? maternityDay.SumLeaveDay : 0;
|
|
||||||
|
|
||||||
var wifeDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-004");
|
|
||||||
var wifeDayCount = wifeDay != null ? wifeDay.SumLeaveDay : 0;
|
|
||||||
|
|
||||||
var restDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-005");
|
|
||||||
var restDayCount = restDay != null ? restDay.SumLeaveDay : 0;
|
|
||||||
|
|
||||||
var ordainDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-006");
|
|
||||||
var ordainDayCount = ordainDay != null ? ordainDay.SumLeaveDay : 0;
|
|
||||||
|
|
||||||
var absentDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-007");
|
|
||||||
var absentDayCount = absentDay != null ? absentDay.SumLeaveDay : 0;
|
|
||||||
|
|
||||||
var studyDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-008");
|
|
||||||
var studyDayCount = studyDay != null ? studyDay.SumLeaveDay : 0;
|
|
||||||
|
|
||||||
var agencyDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-009");
|
|
||||||
var agencyDayCount = agencyDay != null ? agencyDay.SumLeaveDay : 0;
|
|
||||||
|
|
||||||
var coupleDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-010");
|
|
||||||
var coupleDayCount = coupleDay != null ? coupleDay.SumLeaveDay : 0;
|
|
||||||
|
|
||||||
var therapyDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-011");
|
|
||||||
var therapyDayCount = therapyDay != null ? therapyDay.SumLeaveDay : 0;
|
|
||||||
|
|
||||||
var timeStamps = await _processUserTimeStampRepository.GetTimeStampHistoryByRangeForUserAsync(p.Keycloak ?? Guid.Empty, req.StartDate, req.EndDate);
|
|
||||||
|
|
||||||
var defaultRound = await _dutyTimeRepository.GetDefaultAsync();
|
|
||||||
if (defaultRound == null)
|
|
||||||
{
|
{
|
||||||
return Error("ไม่พบรอบการลงเวลา Default", StatusCodes.Status404NotFound);
|
|
||||||
|
|
||||||
|
var sickDaySum = beginningData.FirstOrDefault(x => x.ProfileId == p.Id && x.LeaveType.Code == "LV-001");
|
||||||
|
var sickDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-001");
|
||||||
|
var sickDayCount = sickDaySum != null ? sickDaySum.LeaveDaysUsed : 0;
|
||||||
|
var sickCount = sickDay != null ? sickDay.CountLeaveDay : 0;
|
||||||
|
|
||||||
|
var personalDaySum = beginningData.FirstOrDefault(x => x.ProfileId == p.Id && x.LeaveType.Code == "LV-002");
|
||||||
|
var personalDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-002");
|
||||||
|
var personalDayCount = personalDaySum != null ? personalDaySum.LeaveDaysUsed : 0;
|
||||||
|
var personalCount = personalDay != null ? personalDay.CountLeaveDay : 0;
|
||||||
|
|
||||||
|
var maternityDaySum = beginningData.FirstOrDefault(x => x.ProfileId == p.Id && x.LeaveType.Code == "LV-003");
|
||||||
|
var maternityDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-003");
|
||||||
|
var maternityDayCount = maternityDaySum != null ? maternityDaySum.LeaveDaysUsed : 0;
|
||||||
|
var maternityCount = maternityDay != null ? maternityDay.CountLeaveDay : 0;
|
||||||
|
|
||||||
|
var wifeDaySum = beginningData.FirstOrDefault(x => x.ProfileId == p.Id && x.LeaveType.Code == "LV-004");
|
||||||
|
var wifeDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-004");
|
||||||
|
var wifeDayCount = wifeDaySum != null ? wifeDaySum.LeaveDaysUsed : 0;
|
||||||
|
var wifeCount = wifeDay != null ? wifeDay.CountLeaveDay : 0;
|
||||||
|
|
||||||
|
var restDaySum = beginningData.FirstOrDefault(x => x.ProfileId == p.Id && x.LeaveType.Code == "LV-005");
|
||||||
|
var restDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-005");
|
||||||
|
var restDayCount = restDaySum != null ? restDaySum.LeaveDaysUsed : 0;
|
||||||
|
var restCount = restDay != null ? restDay.CountLeaveDay : 0;
|
||||||
|
|
||||||
|
var ordainDaySum = beginningData.FirstOrDefault(x => x.ProfileId == p.Id && x.LeaveType.Code == "LV-006");
|
||||||
|
var ordainDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-006");
|
||||||
|
var ordainDayCount = ordainDaySum != null ? ordainDaySum.LeaveDaysUsed : 0;
|
||||||
|
var ordainCount = ordainDay != null ? ordainDay.CountLeaveDay : 0;
|
||||||
|
|
||||||
|
var absentDaySum = beginningData.FirstOrDefault(x => x.ProfileId == p.Id && x.LeaveType.Code == "LV-007");
|
||||||
|
var absentDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-007");
|
||||||
|
var absentDayCount = absentDaySum != null ? absentDaySum.LeaveDaysUsed : 0;
|
||||||
|
var absentCount = absentDay != null ? absentDay.CountLeaveDay : 0;
|
||||||
|
|
||||||
|
var studyDaySum = beginningData.FirstOrDefault(x => x.ProfileId == p.Id && x.LeaveType.Code == "LV-008");
|
||||||
|
var studyDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-008");
|
||||||
|
var studyDayCount = studyDaySum != null ? studyDaySum.LeaveDaysUsed : 0;
|
||||||
|
var studyCount = studyDay != null ? studyDay.CountLeaveDay : 0;
|
||||||
|
|
||||||
|
var agencyDaySum = beginningData.FirstOrDefault(x => x.ProfileId == p.Id && x.LeaveType.Code == "LV-009");
|
||||||
|
var agencyDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-009");
|
||||||
|
var agencyDayCount = agencyDaySum != null ? agencyDaySum.LeaveDaysUsed : 0;
|
||||||
|
var agencyCount = agencyDay != null ? agencyDay.CountLeaveDay : 0;
|
||||||
|
|
||||||
|
var coupleDaySum = beginningData.FirstOrDefault(x => x.ProfileId == p.Id && x.LeaveType.Code == "LV-010");
|
||||||
|
var coupleDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-010");
|
||||||
|
var coupleDayCount = coupleDaySum != null ? coupleDaySum.LeaveDaysUsed : 0;
|
||||||
|
var coupleCount = coupleDay != null ? coupleDay.CountLeaveDay : 0;
|
||||||
|
|
||||||
|
var therapyDaySum = beginningData.FirstOrDefault(x => x.ProfileId == p.Id && x.LeaveType.Code == "LV-011");
|
||||||
|
var therapyDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-011");
|
||||||
|
var therapyDayCount = therapyDaySum != null ? therapyDaySum.LeaveDaysUsed : 0;
|
||||||
|
var therapyCount = therapyDay != null ? therapyDay.CountLeaveDay : 0;
|
||||||
|
|
||||||
|
var timeStamps = await _processUserTimeStampRepository.GetTimeStampHistoryByRangeForUserAsync(p.Keycloak ?? Guid.Empty, req.StartDate, req.EndDate);
|
||||||
|
|
||||||
|
var defaultRound = await _dutyTimeRepository.GetDefaultAsync();
|
||||||
|
if (defaultRound == null)
|
||||||
|
{
|
||||||
|
return Error("ไม่พบรอบการลงเวลา Default", StatusCodes.Status404NotFound);
|
||||||
|
}
|
||||||
|
//var userRound = await _dutyTimeRepository.GetByIdAsync(profile.DutyTimeId ?? Guid.Empty);
|
||||||
|
var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(p.Id);
|
||||||
|
var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty;
|
||||||
|
var userRound = await _dutyTimeRepository.GetByIdAsync(roundId);
|
||||||
|
|
||||||
|
var duty = userRound ?? defaultRound;
|
||||||
|
|
||||||
|
/* var processTimeStamps = timeStamps
|
||||||
|
.Select(d => new
|
||||||
|
{
|
||||||
|
d.Id,
|
||||||
|
CheckInStatus = DateTime.Parse(d.CheckIn.ToString("yyyy-MM-dd HH:mm")) >
|
||||||
|
DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}") ?
|
||||||
|
"LATE" :
|
||||||
|
"NORMAL",
|
||||||
|
CheckOutStatus = d.CheckOut == null ? "" :
|
||||||
|
DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) <
|
||||||
|
DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ?
|
||||||
|
"LATE" :
|
||||||
|
DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) <
|
||||||
|
DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ?
|
||||||
|
"ABSENT" :
|
||||||
|
"NORMAL",
|
||||||
|
});*/
|
||||||
|
|
||||||
|
/*var absentCount = processTimeStamps.Count(x => x.CheckOutStatus == "ABSENT");
|
||||||
|
var lateCount = processTimeStamps.Count(x => x.CheckInStatus == "LATE");*/
|
||||||
|
|
||||||
|
var absentCount1 = timeStamps.Count(d =>
|
||||||
|
d.CheckOutStatus == "ABSENT" || d.CheckInStatus == "ABSENT"); // นับจำนวนที่มี CheckOutStatus == "ABSENT"
|
||||||
|
var lateCount1 = timeStamps.Count(d =>
|
||||||
|
d.CheckInStatus == "LATE"); // นับจำนวนที่มี CheckInStatus == "LATE"
|
||||||
|
|
||||||
|
|
||||||
|
var emp = new
|
||||||
|
{
|
||||||
|
no = count,
|
||||||
|
fullName = $"{p.Prefix}{p.FirstName} {p.LastName}",
|
||||||
|
position = p.Position == null ? "" : p.Position,
|
||||||
|
positionLevel = p.PositionLevel == null ? "" : p.PositionLevel,
|
||||||
|
posNo = p.PosNo == null ? "" : p.PosNo,
|
||||||
|
reason = "",
|
||||||
|
sickDayCount = sickDayCount,
|
||||||
|
maternityDayCount = maternityDayCount,
|
||||||
|
wifeDayCount = wifeDayCount,
|
||||||
|
personalDayCount = personalDayCount,
|
||||||
|
restDayCount = restDayCount,
|
||||||
|
ordainDayCount = ordainDayCount,
|
||||||
|
absentDayCount = absentDayCount,
|
||||||
|
studyDayCount = studyDayCount,
|
||||||
|
agencyDayCount = agencyDayCount,
|
||||||
|
coupleDayCount = coupleDayCount,
|
||||||
|
therapyDayCount = therapyDayCount,
|
||||||
|
absentTotal = absentCount1,
|
||||||
|
lateTotal = lateCount1,
|
||||||
|
|
||||||
|
sickCount = sickCount,
|
||||||
|
maternityCount = maternityCount,
|
||||||
|
wifeCount = wifeCount,
|
||||||
|
personalCount = personalCount,
|
||||||
|
restCount = restCount,
|
||||||
|
ordainCount = ordainCount,
|
||||||
|
absentCount = absentCount,
|
||||||
|
studyCount = studyCount,
|
||||||
|
agencyCount = agencyCount,
|
||||||
|
coupleCount = coupleCount,
|
||||||
|
therapyCount = therapyCount,
|
||||||
|
|
||||||
|
leaveTotal = sickCount +
|
||||||
|
maternityCount +
|
||||||
|
wifeCount +
|
||||||
|
personalCount +
|
||||||
|
restCount +
|
||||||
|
ordainCount +
|
||||||
|
absentCount +
|
||||||
|
studyCount +
|
||||||
|
agencyCount +
|
||||||
|
coupleCount +
|
||||||
|
therapyCount
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
employees.Add(emp);
|
||||||
|
count++;
|
||||||
}
|
}
|
||||||
//var userRound = await _dutyTimeRepository.GetByIdAsync(profile.DutyTimeId ?? Guid.Empty);
|
else
|
||||||
var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(p.Id);
|
|
||||||
var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty;
|
|
||||||
var userRound = await _dutyTimeRepository.GetByIdAsync(roundId);
|
|
||||||
|
|
||||||
var duty = userRound ?? defaultRound;
|
|
||||||
|
|
||||||
/* var processTimeStamps = timeStamps
|
|
||||||
.Select(d => new
|
|
||||||
{
|
|
||||||
d.Id,
|
|
||||||
CheckInStatus = DateTime.Parse(d.CheckIn.ToString("yyyy-MM-dd HH:mm")) >
|
|
||||||
DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}") ?
|
|
||||||
"LATE" :
|
|
||||||
"NORMAL",
|
|
||||||
CheckOutStatus = d.CheckOut == null ? "" :
|
|
||||||
DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) <
|
|
||||||
DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ?
|
|
||||||
"LATE" :
|
|
||||||
DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) <
|
|
||||||
DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ?
|
|
||||||
"ABSENT" :
|
|
||||||
"NORMAL",
|
|
||||||
});*/
|
|
||||||
|
|
||||||
/*var absentCount = processTimeStamps.Count(x => x.CheckOutStatus == "ABSENT");
|
|
||||||
var lateCount = processTimeStamps.Count(x => x.CheckInStatus == "LATE");*/
|
|
||||||
|
|
||||||
var absentCount = timeStamps.Count(d =>
|
|
||||||
d.CheckOutStatus == "ABSENT"); // นับจำนวนที่มี CheckOutStatus == "ABSENT"
|
|
||||||
var lateCount = timeStamps.Count(d =>
|
|
||||||
d.CheckInStatus == "LATE"); // นับจำนวนที่มี CheckInStatus == "LATE"
|
|
||||||
|
|
||||||
|
|
||||||
var emp = new
|
|
||||||
{
|
{
|
||||||
no = count,
|
var sickDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-001");
|
||||||
fullName = $"{p.Prefix}{p.FirstName} {p.LastName}",
|
var sickDayCount = sickDay != null ? sickDay.SumLeaveDay : 0;
|
||||||
position = p.Position == null ? "" : p.Position,
|
var sickCount = sickDay != null ? sickDay.CountLeaveDay : 0;
|
||||||
positionLevel = p.PositionLevel == null ? "" : p.PositionLevel,
|
|
||||||
posNo = p.PosNo == null ? "" : p.PosNo,
|
|
||||||
reason = "",
|
|
||||||
sickDayCount = sickDayCount,
|
|
||||||
maternityDayCount = maternityDayCount,
|
|
||||||
wifeDayCount = wifeDayCount,
|
|
||||||
personalDayCount = personalDayCount,
|
|
||||||
restDayCount = restDayCount,
|
|
||||||
ordainDayCount = ordainDayCount,
|
|
||||||
absentDayCount = absentDayCount,
|
|
||||||
studyDayCount = studyDayCount,
|
|
||||||
agencyDayCount = agencyDayCount,
|
|
||||||
coupleDayCount = coupleDayCount,
|
|
||||||
therapyDayCount = therapyDayCount,
|
|
||||||
absentTotal = absentCount,
|
|
||||||
lateTotal = lateCount,
|
|
||||||
|
|
||||||
leaveTotal = sickDayCount +
|
|
||||||
maternityDayCount +
|
|
||||||
wifeDayCount +
|
|
||||||
personalDayCount +
|
|
||||||
restDayCount +
|
|
||||||
ordainDayCount +
|
|
||||||
absentDayCount +
|
|
||||||
studyDayCount +
|
|
||||||
agencyDayCount +
|
|
||||||
coupleDayCount +
|
|
||||||
therapyDayCount
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
employees.Add(emp);
|
var personalDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-002");
|
||||||
count++;
|
var personalDayCount = personalDay != null ? personalDay.SumLeaveDay : 0;
|
||||||
|
var personalCount = personalDay != null ? personalDay.CountLeaveDay : 0;
|
||||||
|
|
||||||
|
var maternityDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-003");
|
||||||
|
var maternityDayCount = maternityDay != null ? maternityDay.SumLeaveDay : 0;
|
||||||
|
var maternityCount = maternityDay != null ? maternityDay.CountLeaveDay : 0;
|
||||||
|
|
||||||
|
var wifeDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-004");
|
||||||
|
var wifeDayCount = wifeDay != null ? wifeDay.SumLeaveDay : 0;
|
||||||
|
var wifeCount = wifeDay != null ? wifeDay.CountLeaveDay : 0;
|
||||||
|
|
||||||
|
var restDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-005");
|
||||||
|
var restDayCount = restDay != null ? restDay.SumLeaveDay : 0;
|
||||||
|
var restCount = restDay != null ? restDay.CountLeaveDay : 0;
|
||||||
|
|
||||||
|
var ordainDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-006");
|
||||||
|
var ordainDayCount = ordainDay != null ? ordainDay.SumLeaveDay : 0;
|
||||||
|
var ordainCount = ordainDay != null ? ordainDay.CountLeaveDay : 0;
|
||||||
|
|
||||||
|
var absentDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-007");
|
||||||
|
var absentDayCount = absentDay != null ? absentDay.SumLeaveDay : 0;
|
||||||
|
var absentCount = absentDay != null ? absentDay.CountLeaveDay : 0;
|
||||||
|
|
||||||
|
var studyDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-008");
|
||||||
|
var studyDayCount = studyDay != null ? studyDay.SumLeaveDay : 0;
|
||||||
|
var studyCount = studyDay != null ? studyDay.CountLeaveDay : 0;
|
||||||
|
|
||||||
|
var agencyDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-009");
|
||||||
|
var agencyDayCount = agencyDay != null ? agencyDay.SumLeaveDay : 0;
|
||||||
|
var agencyCount = agencyDay != null ? agencyDay.CountLeaveDay : 0;
|
||||||
|
|
||||||
|
var coupleDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-010");
|
||||||
|
var coupleDayCount = coupleDay != null ? coupleDay.SumLeaveDay : 0;
|
||||||
|
var coupleCount = coupleDay != null ? coupleDay.CountLeaveDay : 0;
|
||||||
|
|
||||||
|
var therapyDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-011");
|
||||||
|
var therapyDayCount = therapyDay != null ? therapyDay.SumLeaveDay : 0;
|
||||||
|
var therapyCount = therapyDay != null ? therapyDay.CountLeaveDay : 0;
|
||||||
|
|
||||||
|
var timeStamps = await _processUserTimeStampRepository.GetTimeStampHistoryByRangeForUserAsync(p.Keycloak ?? Guid.Empty, req.StartDate, req.EndDate);
|
||||||
|
|
||||||
|
var defaultRound = await _dutyTimeRepository.GetDefaultAsync();
|
||||||
|
if (defaultRound == null)
|
||||||
|
{
|
||||||
|
return Error("ไม่พบรอบการลงเวลา Default", StatusCodes.Status404NotFound);
|
||||||
|
}
|
||||||
|
//var userRound = await _dutyTimeRepository.GetByIdAsync(profile.DutyTimeId ?? Guid.Empty);
|
||||||
|
var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(p.Id);
|
||||||
|
var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty;
|
||||||
|
var userRound = await _dutyTimeRepository.GetByIdAsync(roundId);
|
||||||
|
|
||||||
|
var duty = userRound ?? defaultRound;
|
||||||
|
|
||||||
|
/* var processTimeStamps = timeStamps
|
||||||
|
.Select(d => new
|
||||||
|
{
|
||||||
|
d.Id,
|
||||||
|
CheckInStatus = DateTime.Parse(d.CheckIn.ToString("yyyy-MM-dd HH:mm")) >
|
||||||
|
DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}") ?
|
||||||
|
"LATE" :
|
||||||
|
"NORMAL",
|
||||||
|
CheckOutStatus = d.CheckOut == null ? "" :
|
||||||
|
DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) <
|
||||||
|
DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ?
|
||||||
|
"LATE" :
|
||||||
|
DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) <
|
||||||
|
DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ?
|
||||||
|
"ABSENT" :
|
||||||
|
"NORMAL",
|
||||||
|
});*/
|
||||||
|
|
||||||
|
/*var absentCount = processTimeStamps.Count(x => x.CheckOutStatus == "ABSENT");
|
||||||
|
var lateCount = processTimeStamps.Count(x => x.CheckInStatus == "LATE");*/
|
||||||
|
|
||||||
|
var absentCount2 = timeStamps.Count(d =>
|
||||||
|
d.CheckOutStatus == "ABSENT" || d.CheckInStatus == "ABSENT"); // นับจำนวนที่มี CheckOutStatus == "ABSENT"
|
||||||
|
var lateCount2 = timeStamps.Count(d =>
|
||||||
|
d.CheckInStatus == "LATE"); // นับจำนวนที่มี CheckInStatus == "LATE"
|
||||||
|
|
||||||
|
|
||||||
|
var emp = new
|
||||||
|
{
|
||||||
|
no = count,
|
||||||
|
fullName = $"{p.Prefix}{p.FirstName} {p.LastName}",
|
||||||
|
position = p.Position == null ? "" : p.Position,
|
||||||
|
positionLevel = p.PositionLevel == null ? "" : p.PositionLevel,
|
||||||
|
posNo = p.PosNo == null ? "" : p.PosNo,
|
||||||
|
reason = "",
|
||||||
|
sickDayCount = sickDayCount,
|
||||||
|
maternityDayCount = maternityDayCount,
|
||||||
|
wifeDayCount = wifeDayCount,
|
||||||
|
personalDayCount = personalDayCount,
|
||||||
|
restDayCount = restDayCount,
|
||||||
|
ordainDayCount = ordainDayCount,
|
||||||
|
absentDayCount = absentDayCount,
|
||||||
|
studyDayCount = studyDayCount,
|
||||||
|
agencyDayCount = agencyDayCount,
|
||||||
|
coupleDayCount = coupleDayCount,
|
||||||
|
therapyDayCount = therapyDayCount,
|
||||||
|
absentTotal = absentCount2,
|
||||||
|
lateTotal = lateCount2,
|
||||||
|
|
||||||
|
sickCount = sickCount,
|
||||||
|
maternityCount = maternityCount,
|
||||||
|
wifeCount = wifeCount,
|
||||||
|
personalCount = personalCount,
|
||||||
|
restCount = restCount,
|
||||||
|
ordainCount = ordainCount,
|
||||||
|
absentCount = absentCount,
|
||||||
|
studyCount = studyCount,
|
||||||
|
agencyCount = agencyCount,
|
||||||
|
coupleCount = coupleCount,
|
||||||
|
therapyCount = therapyCount,
|
||||||
|
|
||||||
|
leaveTotal = sickCount +
|
||||||
|
maternityCount +
|
||||||
|
wifeCount +
|
||||||
|
personalCount +
|
||||||
|
restCount +
|
||||||
|
ordainCount +
|
||||||
|
absentCount +
|
||||||
|
studyCount +
|
||||||
|
agencyCount +
|
||||||
|
coupleCount +
|
||||||
|
therapyCount
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
employees.Add(emp);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var leaveTitleType = "";
|
var leaveTitleType = "";
|
||||||
|
|
@ -1983,7 +2178,7 @@ namespace BMA.EHR.Leave.Service.Controllers
|
||||||
worksheet.Cells[2, 1].Value = organizationName;
|
worksheet.Cells[2, 1].Value = organizationName;
|
||||||
worksheet.Cells[3, 1].Value = dateTimeStamp;
|
worksheet.Cells[3, 1].Value = dateTimeStamp;
|
||||||
|
|
||||||
using (var range = worksheet.Cells[4, 1 ,4, 10])
|
using (var range = worksheet.Cells[4, 1, 4, 10])
|
||||||
{
|
{
|
||||||
range.Style.Border.Top.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin;
|
range.Style.Border.Top.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin;
|
||||||
range.Style.Border.Bottom.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin;
|
range.Style.Border.Bottom.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin;
|
||||||
|
|
@ -2020,7 +2215,7 @@ namespace BMA.EHR.Leave.Service.Controllers
|
||||||
}
|
}
|
||||||
|
|
||||||
// ใส่กรอบให้ตาราง
|
// ใส่กรอบให้ตาราง
|
||||||
using (var range = worksheet.Cells[5, 1, startRow-1, 10])
|
using (var range = worksheet.Cells[5, 1, startRow - 1, 10])
|
||||||
{
|
{
|
||||||
range.Style.Border.Top.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin;
|
range.Style.Border.Top.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin;
|
||||||
range.Style.Border.Bottom.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin;
|
range.Style.Border.Bottom.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin;
|
||||||
|
|
|
||||||
|
|
@ -2246,7 +2246,7 @@ namespace BMA.EHR.Leave.Service.Controllers
|
||||||
//approver = list.First().Name;
|
//approver = list.First().Name;
|
||||||
//}
|
//}
|
||||||
|
|
||||||
var leaveSummary = await _leaveRequestRepository.GetSumApproveLeaveByTypeForUserAsync(rawData.KeycloakUserId, rawData.Type.Id, thisYear);
|
|
||||||
|
|
||||||
//var sumLeave = rawData.LeaveStartDate.DiffDay(rawData.LeaveEndDate);
|
//var sumLeave = rawData.LeaveStartDate.DiffDay(rawData.LeaveEndDate);
|
||||||
//var sumHoliday = await _holidayRepository.GetHolidayCountAsync(rawData.LeaveStartDate, rawData.LeaveEndDate, category);
|
//var sumHoliday = await _holidayRepository.GetHolidayCountAsync(rawData.LeaveStartDate, rawData.LeaveEndDate, category);
|
||||||
|
|
@ -2267,7 +2267,8 @@ namespace BMA.EHR.Leave.Service.Controllers
|
||||||
if (rawData.Root != null && rawData.Root != "")
|
if (rawData.Root != null && rawData.Root != "")
|
||||||
orgName += $" {rawData.Root}";
|
orgName += $" {rawData.Root}";
|
||||||
|
|
||||||
var leaveData = await _leaveBeginningRepository.GetByYearAndTypeIdForUserAsync(thisYear, rawData.Type.Id, rawData.KeycloakUserId);
|
var leaveData = await _leaveBeginningRepository.GetByYearAndTypeIdForUser2Async(thisYear, rawData.Type.Id, rawData.KeycloakUserId);
|
||||||
|
var leaveSummary = leaveData == null ? 0.0 : leaveData.LeaveDaysUsed;
|
||||||
|
|
||||||
var extendLeave = 0.0;
|
var extendLeave = 0.0;
|
||||||
var leaveLimit = (double)rawData.Type.Limit;
|
var leaveLimit = (double)rawData.Type.Limit;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue