日报优化-去除不必要日志

dev
zhangming 1 year ago
parent dd07f9838e
commit 30ebc44bb8

@ -380,19 +380,34 @@ public class KQReportBiz extends BaseBean {
datas.putAll(getDailyFlowLeaveBackData(params,user));
/*考勤二开--出差公出流程餐补统计start*/
long startTime = System.currentTimeMillis();
datas.putAll(getDailyMealAllowanceData(params,user));
long endTime = System.currentTimeMillis();
long elapsedTime = endTime - startTime; // 执行时间
bb.writeLog("getDailyMealAllowanceData执行时间" + elapsedTime/1000 + "秒");
/*考勤二开--出差公出流程餐补统计end*/
// /*考勤二开--计算驻点餐补start*/
// datas.putAll(getDailyStatAllowanceData(params,user));
// /*考勤二开--计算驻点餐补end*/
//获取加班时长
Map<String, Object> dailyFlowOverTimeData = getDailyFlowOverTimeDataAllowance(params, user);
/*考勤二开--计算精密夜班餐补start*/
datas.putAll(getDailyNightShiftAllowanceData(params,user));
startTime = System.currentTimeMillis();
datas.putAll(getDailyNightShiftAllowanceDataTemp(params,user,dailyFlowOverTimeData));
endTime = System.currentTimeMillis();
elapsedTime = endTime - startTime; // 执行时间
bb.writeLog("getDailyNightShiftAllowanceData执行时间" + elapsedTime/1000 + "秒");
/*考勤二开--计算精密夜班餐补end*/
/*考勤二开--计算鸿仁驻点餐补start*/
datas.putAll(getDailyOtherStatAllowanceData(params,user));
startTime = System.currentTimeMillis();
datas.putAll(getDailyOtherStatAllowanceDataTemp(params,user,dailyFlowOverTimeData));
endTime = System.currentTimeMillis();
elapsedTime = endTime - startTime; // 执行时间
bb.writeLog("getDailyOtherStatAllowanceData执行时间" + elapsedTime/1000 + "秒");
/*考勤二开--计算鸿仁驻点餐补end*/
@ -1506,7 +1521,7 @@ public class KQReportBiz extends BaseBean {
writeLog(e);
}
bb.writeLog("datas: " + datas);
// bb.writeLog("datas: " + datas);
return datas;
}
@ -1756,7 +1771,251 @@ public class KQReportBiz extends BaseBean {
writeLog(e);
bb.writeLog("DailyOtherStatAllowance Exception: " + e);
}
bb.writeLog("DailyOtherStatAllowance datas: " + datas);
// bb.writeLog("DailyOtherStatAllowance datas: " + datas);
return datas;
}
public Map<String,Object> getDailyOtherStatAllowanceDataTemp(Map<String,Object> params, User user, Map<String, Object> dailyFlowOverTimeData){
Map<String,Object> datas = new HashMap<>();;
RecordSet rs = new RecordSet();
String sql = "";
String sqlWhere = " ";
try{
KQLeaveRulesComInfo kqLeaveRulesComInfo = new KQLeaveRulesComInfo();
JSONObject jsonObj = JSON.parseObject(Util.null2String(params.get("data")));
String fromDate = Util.null2String(jsonObj.get("fromDate"));
String toDate = Util.null2String(jsonObj.get("toDate"));
String typeselect =Util.null2String(jsonObj.get("typeselect"));
if(typeselect.length()==0)typeselect = "3";
if(!typeselect.equals("") && !typeselect.equals("0")&& !typeselect.equals("6")){
if(typeselect.equals("1")){
fromDate = TimeUtil.getCurrentDateString();
toDate = TimeUtil.getCurrentDateString();
}else{
fromDate = TimeUtil.getDateByOption(typeselect,"0");
toDate = TimeUtil.getDateByOption(typeselect,"1");
}
}
String subCompanyId = Util.null2String(jsonObj.get("subCompanyId"));
String departmentId = Util.null2String(jsonObj.get("departmentId"));
String resourceId = Util.null2String(jsonObj.get("resourceId"));
String allLevel = Util.null2String(jsonObj.get("allLevel"));
String isNoAccount = Util.null2String(jsonObj.get("isNoAccount"));
String viewScope = Util.null2String(jsonObj.get("viewScope"));
if(subCompanyId.length()>0){
sqlWhere +=" and a.subcompanyid1 in("+subCompanyId+") ";
}
if(departmentId.length()>0){
sqlWhere +=" and a.departmentid in("+departmentId+") ";
}
if(resourceId.length()>0){
sqlWhere +=" and a.id in("+resourceId+") ";
}
if(viewScope.equals("4")){//我的下属
if(allLevel.equals("1")){//所有下属
sqlWhere+=" and a.managerstr like '%,"+user.getUID()+",%'";
}else{
sqlWhere+=" and a.managerid="+user.getUID();//直接下属
}
}
if (!"1".equals(isNoAccount)) {
sqlWhere += " and a.loginid is not null "+(rs.getDBType().equals("oracle")?"":" and a.loginid<>'' ");
}
//获取加班时长
// Map<String, Object> dailyFlowOverTimeData = getDailyFlowOverTimeDataAllowance(params, user);
// bb.writeLog("dailyFlowOverTimeData: " + dailyFlowOverTimeData);
String otherstatsub = Util.null2String(bb.getPropValue("project_hostar", "otherstatsubcompany"));
if (StringUtils.isNotBlank(otherstatsub)) {
//查询该分部下的人员
List<String> resIds = new ArrayList<>();
sqlWhere += " and a.subcompanyid1 in (" + otherstatsub + ") ";
String acqResSql = "select a.id from hrmresource a where a.status in (0,1,2,3) " + sqlWhere;
bb.writeLog("acqResSql: " + acqResSql);
rs.executeQuery(acqResSql);
while (rs.next()) {
String id = Util.null2String(rs.getString("id"));
if (StringUtils.isNotBlank(id)) {
resIds.add(id);
}
}
bb.writeLog("resIds: " + resIds);
if (resIds != null && resIds.size() > 0 && StringUtils.isNotBlank(fromDate) && StringUtils.isNotBlank(toDate)) {
HostarUtil houtil = new HostarUtil();
List<String> allDates = houtil.getAllDates(fromDate, toDate);
bb.writeLog("allDates: " + allDates);
//先获取到出勤时长
Map<String, Double> attendanceMinsMap = new HashMap<>();
String acqAttenSql = " select resourceid, attendancemins, kqdate from kq_format_total where resourceid in (" + String.join(",", resIds) + ") and kqdate >='" + fromDate + "' and kqdate <='" + toDate + "'";
rs.executeQuery(acqAttenSql);
while (rs.next()) {
String resourceid = Util.null2String(rs.getString("resourceid"));
Double attendancemins = Util.getDoubleValue(Util.null2String(rs.getString("attendancemins")));
String kqdate = Util.null2String(rs.getString("kqdate"));
if (attendancemins >= 0.00) {
attendanceMinsMap.put(resourceid + "|" + kqdate, attendancemins);
}
}
List<String> removeRes = new ArrayList<>();
String acqNoOtherStatAllResSql = "select resourceid from uf_NoOtherStatAllRe where isdelete is null or isdelete = 0 ";
rs.executeQuery(acqNoOtherStatAllResSql);
while (rs.next()){
String resourceid = Util.null2String(rs.getString("resourceid"));
if (StringUtils.isNotBlank(resourceid) ) {
removeRes.add(resourceid);
}
}
if ( removeRes != null && removeRes.size() > 0 ) {
resIds.removeIf(removeRes::contains);
}
for (String date : allDates) {
for (String res : resIds) {
//获取考勤打卡
Map<String, Object> otherinfo = new HashMap<>();//存一些用得到的信息
String uuid = UUID.randomUUID().toString();
KQTimesArrayComInfo kqTimesArrayComInfo = new KQTimesArrayComInfo();
ArrayList<String> hostIps = InitServer.getRealIp();//获取IP
boolean oneSign = false;//一天一段出勤时间段
List<TimeScopeEntity> lsSignTime = new ArrayList<>();
List<TimeScopeEntity> lsWorkTime = new ArrayList<>();
// List<TimeScopeEntity> lsRestTime = new ArrayList<>();
KQWorkTime kqWorkTime = new KQWorkTime();
kqWorkTime.setIsFormat(true);
WorkTimeEntity workTime = kqWorkTime.getWorkTime(res, date);
String preDate = DateUtil.addDate(date, -1);//上一天日期
String nextDate = DateUtil.addDate(date, 1);//下一天日期
if (workTime != null) {
lsSignTime = workTime.getSignTime();//允许打卡时间
lsWorkTime = workTime.getWorkTime();//工作时间
// lsRestTime = workTime.getRestTime();//休息时段时间
oneSign = lsWorkTime != null && lsWorkTime.size() == 1;
}
int shiftCount = lsWorkTime == null ? 0 : lsWorkTime.size();
int shiftI = 0;
List<Object> lsCheckInfo = new ArrayList<>();
for (int i = 0; lsWorkTime != null && i < lsWorkTime.size(); i++) {
shiftI = i;
TimeScopeEntity signTimeScope = lsSignTime.get(i);
TimeScopeEntity workTimeScope = lsWorkTime.get(i);
Map<String, String> shifRuleMap = Maps.newHashMap();
String workBeginTime = Util.null2String(workTimeScope.getBeginTime());
int workBeginIdx = kqTimesArrayComInfo.getArrayindexByTimes(workBeginTime);
String workEndTime = Util.null2String(workTimeScope.getEndTime());
int workEndIdx = kqTimesArrayComInfo.getArrayindexByTimes(workEndTime);
boolean workEndTimeAcross = workTimeScope.getEndTimeAcross();
if (oneSign) {
//个性化设置只支持一天一次上下班
ShiftInfoBean shiftInfoBean = new ShiftInfoBean();
shiftInfoBean.setSplitDate(date);
shiftInfoBean.setShiftRuleMap(workTime.getShiftRuleInfo());
shiftInfoBean.setSignTime(lsSignTime);
shiftInfoBean.setWorkTime(lsWorkTime);
List<String> logList = Lists.newArrayList();
KQShiftRuleInfoBiz.getShiftRuleInfo(shiftInfoBean, res, shifRuleMap, logList);
if (!shifRuleMap.isEmpty()) {
if (!logList.isEmpty()) {
otherinfo.put("logList", logList);
}
otherinfo.put("shiftRule", shifRuleMap);
if (shifRuleMap.containsKey("shift_beginworktime")) {
String shift_beginworktime = Util.null2String(shifRuleMap.get("shift_beginworktime"));
if (shift_beginworktime.length() > 0) {
workBeginTime = Util.null2String(shift_beginworktime);
workBeginIdx = kqTimesArrayComInfo.getArrayindexByTimes(workBeginTime);
workTimeScope.setBeginTime(workBeginTime);
workTimeScope.setBeginTimeAcross(workBeginIdx >= 1440 ? true : false);
}
}
if (shifRuleMap.containsKey("shift_endworktime")) {
String shift_endworktime = Util.null2String(shifRuleMap.get("shift_endworktime"));
if (shift_endworktime.length() > 0) {
workEndTime = Util.null2String(shift_endworktime);
workEndIdx = kqTimesArrayComInfo.getArrayindexByTimes(workEndTime);
workTimeScope.setEndTime(workEndTime);
workTimeScope.setEndTimeAcross(workEndIdx >= 1440 ? true : false);
}
}
}
}
lsCheckInfo = new KQFormatSignData().getSignInfo(res, signTimeScope, workTimeScope, date, preDate, nextDate, kqTimesArrayComInfo, hostIps, uuid, shiftCount, shiftI);
}
List<String> signIdList = new ArrayList<>();
for (int i = 0; i < lsCheckInfo.size(); i++) {
Object o = lsCheckInfo.get(i);
if ( o != null && o != "") {
Map<String, Object> temp = (Map<String, Object>) o;
String signId = Util.null2String(temp.get("signId"));
if (StringUtils.isNotBlank(signId)) {
signIdList.add(signId);
}
}
}
Integer signNumber = 0;
if (signIdList !=null && signIdList.size() > 0) {
String acqShowAddress = "select showaddress from hrmschedulesign where id in (" + String.join(",", signIdList) + ") ";
// bb.writeLog("acqShowAddress: " + acqShowAddress);
rs.executeQuery(acqShowAddress);
while (rs.next()) {
String showaddress = Util.null2String(rs.getString("showaddress"));
if ("鸿仕达".equals(showaddress) || "hostar".equalsIgnoreCase(showaddress)) {
signNumber = signNumber + 1;
}
}
}
if (signNumber == 0) {
double workingDayOvertime_4leave = Util.getDoubleValue(Util.null2String(dailyFlowOverTimeData.get(res + "|" + date + "|workingDayOvertime_4leave")));
workingDayOvertime_4leave = workingDayOvertime_4leave < 0 ? 0 : workingDayOvertime_4leave;
double restDayOvertime_4leave = Util.getDoubleValue(Util.null2String(dailyFlowOverTimeData.get(res + "|" + date + "|restDayOvertime_4leave")));
restDayOvertime_4leave = restDayOvertime_4leave < 0 ? 0 : restDayOvertime_4leave;
double holidayOvertime_4leave = Util.getDoubleValue(Util.null2String(dailyFlowOverTimeData.get(res + "|" + date + "|holidayOvertime_4leave")));
holidayOvertime_4leave = holidayOvertime_4leave < 0 ? 0 : holidayOvertime_4leave;
double workingDayOvertime_nonleave = Util.getDoubleValue(Util.null2String(dailyFlowOverTimeData.get(res + "|" + date + "|workingDayOvertime_nonleave")));
workingDayOvertime_nonleave = workingDayOvertime_nonleave < 0 ? 0 : workingDayOvertime_nonleave;
double restDayOvertime_nonleave = Util.getDoubleValue(Util.null2String(dailyFlowOverTimeData.get(res + "|" + date + "|restDayOvertime_nonleave")));
restDayOvertime_nonleave = restDayOvertime_nonleave < 0 ? 0 : restDayOvertime_nonleave;
double holidayOvertime_nonleave = Util.getDoubleValue(Util.null2String(dailyFlowOverTimeData.get(res + "|" + date + "|holidayOvertime_nonleave")));
holidayOvertime_nonleave = holidayOvertime_nonleave < 0 ? 0 : holidayOvertime_nonleave;
double temp = workingDayOvertime_4leave + restDayOvertime_4leave + holidayOvertime_4leave +
workingDayOvertime_nonleave + restDayOvertime_nonleave + holidayOvertime_nonleave;
// bb.writeLog("-=-temp:" + temp);
Double attendanceMins = Util.getDoubleValue(Util.null2String(attendanceMinsMap.get(res + "|" + date)));
// bb.writeLog("-=-attendanceMins:" + attendanceMins);
double v = Math.round (((attendanceMins < 0.00) ? 0.00 : attendanceMins) + temp) / 60.00;
String value = "0";
if (v >= 4.00 && v < 9.00) {
value = "1";
} else if (v >= 9.00) {
value = "2";
}
// bb.writeLog("-=-value:" + value);
datas.put(res + "|" + "DailyOtherStatAllowance" + "|" + date, value);
}
}
}
}
}
}catch (Exception e){
writeLog(e);
bb.writeLog("DailyOtherStatAllowance Exception: " + e);
}
// bb.writeLog("DailyOtherStatAllowance datas: " + datas);
return datas;
}
@ -1918,7 +2177,163 @@ public class KQReportBiz extends BaseBean {
writeLog(e);
bb.writeLog("DailyNightShiftAllowanceData Exception: " + e);
}
bb.writeLog("DailyNightShiftAllowanceData datas: " + datas);
// bb.writeLog("DailyNightShiftAllowanceData datas: " + datas);
return datas;
}
public Map<String,Object> getDailyNightShiftAllowanceDataTemp(Map<String,Object> params, User user, Map<String, Object> dailyFlowOverTimeData){
Map<String,Object> datas = new HashMap<>();;
RecordSet rs = new RecordSet();
String sql = "";
String sqlWhere = " ";
try{
KQLeaveRulesComInfo kqLeaveRulesComInfo = new KQLeaveRulesComInfo();
JSONObject jsonObj = JSON.parseObject(Util.null2String(params.get("data")));
String fromDate = Util.null2String(jsonObj.get("fromDate"));
String toDate = Util.null2String(jsonObj.get("toDate"));
String typeselect =Util.null2String(jsonObj.get("typeselect"));
if(typeselect.length()==0)typeselect = "3";
if(!typeselect.equals("") && !typeselect.equals("0")&& !typeselect.equals("6")){
if(typeselect.equals("1")){
fromDate = TimeUtil.getCurrentDateString();
toDate = TimeUtil.getCurrentDateString();
}else{
fromDate = TimeUtil.getDateByOption(typeselect,"0");
toDate = TimeUtil.getDateByOption(typeselect,"1");
}
}
String subCompanyId = Util.null2String(jsonObj.get("subCompanyId"));
String departmentId = Util.null2String(jsonObj.get("departmentId"));
String resourceId = Util.null2String(jsonObj.get("resourceId"));
String allLevel = Util.null2String(jsonObj.get("allLevel"));
String isNoAccount = Util.null2String(jsonObj.get("isNoAccount"));
String viewScope = Util.null2String(jsonObj.get("viewScope"));
if(subCompanyId.length()>0){
sqlWhere +=" and a.subcompanyid1 in("+subCompanyId+") ";
}
if(departmentId.length()>0){
sqlWhere +=" and a.departmentid in("+departmentId+") ";
}
if(resourceId.length()>0){
sqlWhere +=" and a.id in("+resourceId+") ";
}
if(viewScope.equals("4")){//我的下属
if(allLevel.equals("1")){//所有下属
sqlWhere+=" and a.managerstr like '%,"+user.getUID()+",%'";
}else{
sqlWhere+=" and a.managerid="+user.getUID();//直接下属
}
}
if (!"1".equals(isNoAccount)) {
sqlWhere += " and a.loginid is not null "+(rs.getDBType().equals("oracle")?"":" and a.loginid<>'' ");
}
//获取加班时长
// Map<String, Object> dailyFlowOverTimeData = getDailyFlowOverTimeDataAllowance(params, user);
// bb.writeLog("dailyFlowOverTimeData: " + dailyFlowOverTimeData);
//获取夜班班次
List<String> nightShiftList = new ArrayList<>();
String acqNightShiftSql = "select shift from uf_nightshiftmanage where isdelete is null or isdelete = 0";
rs.executeQuery(acqNightShiftSql);
while (rs.next()) {
String shift = Util.null2String(rs.getString("shift"));
if (StringUtils.isNotBlank(shift)) {
nightShiftList.add(shift);
}
}
if (nightShiftList != null & nightShiftList.size() > 0) {
String nightshiftsub = Util.null2String(bb.getPropValue("project_hostar", "nightshiftsubcompany"));
if (StringUtils.isNotBlank(nightshiftsub)) {
//查询该分部下的人员
List<String> resIds = new ArrayList<>();
sqlWhere += " and a.subcompanyid1 in ("+nightshiftsub+") ";
String acqResSql = "select a.id from hrmresource a where a.status in (0,1,2,3) " + sqlWhere;
bb.writeLog("acqResSql: " + acqResSql);
rs.executeQuery(acqResSql);
while (rs.next()) {
String id = Util.null2String(rs.getString("id"));
if (StringUtils.isNotBlank(id)) {
resIds.add(id);
}
}
bb.writeLog("resIds: " + resIds);
if ( resIds != null && resIds.size() > 0) {
HostarUtil houtil = new HostarUtil();
List<String> allDates = houtil.getAllDates(fromDate, toDate);
//先获取到出勤时长
Map<String, Double> attendanceMinsMap = new HashMap<>();
String acqAttenSql = " select resourceid, attendancemins, kqdate from kq_format_total where resourceid in (" + String.join(",",resIds) + ") and kqdate >='" + fromDate + "' and kqdate <='" + toDate + "'";
rs.executeQuery(acqAttenSql);
while (rs.next()) {
String resourceid = Util.null2String(rs.getString("resourceid"));
Double attendancemins = Util.getDoubleValue(Util.null2String(rs.getString("attendancemins")));
String kqdate = Util.null2String(rs.getString("kqdate"));
if (attendancemins >= 0.00 ) {
attendanceMinsMap.put(resourceid+"|"+kqdate, attendancemins);
}
}
KQWorkTime kqWorkTime = new KQWorkTime();
for (String res: resIds) {
for (String date : allDates) {
//获取当天班次
Map<String, Object> serialInfo = kqWorkTime.getSerialInfo(res, date, false);
if (serialInfo != null && serialInfo.size() > 0) {
int serialid = Util.getIntValue(Util.null2String(serialInfo.get(date)), 0);
if (serialid > 0) {
if ( !nightShiftList.contains(String.valueOf(serialid))) {
continue;
}
}
}
double workingDayOvertime_4leave = Util.getDoubleValue(Util.null2String(dailyFlowOverTimeData.get(res + "|" + date + "|workingDayOvertime_4leave")));
workingDayOvertime_4leave = workingDayOvertime_4leave < 0 ? 0 : workingDayOvertime_4leave;
double restDayOvertime_4leave = Util.getDoubleValue(Util.null2String(dailyFlowOverTimeData.get(res + "|" + date + "|restDayOvertime_4leave")));
restDayOvertime_4leave = restDayOvertime_4leave < 0 ? 0 : restDayOvertime_4leave;
double holidayOvertime_4leave = Util.getDoubleValue(Util.null2String(dailyFlowOverTimeData.get(res + "|" + date + "|holidayOvertime_4leave")));
holidayOvertime_4leave = holidayOvertime_4leave < 0 ? 0 : holidayOvertime_4leave;
double workingDayOvertime_nonleave = Util.getDoubleValue(Util.null2String(dailyFlowOverTimeData.get(res + "|" + date + "|workingDayOvertime_nonleave")));
workingDayOvertime_nonleave = workingDayOvertime_nonleave < 0 ? 0 : workingDayOvertime_nonleave;
double restDayOvertime_nonleave = Util.getDoubleValue(Util.null2String(dailyFlowOverTimeData.get(res + "|" + date + "|restDayOvertime_nonleave")));
restDayOvertime_nonleave = restDayOvertime_nonleave < 0 ? 0 : restDayOvertime_nonleave;
double holidayOvertime_nonleave = Util.getDoubleValue(Util.null2String(dailyFlowOverTimeData.get(res + "|" + date + "|holidayOvertime_nonleave")));
holidayOvertime_nonleave = holidayOvertime_nonleave < 0 ? 0 : holidayOvertime_nonleave;
double temp = workingDayOvertime_4leave + restDayOvertime_4leave + holidayOvertime_4leave +
workingDayOvertime_nonleave + restDayOvertime_nonleave + holidayOvertime_nonleave;
// bb.writeLog("-=-temp:" + temp);
Double attendanceMins = Util.getDoubleValue(Util.null2String(attendanceMinsMap.get(res + "|" + date)));
// bb.writeLog("-=-attendanceMins:" + attendanceMins);
String value = String.valueOf(Math.floor( ((attendanceMins < 0.00 ? 0.00 : attendanceMins) + temp) / 300));
// bb.writeLog("-=-value:" + value);
datas.put(res + "|" + "DailyNightShiftAllowanceData" + "|" + date, value);
}
}
}
} else {
bb.writeLog("夜班餐补计算失败,没有设置该餐补享受分部");
}
} else {
bb.writeLog("夜班餐补计算失败,没有设置夜班");
}
}catch (Exception e){
writeLog(e);
bb.writeLog("DailyNightShiftAllowanceData Exception: " + e);
}
// bb.writeLog("DailyNightShiftAllowanceData datas: " + datas);
return datas;
}

@ -47,6 +47,7 @@ public class GetKQDailyReportCmd extends AbstractCommonCommand<Map<String, Objec
RecordSet rs = new RecordSet();
String sql = "";
try{
bb.writeLog("GetKQDailyReportCmd start.");
String pageUid = PageUidFactory.getHrmPageUid("KQDailyReport");
SubCompanyComInfo subCompanyComInfo = new SubCompanyComInfo();
@ -229,7 +230,12 @@ public class GetKQDailyReportCmd extends AbstractCommonCommand<Map<String, Objec
} else {
sql = " select " + sql;
}
long startTime = System.currentTimeMillis();
Map<String,Object> flowData = kqReportBiz.getDailyFlowData(params,user);
long endTime = System.currentTimeMillis();
long elapsedTime = endTime - startTime; // 执行时间
bb.writeLog("flowData执行时间" + elapsedTime/1000 + "秒");
// bb.writeLog("=-=-flowData: " + flowData);
// #1475814-概述:满足考勤报分部部门显示及导出时显示全路径
@ -505,6 +511,7 @@ public class GetKQDailyReportCmd extends AbstractCommonCommand<Map<String, Objec
}catch (Exception e){
writeLog(e);
}
bb.writeLog("GetKQDailyReportCmd end.");
return retmap;
}

Loading…
Cancel
Save