初稿

parent 28e2c18b
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Performance.Infrastructure;
using Performance.DtoModels.Second;
namespace Performance.Api.Controllers namespace Performance.Api.Controllers
{ {
...@@ -20,6 +22,7 @@ namespace Performance.Api.Controllers ...@@ -20,6 +22,7 @@ namespace Performance.Api.Controllers
public class SecondAllotController : ControllerBase public class SecondAllotController : ControllerBase
{ {
private readonly ClaimService claimService; private readonly ClaimService claimService;
private readonly AllotService _allotService;
private readonly SecondAllotService secondAllotService; private readonly SecondAllotService secondAllotService;
private readonly ResultComputeService resultComputeService; private readonly ResultComputeService resultComputeService;
private readonly SecondAllotDetails secondAllotDetails; private readonly SecondAllotDetails secondAllotDetails;
...@@ -27,6 +30,7 @@ public class SecondAllotController : ControllerBase ...@@ -27,6 +30,7 @@ public class SecondAllotController : ControllerBase
public SecondAllotController( public SecondAllotController(
ClaimService claimService, ClaimService claimService,
AllotService allotService,
SecondAllotService secondAllotService, SecondAllotService secondAllotService,
ResultComputeService resultComputeService, ResultComputeService resultComputeService,
SecondAllotDetails secondAllotDetails, SecondAllotDetails secondAllotDetails,
...@@ -34,6 +38,7 @@ RedistributionService redistributionService ...@@ -34,6 +38,7 @@ RedistributionService redistributionService
) )
{ {
this.claimService = claimService; this.claimService = claimService;
_allotService = allotService;
this.secondAllotService = secondAllotService; this.secondAllotService = secondAllotService;
this.resultComputeService = resultComputeService; this.resultComputeService = resultComputeService;
this.secondAllotDetails = secondAllotDetails; this.secondAllotDetails = secondAllotDetails;
...@@ -330,7 +335,7 @@ public ApiResponse SingleDelete([CustomizeValidator(RuleSet = "Delete"), FromBod ...@@ -330,7 +335,7 @@ public ApiResponse SingleDelete([CustomizeValidator(RuleSet = "Delete"), FromBod
[Route("/api/second/audit/submit")] [Route("/api/second/audit/submit")]
public ApiResponse SubmitAudit(SubmitAuditRequest request) public ApiResponse SubmitAudit(SubmitAuditRequest request)
{ {
var second = secondAllotService.GetSecondallot(request.SecondId); var second = secondAllotService.GetSecondAllot(request.SecondId);
if (second == null) if (second == null)
return new ApiResponse(ResponseType.ParameterError, "二次绩效Id无效"); return new ApiResponse(ResponseType.ParameterError, "二次绩效Id无效");
if (second.Status == 3) if (second.Status == 3)
...@@ -534,18 +539,144 @@ public ApiResponse AutoCompleteBodyData([FromRoute] int secondId, SecondEmployee ...@@ -534,18 +539,144 @@ public ApiResponse AutoCompleteBodyData([FromRoute] int secondId, SecondEmployee
return new ApiResponse(ResponseType.OK, result); return new ApiResponse(ResponseType.OK, result);
} }
#region 二次分配后台计算
/// <summary> /// <summary>
/// 二次绩效录入页面 /// 二次绩效录入页面
/// </summary> /// </summary>
/// <param name="request"></param> /// <param name="request"></param>
/// <returns></returns> /// <returns></returns>
[Route("api/second/redistribution")] [Route("api/second/redistribution/load")]
[HttpGet] [HttpPost]
public ApiResponse Redistribution([FromRoute] SecondLoadRequest request) public ApiResponse RedistributionLoad([FromBody] SecondLoadDto request)
{ {
var result = _redistributionService.Load(request.SecondId, request.ComputeMode); if (!Enum.IsDefined(typeof(ComputeMode), request.ComputeMode))
throw new PerformanceException("暂不支持当前计算方式!");
var overrideMode = OverrideMode.Initial;
if (Enum.IsDefined(typeof(OverrideMode), request.OverrideMode))
overrideMode = (OverrideMode)request.OverrideMode;
var result = _redistributionService.Load(request.SecondId, (ComputeMode)request.ComputeMode, overrideMode);
return new ApiResponse(ResponseType.OK, result); return new ApiResponse(ResponseType.OK, result);
} }
/// <summary>
/// 提交数据正确性检验
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
[Route("api/second/redistribution/check")]
[HttpPost]
public ApiResponse RedistributionCheck([FromBody] SecondComputeDto request)
{
if (request?.Body == null)
throw new PerformanceException("提交空参数,无法查看计算结果!");
var second = secondAllotService.GetSecondAllot(request.SecondId);
if (second == null) throw new PerformanceException("参数SecondId无效!");
var allot = _allotService.GetAllot(second.AllotId.Value);
if (allot == null)
throw new PerformanceException("绩效记录不存在!");
// 年资职称绩效占比与工作量绩效占比 校验
var loads = _redistributionService.GetWorkLoads(allot.HospitalId, second.UnitType, second.Department);
var workloadGroups = _redistributionService.GetTopWorkloadBodyGroups(loads);
var seniorityTitlesAccountedPerformance = request.Head.GetValue(nameof(ag_headsource.SeniorityTitlesAccountedPerformance), 0m);
var workloadRatio = workloadGroups.Sum(w => request.Head.GetValue($"Workload_Ratio_{w.Name}", 0m));
if (seniorityTitlesAccountedPerformance + workloadRatio > 1)
throw new PerformanceException("年资职称绩效占比与工作量绩效占比总和,超过100%!");
else if (seniorityTitlesAccountedPerformance + workloadRatio < 1)
throw new PerformanceException("年资职称绩效占比与工作量绩效占比总和,不足100%!");
// 二次分配人员信息 校验
var result = _redistributionService.CheckData(second, request.Body);
return new ApiResponse(ResponseType.OK, result);
}
/// <summary>
/// 二次绩效录入页面
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
[Route("api/second/redistribution/compute")]
[HttpPost]
public ApiResponse RedistributionCompute([FromBody] SecondComputeDto request)
{
if (!Enum.IsDefined(typeof(ComputeMode), request.ComputeMode))
throw new PerformanceException("暂不支持当前计算方式!");
if (request?.Body == null)
throw new PerformanceException("提交空参数,无法查看计算结果!");
var second = secondAllotService.GetSecondAllot(request.SecondId);
if (second == null) throw new PerformanceException("参数SecondId无效!");
var allot = _allotService.GetAllot(second.AllotId.Value);
if (allot == null)
throw new PerformanceException("绩效记录不存在!");
// 年资职称绩效占比与工作量绩效占比 校验
var loads = _redistributionService.GetWorkLoads(allot.HospitalId, second.UnitType, second.Department);
var workloadGroups = _redistributionService.GetTopWorkloadBodyGroups(loads);
var seniorityTitlesAccountedPerformance = request.Head.GetValue(nameof(ag_headsource.SeniorityTitlesAccountedPerformance), 0m);
var workloadRatio = workloadGroups.Sum(w => request.Head.GetValue($"Workload_Ratio_{w.Name}", 0m));
if (seniorityTitlesAccountedPerformance + workloadRatio > 1)
throw new PerformanceException("年资职称绩效占比与工作量绩效占比总和,超过100%!");
else if (seniorityTitlesAccountedPerformance + workloadRatio < 1)
throw new PerformanceException("年资职称绩效占比与工作量绩效占比总和,不足100%!");
// 二次分配人员信息 校验
var checkDatas = _redistributionService.CheckData(second, request.Body);
if (checkDatas != null && checkDatas.Any(w => w.Level == ResponseType.Error.ToString()))
return new ApiResponse(ResponseType.Fail, "数据验证未通过,请修复后查看计算结果!", checkDatas.Where(w => w.Level == ResponseType.Error.ToString()));
// 清理无效数据 没有Tab标签的数据才是要计算的数据
var cleanDatas = request.Body.Where(w => w.Count > 0 && w.GetValue(nameof(ResponseType), "") == ResponseType.OK.ToString()).ToList();
if (cleanDatas == null || cleanDatas.Count == 0)
throw new PerformanceException("提交参数都是无效数据,请重新填写数据后查看计算结果!");
// 计算提交数据结果
_redistributionService.ResultCompute((ComputeMode)request.ComputeMode, request.Head, cleanDatas, loads, workloadGroups);
// 补充医院其他绩效
_redistributionService.SupplementOtherPerfor(second, cleanDatas);
// 重算顶部医院其他绩效
_redistributionService.OverviewCalculate_OtherPerformance(request.Head, cleanDatas);
var dic = _redistributionService.GetTableHeaderDictionary(loads);
return new ApiResponse(ResponseType.OK, new { Head = request.Head, Body = cleanDatas, Dic = dic });
}
/// <summary>
/// 二次绩效保存
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
[Route("api/second/redistribution/save")]
[HttpPost]
public ApiResponse RedistributionSave([FromBody] SecondComputeDto request)
{
secondAllotService.SaveSecondAllotData(request.SecondId, request);
return new ApiResponse(ResponseType.OK);
}
/// <summary>
/// 二次绩效提交
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
[Route("api/second/redistribution/submit")]
[HttpPost]
public ApiResponse RedistributionSubmit([FromBody] SecondComputeDto request)
{
var second = secondAllotService.GetSecondAllot(request.SecondId);
if (second == null)
return new ApiResponse(ResponseType.ParameterError, "二次绩效Id无效");
if (second.Status == 3)
return new ApiResponse(ResponseType.Fail, "该绩效已\"审核通过\",无需再次提交");
var userid = claimService.GetUserId();
var result = secondAllotService.AuditSubmit(second, userid);
return result ? new ApiResponse(ResponseType.OK, "提交成功") : new ApiResponse(ResponseType.Fail, "提交失败");
}
#endregion
} }
} }
...@@ -1612,6 +1612,41 @@ ...@@ -1612,6 +1612,41 @@
</summary> </summary>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:Performance.Api.Controllers.SecondAllotController.RedistributionLoad(Performance.DtoModels.SecondLoadDto)">
<summary>
二次绩效录入页面
</summary>
<param name="request"></param>
<returns></returns>
</member>
<member name="M:Performance.Api.Controllers.SecondAllotController.RedistributionCheck(Performance.DtoModels.SecondComputeDto)">
<summary>
提交数据正确性检验
</summary>
<param name="request"></param>
<returns></returns>
</member>
<member name="M:Performance.Api.Controllers.SecondAllotController.RedistributionCompute(Performance.DtoModels.SecondComputeDto)">
<summary>
二次绩效录入页面
</summary>
<param name="request"></param>
<returns></returns>
</member>
<member name="M:Performance.Api.Controllers.SecondAllotController.RedistributionSave(Performance.DtoModels.SecondComputeDto)">
<summary>
二次绩效保存
</summary>
<param name="request"></param>
<returns></returns>
</member>
<member name="M:Performance.Api.Controllers.SecondAllotController.RedistributionSubmit(Performance.DtoModels.SecondComputeDto)">
<summary>
二次绩效提交
</summary>
<param name="request"></param>
<returns></returns>
</member>
<member name="M:Performance.Api.Controllers.SheetController.SheetList(Performance.DtoModels.SheetRequest)"> <member name="M:Performance.Api.Controllers.SheetController.SheetList(Performance.DtoModels.SheetRequest)">
<summary> <summary>
sheet 列表 sheet 列表
......
...@@ -179,6 +179,12 @@ ...@@ -179,6 +179,12 @@
<member name="F:Performance.DtoModels.DataFormat.小数"> <member name="F:Performance.DtoModels.DataFormat.小数">
<summary> 小数 </summary> <summary> 小数 </summary>
</member> </member>
<member name="F:Performance.DtoModels.DataFormat.小数1">
<summary> 小数 </summary>
</member>
<member name="F:Performance.DtoModels.DataFormat.整数">
<summary> 整数 </summary>
</member>
<member name="F:Performance.DtoModels.DataFormat.货币"> <member name="F:Performance.DtoModels.DataFormat.货币">
<summary> 货币 </summary> <summary> 货币 </summary>
</member> </member>
...@@ -3755,6 +3761,56 @@ ...@@ -3755,6 +3761,56 @@
绩效系数 绩效系数
</summary> </summary>
</member> </member>
<member name="T:Performance.DtoModels.Second.ComputeMode">
<summary>
二次分配计算方式
</summary>
</member>
<member name="F:Performance.DtoModels.Second.ComputeMode.NotCalculate">
<summary>
不计算
</summary>
</member>
<member name="F:Performance.DtoModels.Second.ComputeMode.Horizontal">
<summary>
横向计算
</summary>
</member>
<member name="F:Performance.DtoModels.Second.ComputeMode.Vertical">
<summary>
纵向计算
</summary>
</member>
<member name="T:Performance.DtoModels.Second.OverrideMode">
<summary>
人员带出方式 已保存,上次,字典,测算表
</summary>
</member>
<member name="F:Performance.DtoModels.Second.OverrideMode.Initial">
<summary>
初始化(用户保存后的数据)
</summary>
</member>
<member name="F:Performance.DtoModels.Second.OverrideMode.PrevSecondAllot">
<summary>
上一个二次绩效记录
</summary>
</member>
<member name="F:Performance.DtoModels.Second.OverrideMode.EmployeeDict">
<summary>
人员字典
</summary>
</member>
<member name="P:Performance.DtoModels.SecondLoadDto.ComputeMode">
<summary>
计算方式:1 不计算 2 横向计算 3 纵向计算
</summary>
</member>
<member name="P:Performance.DtoModels.SecondLoadDto.OverrideMode">
<summary>
数据加载方式:0 保存,1 上次,2 字典,3 测算表
</summary>
</member>
<member name="P:Performance.DtoModels.SelectionOptions.SelectionID"> <member name="P:Performance.DtoModels.SelectionOptions.SelectionID">
<summary> <summary>
ID ID
......
...@@ -474,19 +474,9 @@ ...@@ -474,19 +474,9 @@
职称绩效 职称绩效
</summary> </summary>
</member> </member>
<member name="P:Performance.EntityModels.ag_bodysource.ManagementAllowance"> <member name="P:Performance.EntityModels.ag_bodysource.WorkPerformance">
<summary> <summary>
管理津贴 工作量绩效
</summary>
</member>
<member name="P:Performance.EntityModels.ag_bodysource.IndividualReward">
<summary>
单项奖励
</summary>
</member>
<member name="P:Performance.EntityModels.ag_bodysource.AllocationOfKeySpecialty">
<summary>
重点专科分配
</summary> </summary>
</member> </member>
<member name="P:Performance.EntityModels.ag_bodysource.DeptReward"> <member name="P:Performance.EntityModels.ag_bodysource.DeptReward">
...@@ -936,12 +926,12 @@ ...@@ -936,12 +926,12 @@
</member> </member>
<member name="P:Performance.EntityModels.ag_headsource.TheTotalAllocationOfPerformanceResults"> <member name="P:Performance.EntityModels.ag_headsource.TheTotalAllocationOfPerformanceResults">
<summary> <summary>
科室单项奖励 业绩分配绩效总额
</summary> </summary>
</member> </member>
<member name="P:Performance.EntityModels.ag_headsource.BasisPerformance"> <member name="P:Performance.EntityModels.ag_headsource.TotalDeptReward">
<summary> <summary>
业绩分配绩效总额 科室单项奖励
</summary> </summary>
</member> </member>
<member name="P:Performance.EntityModels.ag_headsource.SeniorityTitlesAccountedPerformance"> <member name="P:Performance.EntityModels.ag_headsource.SeniorityTitlesAccountedPerformance">
......
...@@ -99,6 +99,10 @@ public enum DataFormat ...@@ -99,6 +99,10 @@ public enum DataFormat
普通格式, 普通格式,
/// <summary> 小数 </summary> /// <summary> 小数 </summary>
小数, 小数,
/// <summary> 小数 </summary>
小数1,
/// <summary> 整数 </summary>
整数,
/// <summary> 货币 </summary> /// <summary> 货币 </summary>
货币, 货币,
/// <summary> 百分比 </summary> /// <summary> 百分比 </summary>
......
...@@ -12,12 +12,12 @@ public HandsonTableBase() ...@@ -12,12 +12,12 @@ public HandsonTableBase()
{ {
ColHeaders = new List<string>(); ColHeaders = new List<string>();
Columns = new List<HandsonColumn>(); Columns = new List<HandsonColumn>();
Data = new List<Dictionary<string, string>>(); Data = new List<Dictionary<string, object>>();
NestedHeadersArray = new List<List<string>>(); NestedHeadersArray = new List<List<string>>();
} }
public List<string> ColHeaders { get; set; } public List<string> ColHeaders { get; set; }
public List<Dictionary<string, string>> Data { get; set; } public List<Dictionary<string, object>> Data { get; set; }
public List<HandsonColumn> Columns { get; set; } public List<HandsonColumn> Columns { get; set; }
public List<List<string>> NestedHeadersArray { get; set; } public List<List<string>> NestedHeadersArray { get; set; }
} }
...@@ -95,9 +95,9 @@ private void InitColumns(List<collect_permission> permissions) ...@@ -95,9 +95,9 @@ private void InitColumns(List<collect_permission> permissions)
Columns = columns; Columns = columns;
} }
private Dictionary<string, string> CreateDataRow(string key, string value) private Dictionary<string, object> CreateDataRow(string key, string value)
{ {
var temp = new Dictionary<string, string>() { { key, value } }; var temp = new Dictionary<string, object>() { { key, value } };
foreach (var item in ColHeaders) foreach (var item in ColHeaders)
{ {
if (!temp.ContainsKey(item)) if (!temp.ContainsKey(item))
...@@ -125,6 +125,16 @@ public HandsonColumn(string data, bool readOnly = false, DataFormat format = Dat ...@@ -125,6 +125,16 @@ public HandsonColumn(string data, bool readOnly = false, DataFormat format = Dat
NumericFormat = new NumericFormat { Pattern = "0,00.00" }; NumericFormat = new NumericFormat { Pattern = "0,00.00" };
break; break;
case DataFormat.小数1:
Type = "numeric";
NumericFormat = new NumericFormat { Pattern = "0,00.0" };
break;
case DataFormat.整数:
Type = "numeric";
NumericFormat = new NumericFormat { Pattern = "0,00" };
break;
case DataFormat.百分比: case DataFormat.百分比:
Type = "numeric"; Type = "numeric";
NumericFormat = new NumericFormat { Pattern = "0,00.00%" }; NumericFormat = new NumericFormat { Pattern = "0,00.00%" };
......
...@@ -5,14 +5,6 @@ ...@@ -5,14 +5,6 @@
namespace Performance.DtoModels namespace Performance.DtoModels
{ {
public class SecondLoadRequest
{
public int SecondId { get; set; }
/// <summary>
/// 计算方式:1 不计算 2 横向计算 3 纵向计算
/// </summary>
public int ComputeMode { get; set; }
}
public class SecondEmployeeRequest public class SecondEmployeeRequest
{ {
public string EmployeeName { get; set; } public string EmployeeName { get; set; }
......
...@@ -14,5 +14,6 @@ public enum ResponseType ...@@ -14,5 +14,6 @@ public enum ResponseType
ParameterError = 6, ParameterError = 6,
Disable = 7, Disable = 7,
TooManyRequests = 8, TooManyRequests = 8,
Warning = 9,
} }
} }
using System;
using System.Collections.Generic;
using System.Text;
namespace Performance.DtoModels.Second
{
/// <summary>
/// 二次分配计算方式
/// </summary>
public enum ComputeMode
{
/// <summary>
/// 不计算
/// </summary>
NotCalculate = 1,
/// <summary>
/// 横向计算
/// </summary>
Horizontal = 2,
/// <summary>
/// 纵向计算
/// </summary>
Vertical = 3,
}
/// <summary>
/// 人员带出方式 已保存,上次,字典,测算表
/// </summary>
public enum OverrideMode
{
/// <summary>
/// 初始化(用户保存后的数据)
/// </summary>
Initial = 0,
/// <summary>
/// 上一个二次绩效记录
/// </summary>
PrevSecondAllot = 1,
/// <summary>
/// 人员字典
/// </summary>
EmployeeDict = 2,
}
}
namespace Performance.DtoModels
{
public class SecondColumnDictionary
{
public string Label { get; set; }
public string Key { get; set; }
public bool IsTrue { get; set; }
public int Sort { get; set; }
public SecondColumnDictionary()
{
}
public SecondColumnDictionary(string label, string key, bool isTrue, int sort)
{
Label = label;
Key = key;
IsTrue = isTrue;
Sort = sort;
}
}
}
namespace Performance.DtoModels
{
public class SecondComputeCheckResultDto
{
public SecondComputeCheckResultDto(string level, string personnelNumber, string personnelName, string message)
{
Level = level;
PersonnelNumber = personnelNumber;
PersonnelName = personnelName;
Message = message;
}
public string Level { get; set; }
public string PersonnelNumber { get; set; }
public string PersonnelName { get; set; }
public string Message { get; set; }
}
}
using System.Collections.Generic;
namespace Performance.DtoModels
{
public class SecondComputeDto : SecondLoadDto
{
public Dictionary<string, object> Head { get; set; }
public List<Dictionary<string, object>> Body { get; set; }
}
}
using System.Collections.Generic;
namespace Performance.DtoModels
{
public class SecondDetailDto
{
public object Head { get; set; }
public HandsonTableBase Body { get; set; }
public List<SecondColumnDictionary> Dic { get; set; }
}
}
namespace Performance.DtoModels
{
public class SecondLoadDto
{
public int SecondId { get; set; }
/// <summary>
/// 计算方式:1 不计算 2 横向计算 3 纵向计算
/// </summary>
public int ComputeMode { get; set; }
/// <summary>
/// 数据加载方式:0 保存,1 上次,2 字典,3 测算表
/// </summary>
public int OverrideMode { get; set; }
}
}
using System;
using System.Collections.Generic;
namespace Performance.DtoModels
{
public class SecondWorkLoadDto
{
public SecondWorkLoadDto(string name)
{
Name = name;
Unit_Price = 0m;
Items = new List<string>();
AssessmentScore = $"AssessmentScore_{Name}";
WorkPerformance = $"WorkPerformance_{Name}";
WorkloadScore = $"WorkloadScore_{Name}";
}
public string AssessmentScore { get; set; }
public string WorkPerformance { get; set; }
public string WorkloadScore { get; set; }
public decimal Unit_Price { get; set; }
public List<string> Items { get; set; }
public string Name { get; set; }
public void AddItem(string name)
{
if (!Items.Contains(name))
Items.Add(name);
}
}
}
...@@ -77,19 +77,24 @@ public class ag_bodysource ...@@ -77,19 +77,24 @@ public class ag_bodysource
public Nullable<decimal> TitlePerformance { get; set; } public Nullable<decimal> TitlePerformance { get; set; }
/// <summary> /// <summary>
/// 管理津贴 /// 工作量绩效
/// </summary> /// </summary>
public Nullable<decimal> ManagementAllowance { get; set; } public Nullable<decimal> WorkPerformance { get; set; }
/// <summary> ///// <summary>
/// 单项奖励 ///// 管理津贴
/// </summary> ///// </summary>
public Nullable<decimal> IndividualReward { get; set; } //public Nullable<decimal> ManagementAllowance { get; set; }
/// <summary> ///// <summary>
/// 重点专科分配 ///// 单项奖励
/// </summary> ///// </summary>
public Nullable<decimal> AllocationOfKeySpecialty { get; set; } //public Nullable<decimal> IndividualReward { get; set; }
///// <summary>
///// 重点专科分配
///// </summary>
//public Nullable<decimal> AllocationOfKeySpecialty { get; set; }
/// <summary> /// <summary>
/// 科室单项奖励 /// 科室单项奖励
......
...@@ -52,14 +52,19 @@ public class ag_headsource ...@@ -52,14 +52,19 @@ public class ag_headsource
public Nullable<decimal> DirectorBasisPerformance { get; set; } public Nullable<decimal> DirectorBasisPerformance { get; set; }
/// <summary> /// <summary>
/// 科室单项奖励 /// 业绩分配绩效总额
/// </summary> /// </summary>
public Nullable<decimal> TheTotalAllocationOfPerformanceResults { get; set; } public Nullable<decimal> TheTotalAllocationOfPerformanceResults { get; set; }
/// <summary> /// <summary>
/// 业绩分配绩效总额 /// 科室单项奖励
/// </summary> /// </summary>
public Nullable<decimal> BasisPerformance { get; set; } public Nullable<decimal> TotalDeptReward { get; set; }
///// <summary>
///// 业绩分配绩效总额
///// </summary>
//public Nullable<decimal> BasisPerformance { get; set; }
/// <summary> /// <summary>
/// 年资职称绩效占比 /// 年资职称绩效占比
......
...@@ -18,9 +18,9 @@ public static partial class UtilExtensions ...@@ -18,9 +18,9 @@ public static partial class UtilExtensions
/// <returns></returns> /// <returns></returns>
public static T GetValue<T>(this Dictionary<string, object> keyValues, string key, T defaultValue = default(T)) public static T GetValue<T>(this Dictionary<string, object> keyValues, string key, T defaultValue = default(T))
{ {
object value; var pair = keyValues.FirstOrDefault(w => w.Key.Equals(key, StringComparison.OrdinalIgnoreCase));
if (keyValues.TryGetValue(key, out value)) if (!default(KeyValuePair<string, object>).Equals(pair))
return ConvertHelper.To<T>(value, defaultValue); return ConvertHelper.To<T>(pair.Value, defaultValue);
return defaultValue; return defaultValue;
} }
...@@ -33,13 +33,29 @@ public static T GetValue<T>(this Dictionary<string, object> keyValues, string ke ...@@ -33,13 +33,29 @@ public static T GetValue<T>(this Dictionary<string, object> keyValues, string ke
/// <returns></returns> /// <returns></returns>
public static T GetValue<T>(this SortedDictionary<string, object> keyValues, string key, T defaultValue = default(T)) public static T GetValue<T>(this SortedDictionary<string, object> keyValues, string key, T defaultValue = default(T))
{ {
object value; var pair = keyValues.FirstOrDefault(w => w.Key.Equals(key, StringComparison.OrdinalIgnoreCase));
if (keyValues.TryGetValue(key, out value)) if (!default(KeyValuePair<string, object>).Equals(pair))
return ConvertHelper.To<T>(value, defaultValue); return ConvertHelper.To<T>(pair.Value, defaultValue);
return defaultValue; return defaultValue;
} }
/// <summary> /// <summary>
/// 添加或修改
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="keyValues"></param>
/// <param name="key"></param>
/// <returns></returns>
public static void AddOrUpdate(this Dictionary<string, object> keyValues, string key, object value)
{
var pair = keyValues.FirstOrDefault(w => w.Key.Equals(key, StringComparison.OrdinalIgnoreCase));
if (default(KeyValuePair<string, object>).Equals(pair))
keyValues.Add(key, value);
else
keyValues[key] = value;
}
/// <summary>
/// form 转换 键值对 /// form 转换 键值对
/// </summary> /// </summary>
/// <param name="pairs"></param> /// <param name="pairs"></param>
......
...@@ -26,5 +26,27 @@ public static string[] SplitRemoveEmpty(this string text, params string[] separa ...@@ -26,5 +26,27 @@ public static string[] SplitRemoveEmpty(this string text, params string[] separa
{ {
return text.Split(separator, StringSplitOptions.RemoveEmptyEntries); return text.Split(separator, StringSplitOptions.RemoveEmptyEntries);
} }
/// <summary>
/// 确定此字符串实例的开头是否与指定的匹配 忽略字母的大小写
/// </summary>
/// <param name="text"></param>
/// <param name="value"></param>
/// <returns></returns>
public static bool StartsWithIgnoreCase(this string text, string value)
{
return text.StartsWith(value, StringComparison.OrdinalIgnoreCase);
}
/// <summary>
/// 确定此字符串是否与指定的字符串对象具有相同的值 忽略字母的大小写
/// </summary>
/// <param name="text"></param>
/// <param name="value"></param>
/// <returns></returns>
public static bool EqualsIgnoreCase(this string text, string value)
{
return text.Equals(value, StringComparison.OrdinalIgnoreCase);
}
} }
} }
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
namespace Performance.Repository namespace Performance.Repository
{ {
/// <summary> /// <summary>
/// per_apr_amount Repository /// per_apr_amount_hide Repository
/// </summary> /// </summary>
public partial class PerforPerapramounthideRepository : PerforRepository<per_apr_amount_hide> public partial class PerforPerapramounthideRepository : PerforRepository<per_apr_amount_hide>
{ {
......
...@@ -208,7 +208,7 @@ public PageList<per_employee> GetPersons(int allotId, int userId, PersonParamsRe ...@@ -208,7 +208,7 @@ public PageList<per_employee> GetPersons(int allotId, int userId, PersonParamsRe
if (request != null && !string.IsNullOrEmpty(request.SearchQuery)) if (request != null && !string.IsNullOrEmpty(request.SearchQuery))
{ {
exp = exp.And(t => true && (t.AccountingUnit.Contains(request.SearchQuery) || t.DoctorName.Contains(request.SearchQuery) || t.Department.Contains(request.SearchQuery))); exp = exp.And(t => true && (t.AccountingUnit.Contains(request.SearchQuery) || t.PersonnelNumber.Contains(request.SearchQuery) || t.DoctorName.Contains(request.SearchQuery) || t.Department.Contains(request.SearchQuery)));
} }
var result = new List<per_employee>(); var result = new List<per_employee>();
......
...@@ -6,32 +6,75 @@ ...@@ -6,32 +6,75 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using Performance.Infrastructure;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using Performance.DtoModels.Second;
using Microsoft.Extensions.Logging;
namespace Performance.Services namespace Performance.Services
{ {
public class RedistributionService : IAutoInjection public class RedistributionService : IAutoInjection
{ {
#region 构造函数
private readonly ILogger<RedistributionService> _logger;
private readonly SecondAllotDetails _secondAllotDetails;
private readonly PerforPerallotRepository _perallotRepository; private readonly PerforPerallotRepository _perallotRepository;
private readonly PerforPeremployeeRepository _peremployeeRepository;
private readonly PerforAgsecondallotRepository _secondallotRepository; private readonly PerforAgsecondallotRepository _secondallotRepository;
private readonly PerforPerapramountRepository _perapramountRepository;
private readonly PerforAgothersourceRepository _agothersourceRepository;
private readonly PerforAgfixatitemRepository _agfixatitemRepository;
private readonly PerforAgheadsourceRepository _agheadsourceRepository;
private readonly PerforAgbodysourceRepository _agbodysourceRepository;
private readonly PerforAgworktypesourceRepository _agworktypesourceRepository;
private readonly PerforAgworkloadRepository _agworkloadRepository; private readonly PerforAgworkloadRepository _agworkloadRepository;
private readonly PerforAgworkloadsourceRepository _agworkloadsourceRepository;
private readonly PerforImemployeelogisticsRepository _imemployeelogisticsRepository;
public RedistributionService( public RedistributionService(
ILogger<RedistributionService> logger,
SecondAllotDetails secondAllotDetails,
PerforPerallotRepository perallotRepository, PerforPerallotRepository perallotRepository,
PerforPeremployeeRepository peremployeeRepository,
PerforAgsecondallotRepository secondallotRepository, PerforAgsecondallotRepository secondallotRepository,
PerforAgworkloadRepository agworkloadRepository) PerforPerapramountRepository perapramountRepository,
PerforAgothersourceRepository agothersourceRepository,
PerforAgfixatitemRepository agfixatitemRepository,
PerforAgheadsourceRepository agheadsourceRepository,
PerforAgbodysourceRepository agbodysourceRepository,
PerforAgworktypesourceRepository agworktypesourceRepository,
PerforAgworkloadRepository agworkloadRepository,
PerforAgworkloadsourceRepository agworkloadsourceRepository,
PerforImemployeelogisticsRepository imemployeelogisticsRepository)
{ {
_logger = logger;
_secondAllotDetails = secondAllotDetails;
_perallotRepository = perallotRepository; _perallotRepository = perallotRepository;
_peremployeeRepository = peremployeeRepository;
_secondallotRepository = secondallotRepository; _secondallotRepository = secondallotRepository;
_perapramountRepository = perapramountRepository;
_agothersourceRepository = agothersourceRepository;
_agfixatitemRepository = agfixatitemRepository;
_agheadsourceRepository = agheadsourceRepository;
_agbodysourceRepository = agbodysourceRepository;
_agworktypesourceRepository = agworktypesourceRepository;
_agworkloadRepository = agworkloadRepository; _agworkloadRepository = agworkloadRepository;
_agworkloadsourceRepository = agworkloadsourceRepository;
_imemployeelogisticsRepository = imemployeelogisticsRepository;
} }
#endregion
#region 加载
/// <summary> /// <summary>
/// /// 加载
/// </summary> /// </summary>
/// <param name="secondId"></param> /// <param name="secondId"></param>
/// <param name="computeMode">计算方式:1 不计算 2 横向计算 3 纵向计算</param> /// <param name="computeMode">计算方式:1 不计算 2 横向计算 3 纵向计算</param>
/// <param name="overrideMode">数据加载方式:0 保存,1 上次,2 字典,3 测算表</param>
/// <returns></returns> /// <returns></returns>
public HandsonTableBase Load(int secondId, int computeMode) public SecondDetailDto Load(int secondId, ComputeMode computeMode, OverrideMode overrideMode)
{ {
var second = _secondallotRepository.GetEntity(t => t.Id == secondId); var second = _secondallotRepository.GetEntity(t => t.Id == secondId);
if (second == null) throw new PerformanceException("参数SecondId无效!"); if (second == null) throw new PerformanceException("参数SecondId无效!");
...@@ -45,17 +88,39 @@ public HandsonTableBase Load(int secondId, int computeMode) ...@@ -45,17 +88,39 @@ public HandsonTableBase Load(int secondId, int computeMode)
HandsonTableBase handson = new HandsonTableBase(); HandsonTableBase handson = new HandsonTableBase();
switch (computeMode) switch (computeMode)
{ {
case 1: case ComputeMode.NotCalculate:
handson = ComputeMode_Format1(colHeaderCustoms, columnCustoms); handson = ComputeMode_Format1(colHeaderCustoms, columnCustoms);
break; break;
case 2: case ComputeMode.Horizontal:
handson = ComputeMode_Format2(colHeaderCustoms, columnCustoms); handson = ComputeMode_Format2(colHeaderCustoms, columnCustoms);
break; break;
case 3: case ComputeMode.Vertical:
handson = ComputeMode_Format3(colHeaderCustoms, columnCustoms, loads); handson = ComputeMode_Format3(colHeaderCustoms, columnCustoms, loads);
break; break;
} }
// 先占位,更加选择加载指定范围数据
// 加载方式分 保存,上次,字典,测算表
var loadEmployees = LoadEmployees(allot, second, overrideMode);
// 设置固定信息默认值
foreach (var item in loadEmployees)
{
item.StaffCoefficient = item.StaffCoefficient ?? 1; // 人员系数
item.ActualAttendance = item.ActualAttendance ?? DateTime.DaysInMonth(allot.Year, allot.Month); // 出勤
item.TitleCoefficient = item.TitleCoefficient ?? 1; // 职称系数
}
// 加载已保存工作量数据
handson.Data = LoadWorkload(allot, second, loadEmployees);
// 设置工作量考核等分默认值
foreach (var item in handson.Data)
{
foreach (var score in item.Where(w => w.Key.StartsWithIgnoreCase("AssessmentScore_")).ToList())
{
if (score.Value == null)
item.AddOrUpdate(score.Key, 100);
}
}
#region 结构案例 #region 结构案例
//var colHeaders = new List<string> { "工号", "姓名", "核算单元", "主管", "人员系数", "出勤", "职称", "职称系数", "A班", "P班", "N班", "考核得分", "A班", "P班", "N班", "考核得分", "单项奖励A", "单项奖励B", "夜班绩效", }; //var colHeaders = new List<string> { "工号", "姓名", "核算单元", "主管", "人员系数", "出勤", "职称", "职称系数", "A班", "P班", "N班", "考核得分", "A班", "P班", "N班", "考核得分", "单项奖励A", "单项奖励B", "夜班绩效", };
...@@ -116,42 +181,475 @@ public HandsonTableBase Load(int secondId, int computeMode) ...@@ -116,42 +181,475 @@ public HandsonTableBase Load(int secondId, int computeMode)
#endregion #endregion
return handson; var head = LoadHead(computeMode, allot, second);
var dic = GetTableHeaderDictionary(loads);
return new SecondDetailDto { Head = head, Body = handson, Dic = dic };
}
public List<SecondColumnDictionary> GetTableHeaderDictionary(List<TitleValue<string, decimal?>> loads)
{
var maps = new List<SecondColumnDictionary>()
{
new SecondColumnDictionary("人员工号","worknumber",true,100 ),
new SecondColumnDictionary("姓名","name",true,100 ),
new SecondColumnDictionary("核算单元","department",true,100 ),
new SecondColumnDictionary("主管","post",true,100 ),
new SecondColumnDictionary("人员系数","staffcoefficient", true, 200 ),
new SecondColumnDictionary("出勤","actualattendance",true,201 ),
new SecondColumnDictionary("职称","jobtitle",true,202 ),
new SecondColumnDictionary( "职称系数","titlecoefficient", true, 203 ),
new SecondColumnDictionary("职称绩效","titleperformance", true, 299 ),
new SecondColumnDictionary("工作量绩效工资","workperformance", true, 399 ),
new SecondColumnDictionary( "单项奖励","deptreward", true, 499),
new SecondColumnDictionary("可分配绩效","distperformance", true, 500 ),
new SecondColumnDictionary("医院其他绩效","otherperformance", true, 501 ),
new SecondColumnDictionary( "夜班工作量绩效","nightworkperformance", true, 502 ),
new SecondColumnDictionary("预留比例","reservedratio", true, 601 ),
new SecondColumnDictionary("预留金额","reservedamount", true, 602 ),
new SecondColumnDictionary("实发绩效工资金额","realamount", true, 700 ),
};
// 工作量
int sort = 300;
foreach (var item in loads.Where(w => !w.Title.StartsWithIgnoreCase("SingleAwards_")))
{
maps.Add(new SecondColumnDictionary(item.Value, item.Title, false, ++sort));
}
// 单项奖励
sort = 400;
foreach (var item in loads.Where(w => w.Title.StartsWithIgnoreCase("SingleAwards_")))
{
maps.Add(new SecondColumnDictionary(item.Value, item.Title, false, ++sort));
}
return maps.OrderBy(w => w.Sort).ToList();
}
/// <summary>
/// 加载已保存工作量数据,加载时区分:已提交和未提交
/// </summary>
/// <param name="allot"></param>
/// <param name="second"></param>
/// <param name="loadEmployees"></param>
/// <returns></returns>
private List<Dictionary<string, object>> LoadWorkload(per_allot allot, ag_secondallot second, List<ag_bodysource> loadEmployees)
{
List<Dictionary<string, object>> result = new List<Dictionary<string, object>>();
var status = (new int[] { (int)SecondAllotStatus.WaitReview, (int)SecondAllotStatus.PassAudit });
// 已提交
if (second.Status.HasValue && status.Contains(second.Status.Value))
{
var bodyDynamic = _agworkloadsourceRepository.GetEntities(t => loadEmployees.Select(w => w.Id).Contains(t.BodyId));
foreach (var employee in loadEmployees)
{
var dict = JsonHelper.Deserialize<Dictionary<string, object>>(JsonHelper.Serialize(employee));
if (bodyDynamic != null && bodyDynamic.Any(t => t.BodyId == employee.Id))
{
foreach (var col in bodyDynamic.Where(t => t.BodyId == employee.Id))
{
dict.Add(col.ItemId, col.Value);
}
}
result.Add(dict);
}
}
// 未提交
else
{
var bodyDynamic = _agworkloadsourceRepository.GetEntities(t => loadEmployees.Select(w => w.Id).Contains(t.BodyId));
var workloads = _agworkloadRepository.GetEntities(t => t.HospitalId == allot.HospitalId && t.Department == second.Department && t.UnitType == second.UnitType);
if (workloads != null && workloads.Any())
{
foreach (var employee in loadEmployees)
{
var dict = JsonHelper.Deserialize<Dictionary<string, object>>(JsonHelper.Serialize(employee));
foreach (var workitem in workloads)
{
var value = bodyDynamic?.FirstOrDefault(w => w.BodyId == employee.Id && w.WorkloadId == workitem.Id)?.Value;
dict.Add(workitem.ItemId, value);
}
result.Add(dict);
}
}
}
return result;
}
/// <summary>
/// 顶部信息加载
/// </summary>
/// <param name="computeMode"></param>
/// <param name="second"></param>
/// <param name="allot"></param>
/// <returns></returns>
private Dictionary<string, object> LoadHead(ComputeMode computeMode, per_allot allot, ag_secondallot second)
{
var head = new Dictionary<string, object>();
// 公共顶部信息
head.AddOrUpdate(nameof(ag_headsource.SecondId), second.Id);
head.AddOrUpdate(nameof(ag_headsource.PaymentOfTheMonth), $"{allot.Year}{allot.Month.ToString().PadLeft(2, '0')}月");
head.AddOrUpdate(nameof(ag_headsource.TotalDistPerformance), second.RealGiveFee ?? 0);
head.AddOrUpdate(nameof(ag_headsource.NightShiftWorkPerforTotal), second.NightShiftWorkPerforFee ?? 0);
head.AddOrUpdate(nameof(ag_headsource.TotalPerformance), (second.RealGiveFee ?? 0) - (second.NightShiftWorkPerforFee ?? 0));
// 横向 纵向 特有顶部信息
if (computeMode != ComputeMode.NotCalculate)
{
head.AddOrUpdate(nameof(ag_headsource.DaysFullAttendance), DateTime.DaysInMonth(allot.Year, allot.Month));
head.AddOrUpdate(nameof(ag_headsource.SeniorityTitlesAccountedPerformance), 0.2m);
head.AddOrUpdate(nameof(ag_headsource.Workload_Ratio_Default), 0.8m);
var history = _agheadsourceRepository.GetEntity(t => t.SecondId == second.Id);
if (history != null)
{
head.AddOrUpdate(nameof(ag_headsource.SeniorityTitlesAccountedPerformance), history.SeniorityTitlesAccountedPerformance);
head.AddOrUpdate(nameof(ag_headsource.Workload_Ratio_Default), history.Workload_Ratio_Default);
head.AddOrUpdate(nameof(ag_headsource.DaysFullAttendance), history.DaysFullAttendance);
}
// 多工作量加载
var headDynamic = _agworktypesourceRepository.GetEntities(t => t.SecondId == second.Id);
if (headDynamic != null && headDynamic.Any())
{
foreach (var item in headDynamic.Where(w => w.FieldId.StartsWithIgnoreCase("Workload_Ratio_")).OrderBy(t => t.Id))
{
head.AddOrUpdate(item.FieldId, item.Value ?? 0);
}
}
}
return head;
} }
/// <summary> /// <summary>
/// 按指定方式加载人员数据
/// </summary>
/// <param name="secondId"></param>
/// <param name="mode"></param>
/// <returns></returns>
private List<ag_bodysource> LoadEmployees(per_allot allot, ag_secondallot second, OverrideMode mode)
{
var employees = _peremployeeRepository.GetEntities(w => w.AllotId == second.AllotId);
// 默认流程
if (mode == OverrideMode.Initial)
{
var saveDatas = _agbodysourceRepository.GetEntities(w => w.SecondId == second.Id);
// 数据带出顺序 1 已保存 2 上次 3 科室字典(或EXCEL行政工勤)
if (saveDatas != null)
return saveDatas;
List<string> numbers = LoadEmployees_PrevSecondAllot(allot, second);
// 如果行政工勤科室没有保存数据,则默认从EXCEL中带出数据
if (UnitTypeUtil.IsOffice(second.UnitType) && (numbers == null || numbers.Count == 0))
numbers = LoadEmployees_OfficeExcel(second);
if (numbers == null || numbers.Count == 0)
numbers = LoadEmployees_EmployeeDict(second, employees);
return LoadEmployeeByDictionary(second, employees, numbers);
}
// 用户指定加载
else
{
List<string> numbers = new List<string>();
if (mode == OverrideMode.PrevSecondAllot)
numbers = LoadEmployees_PrevSecondAllot(allot, second);
// 如果行政工勤科室则默认从EXCEL中带出数据
else if (mode == OverrideMode.EmployeeDict && UnitTypeUtil.IsOffice(second.UnitType))
numbers = LoadEmployees_OfficeExcel(second);
else if (mode == OverrideMode.EmployeeDict)
numbers = LoadEmployees_EmployeeDict(second, employees);
if (numbers == null || numbers.Count == 0)
return new List<ag_bodysource>();
return LoadEmployeeByDictionary(second, employees, numbers);
}
//var data = new List<Dictionary<string, object>>();
//foreach (var item in loadDatas)
//{
// data.Add(new Dictionary<string, object>
// {
// { nameof(ag_bodysource.SecondId), item.SecondId },
// { nameof(ag_bodysource.Department), item.Department },
// { nameof(ag_bodysource.WorkNumber), item.WorkNumber },
// { nameof(ag_bodysource.Name), item.Name },
// { nameof(ag_bodysource.JobTitle), item.JobTitle },
// { nameof(ag_bodysource.ReservedRatio), item.ReservedRatio },
// { nameof(ag_bodysource.Post), item.Post },
// // 设置默认值
// { nameof(ag_bodysource.StaffCoefficient), item.StaffCoefficient ?? 1 },
// { nameof(ag_bodysource.ActualAttendance), item.ActualAttendance ?? DateTime.DaysInMonth(allot.Year, allot.Month) },
// { nameof(ag_bodysource.TitleCoefficient), item.TitleCoefficient ?? 1 },
// });
//}
//return data;
}
List<ag_bodysource> LoadEmployeeByDictionary(ag_secondallot second, List<per_employee> employees, List<string> numbers)
{
List<ag_bodysource> loadDatas = new List<ag_bodysource>();
foreach (var personnelNumber in numbers)
{
var existEmp = employees.FirstOrDefault(w => w.PersonnelNumber?.Trim() == personnelNumber?.Trim());
if (existEmp == null)
continue;
loadDatas.Add(new ag_bodysource
{
SecondId = second.Id,
Department = existEmp.AccountingUnit,
WorkNumber = existEmp.PersonnelNumber,
Name = existEmp.DoctorName,
JobTitle = existEmp.JobTitle,
ReservedRatio = existEmp.ReservedRatio,
Post = "否",
});
}
return loadDatas;
}
private List<string> LoadEmployees_PrevSecondAllot(per_allot allot, ag_secondallot second)
{
//List<ag_bodysource> bodysources = new List<ag_bodysource>();
//// 上次二次分配分三种情况,1.其他来源 2.单工作量 3.多工作量
//var prevSecondAllot = _secondAllotDetails.GetPreviousSecondAllot(allot.HospitalId, second);
//var status = new int[] { (int)SecondAllotStatus.WaitReview, (int)SecondAllotStatus.PassAudit };
//if (prevSecondAllot != null && status.Contains(prevSecondAllot.Status ?? (int)SecondAllotStatus.Uncommitted))
//{
// if (prevSecondAllot.Status == 6)
// {
// var prevDatas = _agothersourceRepository.GetEntities(w => w.SecondId == prevSecondAllot.Id);
// if (prevDatas != null)
// {
// foreach (var pre in prevDatas)
// {
// var existEmp = dicEmployees.FirstOrDefault(w => w.PersonnelNumber?.Trim() == pre.WorkNumber?.Trim());
// if (existEmp != null)
// continue;
// bodysources.Add(new ag_bodysource
// {
// SecondId = second.Id,
// Department = existEmp.AccountingUnit,
// WorkNumber = existEmp.PersonnelNumber,
// Name = existEmp.DoctorName,
// JobTitle = existEmp.JobTitle,
// ReservedRatio = existEmp.ReservedRatio,
// Post = "否",
// });
// }
// }
// }
// else if (prevSecondAllot.Status == 7 || prevSecondAllot.Status == 8)
// {
// var prevDatas = _agfixatitemRepository
// .GetEntities(w => w.SecondId == prevSecondAllot.Id && w.RowNumber.HasValue && w.RowNumber > -1 && w.Type == (int)TempColumnType.TableFixedColumns);
// if (prevDatas != null)
// {
// foreach (var row in prevDatas.GroupBy(w => w.RowNumber.Value))
// {
// var personnelNumber = row.FirstOrDefault(w => w.ItemName == "人员工号")?.ItemValue;
// if (string.IsNullOrEmpty(personnelNumber))
// continue;
// var existEmp = dicEmployees.FirstOrDefault(w => w.PersonnelNumber?.Trim() == personnelNumber?.Trim());
// if (existEmp != null)
// continue;
// bodysources.Add(new ag_bodysource
// {
// SecondId = second.Id,
// Department = existEmp.AccountingUnit,
// WorkNumber = existEmp.PersonnelNumber,
// Name = existEmp.DoctorName,
// JobTitle = existEmp.JobTitle,
// ReservedRatio = existEmp.ReservedRatio,
// Post = "否",
// });
// }
// }
// }
// else if (prevSecondAllot.Status == 9 || prevSecondAllot.Status == 10)
// {
// var prevDatas = _agbodysourceRepository.GetEntities(w => w.SecondId == prevSecondAllot.Id);
// if (prevDatas != null)
// {
// foreach (var pre in prevDatas)
// {
// var existEmp = dicEmployees.FirstOrDefault(w => w.PersonnelNumber?.Trim() == pre.WorkNumber?.Trim());
// if (existEmp != null)
// continue;
// bodysources.Add(new ag_bodysource
// {
// SecondId = second.Id,
// Department = existEmp.AccountingUnit,
// WorkNumber = existEmp.PersonnelNumber,
// Name = existEmp.DoctorName,
// JobTitle = existEmp.JobTitle,
// ReservedRatio = existEmp.ReservedRatio,
// Post = "否",
// });
// }
// }
// }
//}
//return bodysources;
List<string> numbers = new List<string>();
// 上次二次分配分三种情况,1.其他来源 2.单工作量 3.多工作量
var prevSecondAllot = _secondAllotDetails.GetPreviousSecondAllot(allot.HospitalId, second);
var status = new int[] { (int)SecondAllotStatus.WaitReview, (int)SecondAllotStatus.PassAudit };
if (prevSecondAllot != null && status.Contains(prevSecondAllot.Status ?? (int)SecondAllotStatus.Uncommitted))
{
if (prevSecondAllot.Status == 6)
{
var prevDatas = _agothersourceRepository.GetEntities(w => w.SecondId == prevSecondAllot.Id);
numbers = prevDatas
?.Where(pre => string.IsNullOrEmpty(pre.WorkNumber?.Trim()))
.Select(pre => pre.WorkNumber?.Trim())
.Distinct().ToList() ?? new List<string>();
}
else if (prevSecondAllot.Status == 7 || prevSecondAllot.Status == 8)
{
var prevDatas = _agfixatitemRepository
.GetEntities(w => w.SecondId == prevSecondAllot.Id && w.RowNumber.HasValue && w.RowNumber > -1 && w.Type == (int)TempColumnType.TableFixedColumns);
numbers = prevDatas
?.GroupBy(w => w.RowNumber.Value)
.Select(row => row.FirstOrDefault(w => w.ItemName == "人员工号")?.ItemValue)
.Where(w => string.IsNullOrEmpty(w?.Trim()))
.Distinct().ToList() ?? new List<string>();
}
else if (prevSecondAllot.Status == 9 || prevSecondAllot.Status == 10)
{
var prevDatas = _agbodysourceRepository.GetEntities(w => w.SecondId == prevSecondAllot.Id);
numbers = prevDatas
?.Where(pre => string.IsNullOrEmpty(pre.WorkNumber?.Trim()))
.Select(pre => pre.WorkNumber?.Trim())
.Distinct().ToList() ?? new List<string>();
}
}
return numbers;
}
private List<string> LoadEmployees_EmployeeDict(ag_secondallot second, List<per_employee> dicEmployees)
{
//if (dicEmployees == null)
// return new List<ag_bodysource>();
//var employees = dicEmployees
// .Where(w => w.UnitType == second.UnitType && w.AccountingUnit == second.Department)
// .Select(emp => new ag_bodysource
// {
// SecondId = second.Id,
// Department = emp.AccountingUnit,
// WorkNumber = emp.PersonnelNumber,
// Name = emp.DoctorName,
// JobTitle = emp.JobTitle,
// ReservedRatio = emp.ReservedRatio,
// Post = "否",
// }).ToList();
//return employees;
var employees = dicEmployees
?.Where(w => w.UnitType == second.UnitType && w.AccountingUnit == second.Department)
.Select(emp => emp.PersonnelNumber?.Trim())
.Distinct().ToList() ?? new List<string>();
return employees;
}
private List<string> LoadEmployees_OfficeExcel(ag_secondallot second)
{
//List<ag_bodysource> bodysources = new List<ag_bodysource>();
//if (dicEmployees == null)
// return bodysources;
//var employees = _imemployeelogisticsRepository.GetEntities(w => w.AllotID == second.AllotId && w.AccountingUnit == second.Department);
//if (employees != null)
//{
// foreach (var emp in employees)
// {
// var existEmp = dicEmployees.FirstOrDefault(w => w.PersonnelNumber?.Trim() == emp.PersonnelNumber?.Trim());
// if (existEmp != null)
// continue;
// bodysources.Add(new ag_bodysource
// {
// SecondId = second.Id,
// Department = existEmp.AccountingUnit,
// WorkNumber = existEmp.PersonnelNumber,
// Name = existEmp.DoctorName,
// JobTitle = existEmp.JobTitle,
// ReservedRatio = existEmp.ReservedRatio,
// Post = "否",
// });
// }
//}
//return bodysources;
List<string> numbers = new List<string>();
var employees = _imemployeelogisticsRepository.GetEntities(w => w.AllotID == second.AllotId && w.AccountingUnit == second.Department);
if (employees != null)
numbers = employees.Select(w => w.PersonnelNumber).ToList();
return numbers;
}
private List<string> LoadEmployees_Save(List<ag_bodysource> saveDatas)
{
return saveDatas.Select(w => w.WorkNumber).ToList();
}
#region 动态生成列头信息
/// <summary>
/// 动态生成列头信息 /// 动态生成列头信息
/// </summary> /// </summary>
/// <param name="computeMode"></param> /// <param name="computeMode"></param>
/// <param name="loads"></param> /// <param name="loads"></param>
/// <returns></returns> /// <returns></returns>
private (List<string> colHeaderCustoms, List<HandsonColumn> columnCustoms) GetCustomColumns(int computeMode, List<TitleValue<string, decimal?>> loads) private (List<string> colHeaderCustoms, List<HandsonColumn> columnCustoms) GetCustomColumns(ComputeMode computeMode, List<TitleValue<string, decimal?>> loads)
{ {
var colHeaderCustoms = new List<string>(); var colHeaderCustoms = new List<string>();
var columnCustoms = new List<HandsonColumn>(); var columnCustoms = new List<HandsonColumn>();
// 工作量 // 工作量
if (computeMode == 2 || computeMode == 3) if (computeMode == ComputeMode.Horizontal || computeMode == ComputeMode.Vertical)
{ {
var keys = loads.Where(w => w.Title.StartsWith("AssessmentScore_")).Select(w => w.Title.Replace("AssessmentScore_", "")).Distinct(); var keys = loads.Where(w => w.Title.StartsWithIgnoreCase("AssessmentScore_")).Select(w => w.Title.Replace("AssessmentScore_", "")).Distinct();
foreach (var key in keys) foreach (var key in keys)
{ {
foreach (var item in loads.Where(w => w.Title.Equals($"AssessmentScore_{key}") || w.Title.StartsWith($"Workload_{key}_"))) if (loads.Any(w => w.Title.StartsWithIgnoreCase($"Workload_{key}_")))
{ {
colHeaderCustoms.Add(item.Value); foreach (var item in loads.Where(w => w.Title.EqualsIgnoreCase($"AssessmentScore_{key}") || w.Title.StartsWithIgnoreCase($"Workload_{key}_")))
columnCustoms.Add(new HandsonColumn(item.Title, format: DataFormat.小数)); {
colHeaderCustoms.Add(item.Value);
columnCustoms.Add(new HandsonColumn(item.Title.ToLower(), format: DataFormat.小数));
}
} }
} }
} }
// 单项奖励 // 单项奖励
foreach (var awards in loads.Where(w => w.Title.StartsWith("SingleAwards_"))) foreach (var awards in loads.Where(w => w.Title.StartsWithIgnoreCase("SingleAwards_")))
{ {
colHeaderCustoms.Add(awards.Value); colHeaderCustoms.Add(awards.Value);
columnCustoms.Add(new HandsonColumn(awards.Title, format: DataFormat.小数)); columnCustoms.Add(new HandsonColumn(awards.Title.ToLower(), format: DataFormat.小数));
} }
return (colHeaderCustoms, columnCustoms); return (colHeaderCustoms, columnCustoms);
} }
#endregion
#region 获取工作量及单项奖励
/// <summary> /// <summary>
/// 获取工作量及单项奖励 /// 获取工作量及单项奖励
/// </summary> /// </summary>
...@@ -159,7 +657,7 @@ public HandsonTableBase Load(int secondId, int computeMode) ...@@ -159,7 +657,7 @@ public HandsonTableBase Load(int secondId, int computeMode)
/// <param name="unitType"></param> /// <param name="unitType"></param>
/// <param name="accountingUnit"></param> /// <param name="accountingUnit"></param>
/// <returns></returns> /// <returns></returns>
private List<TitleValue<string, decimal?>> GetWorkLoads(int hospitalId, string unitType, string accountingUnit) public List<TitleValue<string, decimal?>> GetWorkLoads(int hospitalId, string unitType, string accountingUnit)
{ {
var workloads = _agworkloadRepository.GetEntities(t => t.HospitalId == hospitalId && t.Department == accountingUnit && t.UnitType == unitType) ?? new List<ag_workload>(); var workloads = _agworkloadRepository.GetEntities(t => t.HospitalId == hospitalId && t.Department == accountingUnit && t.UnitType == unitType) ?? new List<ag_workload>();
var loads = workloads var loads = workloads
...@@ -169,7 +667,9 @@ public HandsonTableBase Load(int secondId, int computeMode) ...@@ -169,7 +667,9 @@ public HandsonTableBase Load(int secondId, int computeMode)
return loads; return loads;
} }
#endregion
#region 三种计算对应的格式
/// <summary> /// <summary>
/// 格式生成,不计算,手动录入 /// 格式生成,不计算,手动录入
/// </summary> /// </summary>
...@@ -179,23 +679,22 @@ public HandsonTableBase Load(int secondId, int computeMode) ...@@ -179,23 +679,22 @@ public HandsonTableBase Load(int secondId, int computeMode)
private HandsonTableBase ComputeMode_Format1(List<string> colHeaders, List<HandsonColumn> columns) private HandsonTableBase ComputeMode_Format1(List<string> colHeaders, List<HandsonColumn> columns)
{ {
HandsonTableBase handson = new HandsonTableBase(); HandsonTableBase handson = new HandsonTableBase();
handson.ColHeaders.AddRange(new string[] { "工号", "姓名", "核算单元", "主管", "职称绩效", "工作量绩效工资", }); handson.ColHeaders.AddRange(new string[] { "工号", "姓名",/* "核算单元",*/ "主管", "职称绩效", "工作量绩效工资", });
handson.Columns.AddRange( handson.Columns.AddRange(
new HandsonColumn[] new HandsonColumn[]
{ {
new HandsonColumn("WorkNumber"), new HandsonColumn(nameof(ag_bodysource.WorkNumber).ToLower()),
new HandsonColumn("Name"), new HandsonColumn(nameof(ag_bodysource.Name).ToLower()),
new HandsonColumn("Department"), //new HandsonColumn(nameof(ag_bodysource.Department).ToLower()),
new HandsonColumn("Post"), new HandsonColumn(nameof(ag_bodysource.TitlePerformance).ToLower(), format: DataFormat.小数),
new HandsonColumn("TitlePerformance", format: DataFormat.小数), new HandsonColumn(nameof(ag_bodysource.WorkPerformance).ToLower(), format: DataFormat.小数),
new HandsonColumn("WorkPerformance", format: DataFormat.小数),
}); });
handson.ColHeaders.AddRange(colHeaders); handson.ColHeaders.AddRange(colHeaders);
handson.Columns.AddRange(columns); handson.Columns.AddRange(columns);
handson.ColHeaders.AddRange(new string[] { "夜班绩效", }); handson.ColHeaders.AddRange(new string[] { "夜班绩效", });
handson.Columns.AddRange(new HandsonColumn[] { new HandsonColumn("NightWorkPerformance", format: DataFormat.小数), }); handson.Columns.AddRange(new HandsonColumn[] { new HandsonColumn(nameof(ag_bodysource.NightWorkPerformance).ToLower(), format: DataFormat.小数), });
return handson; return handson;
} }
...@@ -208,25 +707,25 @@ private HandsonTableBase ComputeMode_Format1(List<string> colHeaders, List<Hands ...@@ -208,25 +707,25 @@ private HandsonTableBase ComputeMode_Format1(List<string> colHeaders, List<Hands
private HandsonTableBase ComputeMode_Format2(List<string> colHeaders, List<HandsonColumn> columns) private HandsonTableBase ComputeMode_Format2(List<string> colHeaders, List<HandsonColumn> columns)
{ {
HandsonTableBase handson = new HandsonTableBase(); HandsonTableBase handson = new HandsonTableBase();
handson.ColHeaders.AddRange(new string[] { "工号", "姓名", "核算单元", "主管", "人员系数", "出勤", "职称", "职称系数", }); handson.ColHeaders.AddRange(new string[] { "工号", "姓名", /*"核算单元",*/ "主管", "人员系数", "出勤", "职称", "职称系数", });
handson.Columns.AddRange( handson.Columns.AddRange(
new HandsonColumn[] new HandsonColumn[]
{ {
new HandsonColumn("WorkNumber"), new HandsonColumn(nameof(ag_bodysource.WorkNumber).ToLower()),
new HandsonColumn("Name"), new HandsonColumn(nameof(ag_bodysource.Name).ToLower()),
new HandsonColumn("Department"), //new HandsonColumn(nameof(ag_bodysource.Department).ToLower()),
new HandsonColumn("Post"), new HandsonColumn(nameof(ag_bodysource.Post).ToLower()){ Type = "autocomplete", Strict = true, Source = new string[] { "是", "否"} },
new HandsonColumn("StaffCoefficient", format: DataFormat.小数), new HandsonColumn(nameof(ag_bodysource.StaffCoefficient).ToLower(), format: DataFormat.小数),
new HandsonColumn("ActualAttendance", format: DataFormat.小数), new HandsonColumn(nameof(ag_bodysource.ActualAttendance).ToLower(), format: DataFormat.小数1),
new HandsonColumn("JobTitle"), new HandsonColumn(nameof(ag_bodysource.JobTitle).ToLower()),
new HandsonColumn("TitleCoefficient", format: DataFormat.小数), new HandsonColumn(nameof(ag_bodysource.TitleCoefficient).ToLower(), format: DataFormat.小数),
}); });
handson.ColHeaders.AddRange(colHeaders); handson.ColHeaders.AddRange(colHeaders);
handson.Columns.AddRange(columns); handson.Columns.AddRange(columns);
handson.ColHeaders.AddRange(new string[] { "夜班绩效", }); handson.ColHeaders.AddRange(new string[] { "夜班绩效", });
handson.Columns.AddRange(new HandsonColumn[] { new HandsonColumn("NightWorkPerformance", format: DataFormat.小数), }); handson.Columns.AddRange(new HandsonColumn[] { new HandsonColumn(nameof(ag_bodysource.NightWorkPerformance).ToLower(), format: DataFormat.小数), });
return handson; return handson;
} }
...@@ -244,9 +743,8 @@ private HandsonTableBase ComputeMode_Format3(List<string> colHeaders, List<Hands ...@@ -244,9 +743,8 @@ private HandsonTableBase ComputeMode_Format3(List<string> colHeaders, List<Hands
var fs = handson.Columns.Select(col => var fs = handson.Columns.Select(col =>
{ {
var f = loads.FirstOrDefault(w => w.Title.StartsWith($"Workload_") && w.State.HasValue && w.State != 0 && w.Title == col.Data); var f = loads.FirstOrDefault(w => w.Title.StartsWithIgnoreCase($"Workload_") && w.Title.EqualsIgnoreCase(col.Data));
return f == null ? "" : (f.State ?? 0).ToString("F0");
return f == null || !f.State.HasValue ? "" : f.State.Value.ToString("F0");
}); });
handson.NestedHeadersArray.Add(handson.ColHeaders); handson.NestedHeadersArray.Add(handson.ColHeaders);
...@@ -254,5 +752,556 @@ private HandsonTableBase ComputeMode_Format3(List<string> colHeaders, List<Hands ...@@ -254,5 +752,556 @@ private HandsonTableBase ComputeMode_Format3(List<string> colHeaders, List<Hands
return handson; return handson;
} }
#endregion
#endregion
#region 补充医院其他绩效
/// <summary>
/// 补充医院其他绩效
/// </summary>
/// <param name="second"></param>
/// <param name="rows"></param>
public void SupplementOtherPerfor(ag_secondallot second, List<Dictionary<string, object>> rows)
{
if (rows == null || rows.Count == 0)
return;
var perapramounts = _perapramountRepository
.GetFullAmount(t => t.AllotId == second.AllotId && t.Status == 3);
if (perapramounts == null || !perapramounts.Any())
return;
foreach (var row in rows)
{
var personnelNumber = row.GetValue(nameof(ag_bodysource.WorkNumber), "");
var amounts = perapramounts.Where(w => w.PersonnelNumber?.Trim() == personnelNumber?.Trim());
row.AddOrUpdate(nameof(ag_bodysource.OtherPerformance), amounts.Sum(w => w.Amount ?? 0));
}
// 补充字典中该科室不存在,但有其它绩效的人员信息
var groupDatas = perapramounts
.Where(t => t.UnitType == second.UnitType && t.AccountingUnit == second.Department)
.GroupBy(w => new { PersonnelNumber = w.PersonnelNumber, DoctorName = w.DoctorName, AccountingUnit = w.AccountingUnit, UnitType = w.UnitType, });
foreach (var item in groupDatas)
{
if (!rows.Any(row => row.GetValue(nameof(ag_bodysource.WorkNumber), "") == item.Key.PersonnelNumber))
{
rows.Add(new Dictionary<string, object>
{
{ nameof(ag_bodysource.SecondId), second.Id },
{ nameof(ag_bodysource.WorkNumber), item.Key.PersonnelNumber },
{ nameof(ag_bodysource.Name), item.Key.DoctorName },
{ nameof(ag_bodysource.Department), item.Key.AccountingUnit },
{ nameof(ag_bodysource.OtherPerformance), item.Sum(w => w.Amount )},
});
}
}
}
#endregion
#region 重算顶部医院其他绩效
/// <summary>
/// 计算顶部相关总和>>医院其他绩效(比较特殊,仅用作展示,所以需要重新计算一次)
/// </summary>
/// <param name="head"></param>
/// <param name="rows"></param>
public void OverviewCalculate_OtherPerformance(Dictionary<string, object> head, List<Dictionary<string, object>> rows)
{
//医院其他绩效总和
var otherPerformance = rows.Sum(row => GetDecimal2(row, nameof(ag_bodysource.OtherPerformance)));
head.AddOrUpdate(nameof(ag_headsource.HosOtherPerformance), otherPerformance);
}
#endregion
#region 计算
/// <summary>
/// 分配结果结果计算
/// </summary>
/// <param name="computeMode"></param>
/// <param name="head"></param>
/// <param name="rows"></param>
/// <param name="loads"></param>
/// <param name="workloadGroups"></param>
public void ResultCompute(ComputeMode computeMode, Dictionary<string, object> head, List<Dictionary<string, object>> rows, List<TitleValue<string, decimal?>> loads, List<SecondWorkLoadDto> workloadGroups)
{
var specialPostName = new string[] { "科主任/护士长", "主任", "主管", "是", };
if (computeMode == ComputeMode.NotCalculate)
{
// 清空无效数据
clearPerformanceWorkload(rows, workloadGroups);
// 行内可分配绩效
distPerformanceCalculate(head, rows, workloadGroups);
// 行内实发绩效
realAmountCalculate(head, rows, specialPostName);
}
else if (computeMode == ComputeMode.Horizontal || computeMode == ComputeMode.Vertical)
{
// 行内计算单项奖励
deptRewardCalculate(rows);
// 计算顶部相关总和
overviewCalculate(head, rows);
// 计算顶部工作量
topWorkloadCalculate(head, workloadGroups);
// 计算顶部年资系数
topSeniorityCalculate(head);
// 行内主任基础绩效
basisPerformanceCalculate(head, rows, specialPostName);
// 行内职称绩效计算
titleCoefficientCalculate(head, rows, specialPostName);
// 行内工作量分组计算
workloadCalculate(head, rows, computeMode, loads, workloadGroups, specialPostName);
// 行内可分配绩效
distPerformanceCalculate(head, rows, workloadGroups);
// 差额从主任或第一个人身上扣除
balanceTotalDistPerformance(head, rows, specialPostName);
// 行内实发绩效
realAmountCalculate(head, rows, specialPostName);
}
}
/// <summary>
/// ComputeMode.NotCalculate 不计算时,清空无效数据
/// </summary>
/// <param name="rows"></param>
/// <param name="workloadGroups"></param>
private void clearPerformanceWorkload(List<Dictionary<string, object>> rows, List<SecondWorkLoadDto> workloadGroups)
{
foreach (var row in rows)
{
foreach (var workload in workloadGroups)
{
row.Remove(workload.WorkloadScore);
row.Remove(workload.AssessmentScore);
row.Remove(workload.WorkPerformance);
foreach (var item in workload.Items)
{
row.Remove(item);
}
}
}
}
/// <summary>
/// 行内计算单项奖励
/// </summary>
/// <param name="rows"></param>
private void deptRewardCalculate(List<Dictionary<string, object>> rows)
{
foreach (var row in rows)
{
decimal total_deptReward = row.Where(w => w.Key.StartsWithIgnoreCase("SingleAwards_")).Sum(r => GetDecimal2(row, r.Key));
row.AddOrUpdate(nameof(ag_bodysource.DeptReward), total_deptReward);
}
}
/// <summary>
/// 计算顶部工作量
/// </summary>
/// <param name="head"></param>
/// <param name="workloadGroups"></param>
private void topWorkloadCalculate(Dictionary<string, object> head, List<SecondWorkLoadDto> workloadGroups)
{
foreach (var workload in workloadGroups)
{
var amount = GetDecimal2(head, nameof(ag_headsource.TheTotalAllocationOfPerformanceResults)) * GetDecimal2(head, $"Workload_Ratio_{workload.Name}");
head.AddOrUpdate($"Workload_Amount_{workload.Name}", amount);
}
}
/// <summary>
/// 计算顶部年资系数
/// </summary>
/// <param name="head"></param>
private void topSeniorityCalculate(Dictionary<string, object> head)
{
var amount = GetDecimal2(head, nameof(ag_headsource.SeniorityTitlesAccountedPerformance)) * GetDecimal2(head, nameof(ag_headsource.TheTotalAllocationOfPerformanceResults));
head.AddOrUpdate(nameof(ag_headsource.SeniorityTitlesPerformance), amount);
}
/// <summary>
/// 计算顶部相关总和
/// </summary>
/// <param name="head"></param>
/// <param name="rows"></param>
private void overviewCalculate(Dictionary<string, object> head, List<Dictionary<string, object>> rows)
{
// 可分配绩效(顶栏) = 科室总绩效 - 夜班绩效(顶栏)
var totalPerformance = GetDecimal2(head, nameof(ag_headsource.TotalDistPerformance)) - GetDecimal2(head, nameof(ag_headsource.NightShiftWorkPerforTotal));
head.AddOrUpdate(nameof(ag_headsource.TotalPerformance), totalPerformance);
//夜班工作量绩效总和
var nightShiftWorkPerforTotal = rows.Sum(row => GetDecimal2(row, nameof(ag_bodysource.NightWorkPerformance)));
head.AddOrUpdate(nameof(ag_headsource.NightShiftWorkPerforTotal), nightShiftWorkPerforTotal);
////医院其他绩效总和
//var otherPerformance = rows.Sum(row => GetDecimal2(row, nameof(ag_bodysource.OtherPerformance)));
//head.AddOrUpdate(nameof(ag_headsource.HosOtherPerformance), otherPerformance);
//科室核算人数 = 人员系数 * 实际出勤
var daysFullAttendance = GetDecimal2(head, nameof(ag_headsource.DaysFullAttendance));
var theNumberOfAccountingDepartment = daysFullAttendance == 0 ? 0m : rows.Sum(row => GetDecimal2(row, nameof(ag_bodysource.StaffCoefficient)) * GetDecimal2(row, nameof(ag_bodysource.ActualAttendance)) / daysFullAttendance);
head.AddOrUpdate(nameof(ag_headsource.TheNumberOfAccountingDepartment), 0m);
// 科室人均 = 可分配绩效 / 科室核算人数
var departmentsPerCapita = theNumberOfAccountingDepartment == 0 ? 0m : GetDecimal2(totalPerformance / theNumberOfAccountingDepartment);
head.AddOrUpdate(nameof(ag_headsource.DepartmentsPerCapita), departmentsPerCapita);
// 科室单项奖励
var totalDeptReward = rows.Sum(row => GetDecimal2(row, nameof(ag_bodysource.DeptReward)));
head.AddOrUpdate(nameof(ag_headsource.TotalDeptReward), totalDeptReward);
// 主任基础绩效(顶部) = 行内主任基础绩效总和
var directorBasisPerformance = rows.Sum(row => GetDecimal2(row, nameof(ag_bodysource.BasisPerformance)));
head.AddOrUpdate(nameof(ag_headsource.DirectorBasisPerformance), directorBasisPerformance);
// 业绩分配绩效总额 = 可分配绩效 - 科主任或护士长基础绩效 - 科室单项奖励
var theTotalAllocationOfPerformanceResults = totalPerformance - directorBasisPerformance - totalDeptReward;
head.AddOrUpdate(nameof(ag_headsource.TheTotalAllocationOfPerformanceResults), theTotalAllocationOfPerformanceResults);
}
/// <summary>
/// 行内主任基础绩效
/// </summary>
/// <param name="head"></param>
/// <param name="rows"></param>
/// <param name="specialPostName"></param>
private void basisPerformanceCalculate(Dictionary<string, object> head, List<Dictionary<string, object>> rows, string[] specialPostName)
{
foreach (var row in rows)
{
// 除 科主任/护士长 以外人员没有 主任基础绩效
// 当前行主任基础绩效 = 科室人均 * 当前行人员系数 * 当前行人员出勤/满勤天数
var post = row.GetValue(nameof(ag_bodysource.Post), "");
if (specialPostName.Contains(post))
{
var daysFullAttendance = GetDecimal2(head, nameof(ag_headsource.DaysFullAttendance));
var departmentsPerCapita = GetDecimal2(head, nameof(ag_headsource.DepartmentsPerCapita));
var staffCoefficient = GetDecimal2(row, nameof(ag_bodysource.StaffCoefficient));
var actualAttendance = GetDecimal2(row, nameof(ag_bodysource.ActualAttendance));
var basisPerformance = daysFullAttendance == 0 ? 0 : departmentsPerCapita * staffCoefficient * actualAttendance / daysFullAttendance;
row.AddOrUpdate(nameof(ag_bodysource.BasisPerformance), basisPerformance);
}
else
{
row.AddOrUpdate(nameof(ag_bodysource.BasisPerformance), 0m);
}
}
}
/// <summary>
/// 行内职称绩效计算
/// </summary>
/// <param name="head"></param>
/// <param name="rows"></param>
/// <param name="specialPostName"></param>
private void titleCoefficientCalculate(Dictionary<string, object> head, List<Dictionary<string, object>> rows, string[] specialPostName)
{
var total_titleCoefficient = 0m;
foreach (var row in rows)
{
var post = row.GetValue(nameof(ag_bodysource.Post), "");
if (!specialPostName.Contains(post))
total_titleCoefficient += GetDecimal2(row, nameof(ag_bodysource.ActualAttendance)) * GetDecimal2(row, nameof(ag_bodysource.TitleCoefficient));
}
var seniorityTitlesPerformance = GetDecimal2(head, nameof(ag_headsource.SeniorityTitlesPerformance));
foreach (var row in rows)
{
var post = row.GetValue(nameof(ag_bodysource.Post), "");
// 科主任/护士长 不参与职称绩效考核
if (specialPostName.Contains(post))
row.AddOrUpdate(nameof(ag_bodysource.TitleCoefficient), 0m);
//个人职称绩效 = ( 当前行实际出勤 * 当前行职称系数 ) / 职称系数总和* 年资职称绩效总和
var titlePerformance = total_titleCoefficient == 0 ? 0 : GetDecimal2(row, nameof(ag_bodysource.ActualAttendance)) * GetDecimal2(row, nameof(ag_bodysource.TitleCoefficient)) / total_titleCoefficient * seniorityTitlesPerformance;
row.AddOrUpdate(nameof(ag_bodysource.TitlePerformance), GetDecimal2(titlePerformance));
}
}
/// <summary>
/// 行内工作量分组计算
/// </summary>
/// <param name="head"></param>
/// <param name="rows"></param>
/// <param name="computeMode"></param>
/// <param name="loads"></param>
/// <param name="workloadGroups"></param>
/// <param name="specialPostName"></param>
private void workloadCalculate(Dictionary<string, object> head, List<Dictionary<string, object>> rows, ComputeMode computeMode,
List<TitleValue<string, decimal?>> loads, List<SecondWorkLoadDto> workloadGroups, string[] specialPostName)
{
// 计算方式:1 不计算 2 横向计算 3 纵向计算
foreach (var row in rows)
{
foreach (var gp in workloadGroups)
{
var workload_score = 0m;
// 计算方式:1 不计算 2 横向计算 3 纵向计算
switch (computeMode)
{
case ComputeMode.Horizontal:
workload_score = ComputeMode_2(row, gp, specialPostName);
break;
case ComputeMode.Vertical:
workload_score = ComputeMode_3(row, loads, gp, specialPostName);
break;
}
// 工作量得分
row.AddOrUpdate(gp.WorkloadScore, GetDecimal2(workload_score));
}
}
// 工作量每分价格计算
foreach (var gp in workloadGroups)
{
// 汇总行内工作量总得分
var total_score = rows.Sum(row => GetDecimal2(row, gp.WorkloadScore) * GetDecimal2(row, gp.AssessmentScore));
// 计算每分价格
gp.Unit_Price = total_score == 0 ? 0 : head.GetValue($"Workload_Amount_{gp.Name}", 0m) / total_score;
}
// 行内工作量绩效计算
foreach (var row in rows)
{
var workPerformance = 0m;
var post = row.GetValue(nameof(ag_bodysource.Post), "");
foreach (var gp in workloadGroups)
{
// 科主任/护士长 不参与工作量考核
if (specialPostName.Contains(post))
{
row.AddOrUpdate(gp.WorkloadScore, 0m);
row.AddOrUpdate(gp.AssessmentScore, 0m);
}
// 工作量绩效 = 工作量得分 * 每分价格 * 考核得分
var workload_fee = GetDecimal2(row.GetValue(gp.WorkloadScore, 0m) * gp.Unit_Price * row.GetValue(gp.AssessmentScore, 0m));
row.AddOrUpdate(gp.WorkPerformance, workload_fee);
workPerformance += workload_fee;
}
// 行内工作量汇总到一起
row.AddOrUpdate(nameof(ag_bodysource.WorkPerformance), workPerformance);
}
}
#region 行内工作量 辅助计算
/// <summary>
/// 获取系数
/// </summary>
/// <param name="workloads"></param>
/// <param name="item"></param>
/// <returns></returns>
decimal getFactorValue(List<TitleValue<string, decimal?>> workloads, string item) => workloads.FirstOrDefault((w) => w.Title == item)?.State ?? 0m;
/// <summary>
/// 3 纵向计算
/// </summary>
/// <param name="row"></param>
/// <param name="loads"></param>
/// <param name="gp"></param>
/// <param name="specialPostName"></param>
/// <returns></returns>
decimal ComputeMode_3(Dictionary<string, object> row, List<TitleValue<string, decimal?>> loads, SecondWorkLoadDto gp, string[] specialPostName)
{
var post = row.GetValue(nameof(ag_bodysource.Post), "");
var workload_score = gp.Items.Sum((item) =>
{
// 科主任/护士长 不参与工作量考核
if (specialPostName.Contains(post))
row.AddOrUpdate(item, 0);
return GetDecimal2(row.GetValue(item, 0m) * getFactorValue(loads, item));
});
return workload_score;
}
/// <summary>
/// 2 横向计算
/// </summary>
/// <param name="row"></param>
/// <param name="gp"></param>
/// <param name="specialPostName"></param>
/// <returns></returns>
decimal ComputeMode_2(Dictionary<string, object> row, SecondWorkLoadDto gp, string[] specialPostName)
{
var post = row.GetValue(nameof(ag_bodysource.Post), "");
var workload_score = 0m;
if (gp.Items.Count % 2 == 0)
{
for (int i = 0; i < gp.Items.Count / 2; i = i + 2)
{
// 科主任/护士长 不参与工作量考核
if (specialPostName.Contains(post))
{
row.AddOrUpdate(gp.Items[i], 0m);
row.AddOrUpdate(gp.Items[i + 1], 0m);
}
var amount = row.GetValue(gp.Items[i], 0m) * row.GetValue(gp.Items[i + 1], 0m);
workload_score += GetDecimal2(amount);
}
}
return workload_score;
}
#endregion
/// <summary>
/// 行内可分配绩效
/// </summary>
/// <param name="head"></param>
/// <param name="rows"></param>
/// <param name="workloadGroups"></param>
private void distPerformanceCalculate(Dictionary<string, object> head, List<Dictionary<string, object>> rows, List<SecondWorkLoadDto> workloadGroups)
{
foreach (var row in rows)
{
var workPerformance = GetDecimal2(row, nameof(ag_bodysource.WorkPerformance));
var titlePerformance = GetDecimal2(row, nameof(ag_bodysource.TitlePerformance));
var deptReward = GetDecimal2(row, nameof(ag_bodysource.DeptReward));
var basisPerformance = GetDecimal2(row, nameof(ag_bodysource.BasisPerformance));
// 可分配绩效 =   当前行职称绩效 + 工作量绩效工资 + 当前行单项奖励 + 当前行主任基础绩效 (科主任护士长)
var distPerformance = titlePerformance + workPerformance + deptReward + basisPerformance;
row.AddOrUpdate(nameof(ag_bodysource.DistPerformance), distPerformance);
}
}
/// <summary>
/// 差额从主任或第一个人身上扣除(小于等于1元)
/// </summary>
/// <param name="head"></param>
/// <param name="rows"></param>
/// <param name="specialPostName"></param>
private void balanceTotalDistPerformance(Dictionary<string, object> head, List<Dictionary<string, object>> rows, string[] specialPostName)
{
var totalPerformance = GetDecimal2(head, nameof(ag_headsource.TotalPerformance));
var total_distPerformance = rows.Sum(row => GetDecimal2(row, nameof(ag_bodysource.DistPerformance)));
var difference = total_distPerformance - totalPerformance;
if (Math.Abs(difference) <= 1)
{
var atRow = rows.Where(row => specialPostName.Contains(row.GetValue(nameof(ag_bodysource.Post), "")));
if (atRow == null || atRow.Count() == 0)
atRow = rows;
if (atRow != null && atRow.Count() > 0)
{
var distPerformance = GetDecimal2(atRow.ElementAt(0), nameof(ag_bodysource.DistPerformance));
atRow.ElementAt(0).AddOrUpdate(nameof(ag_bodysource.DistPerformance), distPerformance - difference);
}
}
}
/// <summary>
/// 行内实发绩效
/// </summary>
/// <param name="head"></param>
/// <param name="rows"></param>
/// <param name="specialPostName"></param>
private void realAmountCalculate(Dictionary<string, object> head, List<Dictionary<string, object>> rows, string[] specialPostName)
{
foreach (var row in rows)
{
// 总绩效 = 夜班工作量绩效 + 医院其他绩效
var row_ShifaAmountOfPerformancePay = GetDecimal2(row, nameof(ag_bodysource.NightWorkPerformance)) + GetDecimal2(row, nameof(ag_bodysource.OtherPerformance));
// ReservedAmount 年度考核发放金额 DistPerformance 可分配绩效
var reservedAmount = GetDecimal2(row, nameof(ag_bodysource.DistPerformance)) * GetDecimal2(row, nameof(ag_bodysource.ReservedRatio));
row.AddOrUpdate(nameof(ag_bodysource.ReservedAmount), reservedAmount);
var realAmount = GetDecimal2(row, nameof(ag_bodysource.DistPerformance)) - GetDecimal2(row, nameof(ag_bodysource.ReservedAmount)) + row_ShifaAmountOfPerformancePay;
row.AddOrUpdate(nameof(ag_bodysource.RealAmount), realAmount);
}
}
#region 四射五入 辅助方式
/// <summary>
/// 从键值对中获取decimal,默认:0,保留2位小数
/// </summary>
/// <param name="pairs"></param>
/// <param name="key"></param>
/// <returns></returns>
private decimal GetDecimal2(Dictionary<string, object> pairs, string key) => GetDecimal2(pairs.GetValue(key, 0m));
/// <summary>
/// decimal?类型转换decimal 默认:0,保留2位小数
/// </summary>
/// <param name="pairs"></param>
/// <param name="key"></param>
/// <returns></returns>
private decimal GetDecimal2(decimal? value) => value.HasValue ? Math.Round(value.Value, 2, MidpointRounding.AwayFromZero) : 0m;
#endregion
#endregion
#region 获取行内工作量分组数
/// <summary>
/// 获取行内工作量分组数
/// </summary>
/// <param name="loads"></param>
/// <returns></returns>
public List<SecondWorkLoadDto> GetTopWorkloadBodyGroups(List<TitleValue<string, decimal?>> loads)
{
List<SecondWorkLoadDto> result = new List<SecondWorkLoadDto>();
if (loads != null)
{
var keys = loads
.Where(w => w.Title.StartsWithIgnoreCase("AssessmentScore_"))
.Select(w => w.Title.Replace("AssessmentScore_", ""))
.Distinct();
foreach (var key in keys)
{
SecondWorkLoadDto dto = new SecondWorkLoadDto(key);
foreach (var item in loads.Where(w => w.Title.StartsWithIgnoreCase($"Workload_{key}_")))
{
dto.AddItem(item.Title);
}
result.Add(dto);
}
}
return result;
}
#endregion
#region 检查工号和姓名是否匹配
/// <summary>
/// 检查工号和姓名是否匹配
/// </summary>
/// <param name="second"></param>
/// <param name="body"></param>
/// <returns></returns>
public List<SecondComputeCheckResultDto> CheckData(ag_secondallot second, List<Dictionary<string, object>> body)
{
if (body == null || body.Count == 0)
throw new PerformanceException("分配人员信息不存在!");
List<SecondComputeCheckResultDto> result = new List<SecondComputeCheckResultDto>();
var employees = _peremployeeRepository.GetEntities(w => w.AllotId == second.AllotId)
.Select(w => new { w.PersonnelNumber, w.DoctorName, w.AccountingUnit, w.UnitType, w.ReservedRatio });
for (int i = 0; i < body.Count; i++)
{
var item = body[i];
if (item.Where(w => w.Value != null && !string.IsNullOrEmpty(w.Value.ToString())).Count() > 0)
{
var number = item.GetValue(nameof(ag_bodysource.WorkNumber), "");
var name = item.GetValue(nameof(ag_bodysource.Name), "");
if (string.IsNullOrEmpty(number) || string.IsNullOrEmpty(name))
{
item.AddOrUpdate(nameof(ResponseType), ResponseType.Warning);
result.Add(new SecondComputeCheckResultDto(nameof(ResponseType.Warning), number, name, $"第{(i + 1)}行,工号或姓名无效;计算式忽略。"));
}
var emp = employees.FirstOrDefault(w => w.PersonnelNumber == number && w.DoctorName == name);
if (emp == null)
{
item.AddOrUpdate(nameof(ResponseType), ResponseType.Error);
result.Add(new SecondComputeCheckResultDto(nameof(ResponseType.Error), number, name, $"第{(i + 1)}行,工号和姓名在字典中不存在,请修复。"));
}
else
{
item.AddOrUpdate(nameof(ResponseType), ResponseType.OK);
item.AddOrUpdate(nameof(ag_bodysource.Department), emp.AccountingUnit);
item.AddOrUpdate(nameof(ag_bodysource.ReservedRatio), emp.ReservedRatio);
}
}
}
return result;
}
#endregion
} }
} }
...@@ -923,7 +923,7 @@ private void SupplementSecondDetail(ag_secondallot second, List<per_employee> em ...@@ -923,7 +923,7 @@ private void SupplementSecondDetail(ag_secondallot second, List<per_employee> em
/// <param name="hospitalId"></param> /// <param name="hospitalId"></param>
/// <param name="secondAllot"></param> /// <param name="secondAllot"></param>
/// <returns></returns> /// <returns></returns>
private ag_secondallot GetPreviousSecondAllot(int hospitalId, ag_secondallot secondAllot) public ag_secondallot GetPreviousSecondAllot(int hospitalId, ag_secondallot secondAllot)
{ {
// 历史删除绩效时,未删除对应的二次绩效记录 // 历史删除绩效时,未删除对应的二次绩效记录
var allotList = perallotRepository.GetEntities(w => w.HospitalId == hospitalId)?.OrderBy(s => s.Year).ThenBy(s => s.Month).ToList(); var allotList = perallotRepository.GetEntities(w => w.HospitalId == hospitalId)?.OrderBy(s => s.Year).ThenBy(s => s.Month).ToList();
......
...@@ -1605,7 +1605,7 @@ bool VerifySubmissioAmount(decimal? submitDataAmount, decimal? realGiveFee) ...@@ -1605,7 +1605,7 @@ bool VerifySubmissioAmount(decimal? submitDataAmount, decimal? realGiveFee)
if (!VerifySubmissioAmount(total, second.RealGiveFee)) if (!VerifySubmissioAmount(total, second.RealGiveFee))
throw new PerformanceException($"总金额与考核后金额不一致!可分配金额:{second.RealGiveFee},提交金额:{total}"); throw new PerformanceException($"总金额与考核后金额不一致!可分配金额:{second.RealGiveFee},提交金额:{total}");
} }
else if (new int[] { 9, 10 }.Contains(temp.UseTempId.Value)) else /*if (new int[] { 9, 10 }.Contains(temp.UseTempId.Value))*/
{ {
var data = agbodysourceRepository.GetEntities(t => t.SecondId == second.Id); var data = agbodysourceRepository.GetEntities(t => t.SecondId == second.Id);
if (data == null || !data.Any()) if (data == null || !data.Any())
...@@ -1803,7 +1803,7 @@ public bool NursingDeptAudit(int userId, SecondAuditRequest request) ...@@ -1803,7 +1803,7 @@ public bool NursingDeptAudit(int userId, SecondAuditRequest request)
/// </summary> /// </summary>
/// <param name="secondId">二次绩效Id</param> /// <param name="secondId">二次绩效Id</param>
/// <returns></returns> /// <returns></returns>
public ag_secondallot GetSecondallot(int secondId) public ag_secondallot GetSecondAllot(int secondId)
{ {
return agsecondallotRepository.GetEntity(t => t.Id == secondId); return agsecondallotRepository.GetEntity(t => t.Id == secondId);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment