import decimal
import math
import os
from typing import Optional, Dict, Any

from flask import flash, redirect, url_for, get_flashed_messages

from flask import Blueprint, render_template, request, g
from sqlalchemy import text, or_, and_, distinct
from exts import db
from decorators import login_required
from forms.wgcommform import MealSetupForm, report_attSetupForm
from models.iccard import *
from forms.wgcommform import *  # AddControllerForm, LoginForm
import vwAppConfig
import datetime
# from openpyxl import Workbook
from urllib.parse import quote
from flask import make_response
from flask import send_file
import re
from decimal import Decimal
from datetime import  timedelta
from datetime import datetime
__all__ = [
    "bp",
    "web_report_att",
    "web_report_att_create",
    "web_report_att_setup",
]

from vwAppConfigParam import vwAppConfigParam

bp = Blueprint('wgcomm_report_att', __name__, url_prefix='/')


def get_last_month_range() -> [datetime.date, datetime.date]:
    today = datetime.date.today()  # 当前日期（2025-07-12）
    # 上Unit月最后一天 = 当月第一天减去1天
    first_day_of_current_month = today.replace(day=1)
    last_day_of_last_month = first_day_of_current_month - datetime.timedelta(days=1)
    # 上Unit月第一天 = 最后一天的day替换为1
    first_day_of_last_month = last_day_of_last_month.replace(day=1)
    return first_day_of_last_month, last_day_of_last_month


@bp.route('/report_att', methods=['GET', 'POST'])
@login_required
def web_report_att():  #
    return web_report_att_create()


@bp.route('/report_att_create', methods=['POST'])
@login_required
def web_report_att_create():
    url4htmltemplate = "wgcomm/report-att.html"  # template 实际操作界面
    url4redirect = "wgcomm_report_att.web_report_att"  # redirect 返回上一Unit界面 主界面
    get_flashed_messages()  # 2025-07-22 11:38:05 清空缓存内容信息
    wgform = AddUserForm()
    if request.form.get('f_BeginYMD'):  # 如果有日期 生成新的报表
        wgform = AddUserFormWithoutCard(request.form)
        if wgform.submitcancel.data:  # 取消时 退出到列表
            return redirect(url_for(url4redirect))  # url_for("wgcomm.controller_list"))  # 要跳转 controller_list()
        if wgform.submitsaveandadd.data:  # 下载上次生成的报表
            file_path = f"{vwAppConfigParam.wgcloudpathdir}考勤报表.xlsx"
            if os.path.exists(file_path):
                response = make_response(send_file(file_path))
                safe_name = quote("考勤报表.xlsx")  # 文件名URL编码
                response.headers["Content-Disposition"] = "attachment; filename*=UTF-8''{}".format(safe_name)
                return response
            else:
                flash("Report does not exist, please regenerate!  ")
                return redirect(url_for(url4redirect))
        f_BeginYMD = wgform.f_BeginYMD.data
        f_EndYMD = wgform.f_EndYMD.data
        f_BeginYMD = vwAppConfig.getDateTimeFromStr(f_BeginYMD)  # 2023-12-16 18:55:51
        f_EndYMD = vwAppConfig.getDateTimeFromStr(f_EndYMD)  # 2023-12-16 18:56:08
        f_GroupName = request.form.get('f_GroupName')
        info = startDeal(vwAppConfig.getStrOfDatetimeYMD(f_BeginYMD),
                         vwAppConfig.getStrOfDatetimeYMD(f_EndYMD),
                         f_GroupName)  # 2025-07-12 17:27:07
        try:
            # info = startDeal(vwAppConfig.getStrOfDatetimeYMD(f_BeginYMD),
            #                  vwAppConfig.getStrOfDatetimeYMD(f_EndYMD),
            #                  f_GroupName)  # 2025-07-12 17:27:07
            vwAppConfig.vwlog('{}--{} '.format(g.user.f_OperatorName, "Report generated successfully!  "))  # 增加日志
            # flash("Report generated successfully!  ")
            return info  # 下载文件
        except Exception as e:
            print(e)
            flash("Report generation failed!  ")
            vwAppConfig.vwlog('{}--{}[{}] '.format(g.user.f_OperatorName, "Report generation failed!  ", e))  # 增加日志
            wgform = AddUserForm(request.form)
            # return render_template(url4htmltemplate, wgform=wgform)
    query_obj = TBGroup.query.order_by(TBGroup.f_GroupNO)
    departentlist = query_obj.all()
    first_day, last_day = get_last_month_range()
    wgform.f_BeginYMD.data = first_day
    wgform.f_EndYMD.data = last_day
    context = {
        "wgform": wgform,
        "departments": departentlist
    }
    return render_template(url4htmltemplate, **context)  # wgform=wgform


# 获取Time和日期的查询
def getSqlOfDateTime(beginYMD, endYMD) -> str:
    strSql = "  ([f_ReadDate]>='{}') ".format(beginYMD)
    strSql += " AND "
    # strSql += "  ([f_ReadDate]<= '{}') ".format(endYMD)
    strSql += "  ([f_ReadDate]<= '{} 23:59:59') ".format(endYMD)
    return strSql


def get_sql_find_swipe_record_4_att(
        str_base_info: str,
        from_main_dt: str,
        str_time_con: str = "",
        group_min_no: int = 0,
        group_id_of_min_no: int = 0,
        group_max_no: int = 0,
        find_name: str = "",
        find_card: int = 0,
        find_consumer_id: int = 0
) -> str:
    """
    动态生成 SQL 查询语句，支持多条件筛选

    参数:
        str_base_info: SELECT 字段部分
        from_main_dt: 主表名
        str_time_con: Time条件表达式
        group_min_no: 最小Department编号
        group_id_of_min_no: 最小Department编号对应的ID
        group_max_no: 最大Department编号
        find_name: Name搜索关键词
        find_card: Card NO搜索值
        find_consumer_id: 消费者ID

    返回:
        完整的 SQL 查询语句
    """

    # 安全过滤函数（防止SQL注入）
    def safe_sql_value(value):
        if isinstance(value, str):
            # 转义单引号并移除危险字符
            value = re.sub(r"[;\\-]", "", value)
            value = value.replace("'", "''")
            return f"'{value}'"
        return str(value)

    # 基础 WHERE 条件
    conditions = ["1=1"]

    # Time条件处理
    if str_time_con:
        conditions.append(f"({str_time_con})")

    # 消费者ID精确查询
    if find_consumer_id > 0:
        conditions.append(f"t_b_Consumer.f_ConsumerID = {find_consumer_id}")
        join_clause = f"""
            (((t_b_Consumer
            INNER JOIN {from_main_dt} ON t_b_Consumer.f_ConsumerID = {from_main_dt}.f_ConsumerID)
            INNER JOIN t_b_Reader ON t_b_Reader.f_ReaderID = {from_main_dt}.f_ReaderID)
            INNER JOIN t_b_Controller ON t_b_Reader.f_ControllerID = t_b_Controller.f_ControllerID)
            LEFT JOIN t_b_Group ON t_b_Consumer.f_GroupID = t_b_Group.f_GroupID
        """
        #  2025-07-17 12:24:42
        join_clause = f"""
            (t_b_Consumer
            INNER JOIN {from_main_dt} ON t_b_Consumer.f_ConsumerID = {from_main_dt}.f_ConsumerID)
            LEFT JOIN t_b_Group ON t_b_Consumer.f_GroupID = t_b_Group.f_GroupID
        """
        join_clause = f"""
            (((t_b_Consumer
            INNER JOIN {from_main_dt} ON t_b_Consumer.f_ConsumerID = {from_main_dt}.f_ConsumerID)
            INNER JOIN t_b_door ON t_b_door.f_DoorID = {from_main_dt}.f_DoorID)
            )
            LEFT JOIN t_b_Group ON t_b_Consumer.f_GroupID = t_b_Group.f_GroupID
        """
    else:
        # Name模糊查询
        if find_name:
            safe_name = safe_sql_value(f"%{find_name}%")
            conditions.append(f"t_b_Consumer.f_ConsumerName LIKE {safe_name}")

        # Card NO精确查询
        if find_card > 0:
            conditions.append(f"{from_main_dt}.f_CardNO = {find_card}")

        # Department范围查询
        if group_min_no > 0:
            if group_min_no >= group_max_no:
                conditions.append(f"t_b_Group.f_GroupID = {group_id_of_min_no}")
            else:
                conditions.append(f"t_b_Group.f_GroupNO BETWEEN {group_min_no} AND {group_max_no}")

            # join_clause = f"""
            #     (((t_b_Consumer
            #     INNER JOIN {from_main_dt} ON t_b_Consumer.f_ConsumerID = {from_main_dt}.f_ConsumerID)
            #     INNER JOIN t_b_Reader ON t_b_Reader.f_ReaderID = {from_main_dt}.f_ReaderID)
            #     INNER JOIN t_b_Controller ON t_b_Reader.f_ControllerID = t_b_Controller.f_ControllerID)
            #     INNER JOIN t_b_Group ON t_b_Consumer.f_GroupID = t_b_Group.f_GroupID
            # """
            join_clause = f"""
                (t_b_Consumer
                INNER JOIN {from_main_dt} ON t_b_Consumer.f_ConsumerID = {from_main_dt}.f_ConsumerID)                
                INNER JOIN t_b_Group ON t_b_Consumer.f_GroupID = t_b_Group.f_GroupID
            """
            join_clause = f"""
                (((t_b_Consumer
                INNER JOIN {from_main_dt} ON t_b_Consumer.f_ConsumerID = {from_main_dt}.f_ConsumerID)
                INNER JOIN t_b_door ON t_b_door.f_DoorID = {from_main_dt}.f_DoorID)
                )
                INNER JOIN t_b_Group ON t_b_Consumer.f_GroupID = t_b_Group.f_GroupID
            """
        else:
            join_clause = f"""
                ((({from_main_dt}
                INNER JOIN t_b_Consumer ON t_b_Consumer.f_ConsumerID = {from_main_dt}.f_ConsumerID)
                INNER JOIN t_b_Reader ON t_b_Reader.f_ReaderID = {from_main_dt}.f_ReaderID)
                INNER JOIN t_b_Controller ON t_b_Reader.f_ControllerID = t_b_Controller.f_ControllerID)
                LEFT JOIN t_b_Group ON t_b_Consumer.f_GroupID = t_b_Group.f_GroupID
            """
            join_clause = f"""
                ({from_main_dt}
                INNER JOIN t_b_Consumer ON t_b_Consumer.f_ConsumerID = {from_main_dt}.f_ConsumerID)
                LEFT JOIN t_b_Group ON t_b_Consumer.f_GroupID = t_b_Group.f_GroupID
            """
            join_clause = f"""
                ((({from_main_dt}
                INNER JOIN t_b_Consumer ON t_b_Consumer.f_ConsumerID = {from_main_dt}.f_ConsumerID)
                INNER JOIN t_b_door ON t_b_door.f_DoorID = {from_main_dt}.f_DoorID)
                )
                LEFT JOIN t_b_Group ON t_b_Consumer.f_GroupID = t_b_Group.f_GroupID
            """
    # 构建完整 SQL
    where_clause = " AND ".join(conditions)
    return f"""
        {str_base_info}
        FROM {join_clause}
        WHERE {where_clause}
    """


def get_query_consumer_condition_str(
        str_base_info: str,
        from_main_dt: str = "",  # 注意：此参数在函数中未使用，但保留参数位置
        str_time_con: str = "",
        group_min_no: int = 0,
        group_id_of_min_no: int = 0,
        group_max_no: int = 0,
        find_name: str = '',
        find_card: int = 0,
        find_consumer_id: int = 0
) -> str:
    """
    构建消费者查询条件的SQL语句

    参数:
        str_base_info: SELECT子句部分
        from_main_dt: 主表名（未使用）
        str_time_con: Time条件（未使用）
        group_min_no: 最小Department编号
        group_id_of_min_no: 最小Department编号对应ID
        group_max_no: 最大Department编号
        find_name: Name搜索关键词
        find_card: Card NO搜索值
        find_consumer_id: 消费者ID

    返回:
        完整的SQL查询语句
    """

    # 安全处理字符串值（防SQL注入）
    def safe_sql_value(value: str) -> str:
        escaped = value.replace("'", "''")  # 转义单引号
        return f"N'{escaped}'"  # Unicode安全格式[1](@ref)

    try:
        # 基础WHERE条件
        conditions = ["1>0"]
        if str_time_con:
            conditions.append(f"({str_time_con})")

        # 消费者ID精确查询
        if find_consumer_id > 0:
            return f"""
                {str_base_info}
                FROM t_b_Consumer
                LEFT JOIN t_b_Group ON t_b_Consumer.f_GroupID = t_b_Group.f_GroupID
                WHERE t_b_Consumer.f_ConsumerID = {find_consumer_id}
            """

        # Name模糊查询
        if find_name:
            like_value = f"%{find_name}%"
            conditions.append(f"t_b_Consumer.f_ConsumerName LIKE {safe_sql_value(like_value)}")

        # Card NO精确查询
        if find_card > 0:
            conditions.append(f"t_b_Consumer.f_CardNO = {find_card}")

        # Department范围查询
        if group_min_no > 0:
            if group_min_no >= group_max_no:
                join_condition = f"AND t_b_Group.f_GroupID = {group_id_of_min_no}"
                sql = f"""
                    {str_base_info}
                    FROM t_b_Consumer
                    INNER JOIN t_b_Group ON (
                        t_b_Consumer.f_GroupID = t_b_Group.f_GroupID 
                        {join_condition}
                    )
                """
            else:
                range_condition = (
                    f"AND t_b_Group.f_GroupNO >= {group_min_no} "
                    f"AND t_b_Group.f_GroupNO <= {group_max_no}"
                )
                sql = f"""
                    {str_base_info}
                    FROM t_b_Consumer
                    INNER JOIN t_b_Group ON (
                        t_b_Consumer.f_GroupID = t_b_Group.f_GroupID 
                        {range_condition}
                    )
                """
        else:
            sql = f"""
                {str_base_info}
                FROM t_b_Consumer
                LEFT JOIN t_b_Group ON t_b_Consumer.f_GroupID = t_b_Group.f_GroupID
            """

        # 组合WHERE条件
        where_clause = " AND ".join(conditions)
        return f"{sql} WHERE {where_clause}"

    except Exception as ex:
        # 实际项目中应使用日志系统
        print(f"SQL generation error:   {ex}")
        vwAppConfig.vwlog('{}--{}[{}]'.format(g.user.f_OperatorName, "".format(
            f"SQL generation error:   {ex}"), "ex"))  # 2024-01-13 12:59:02 增加日志
        return ""  # 返回空字符串避免中断流程


# 步骤1：模拟 DataView 过滤功能
def filter_data(table, condition):
    """实现类似 DataView.RowFilter 的过滤功能[2,10](@ref)"""
    if not condition:
        return table
    key, value = condition.split("=")
    key = key.strip()
    value = int(value.strip())  # 根据实际类型调整
    return [row for row in table if row.get(key) == value]

def get_dict_table(strSql):
    # strSql = "SELECT * FROM t_a_Holiday ORDER BY  f_NO ASC"
    query_obj = db.session.execute(text(strSql))
    objtable = query_obj.fetchall()
    columns = query_obj.keys()  # 获取列名
    objdict = [dict(zip(columns, row)) for row in objtable]
    return  objdict

def get_system_param_by_no(par_no):
    try:
        val = TASystemparam.query.filter_by(f_NO=par_no).first().f_Value
        return val
    except:
        pass
    return ""
def startDeal(beginYMD, endYMD, f_GroupName):  # 开始处理生成报表
    #1 获取班次设置参数 getAttendenceParam(); //获取班次设置参数
    wgform = get_report_att_setup()  # 获取到参数值
    #// 清空考勤Details  数据---这Unit直接放入excel表, 所以不用清库存了
    #_clearAttendenceData();
    #_clearAttStatistic();

    #"SELECT * FROM t_a_Holiday ORDER BY  f_NO ASC"
    #2 获取假日信息 按编号的上升进行排列
    strSql = "SELECT * FROM t_a_Holiday ORDER BY  f_NO ASC"
    objt_a_Holiday = get_dict_table(strSql)

    #3 请假出差表
    strSql = "SELECT * FROM t_d_Leave ORDER BY  f_NO ASC"
    objt_d_Leave = get_dict_table(strSql)

    #4 假期类型
    strSql = "SELECT *,0 as f_fullAttendance FROM t_a_HolidayType BY  f_NO ASC"
    objt_a_HolidayType = get_dict_table(strSql)

    # 本地化 localizedHolidayType(dtHolidayType);
    #                         dfrm.dtBegin = this.dtpDateFrom.Value;  //开始Time
    #                         dfrm.dtEnd = this.dtpDateTo.Value;      //结束Time
    #                         dfrm.strConsumerSql = strSql;           //人员

    #5 获取人员信息
    strSql = " SELECT   f_ConsumerID, f_ConsumerNO, f_ConsumerName, f_CardNO, f_AttendEnabled, f_ShiftEnabled "
    if f_GroupName == '':
        strSql += " FROM t_b_Consumer WHERE (f_AttendEnabled >0 ) " #;  //2010-5-28 22:51:28 引入考勤
    else:
        strSql += " FROM t_b_Consumer,t_b_Group "
        strSql += " WHERE (t_b_Consumer.f_AttendEnabled >0 ) and t_b_Consumer.f_GroupID = t_b_Group.f_GroupID  "
        strSql += f" and (f_GroupName == '{f_GroupName}' or f_GroupName like '{f_GroupName}/%')"
        #" AND  t_b_Group.f_GroupID ={0:d} ".format(groupIDOfMinNO);
    query_obj = db.session.execute(text(strSql))
    colt_b_Consumer = query_obj.keys()
    objt_b_Consumer = query_obj.fetchall()  # .all()

    Param_InvalidSwipeNotAsAttend = 54 #;  //2011-8-11_11:36:05 无效卡不作考勤
    Param_OffDutyLatestTimeForNormalAttendance = 55 # ;    //2011-8-11_10:37:15 正常考勤时 下班最晚刷卡Time , 缺省=00:00:00表示0点. 如果是1:00:00表示第二天凌晨1点
    Param_EarliestTimeAsOndutyForNormalAttendance = 56 #;           //2011-8-11_10:37:15 正常考勤时 上班取最早刷卡Time为上班Time ,  缺省=0 表示取靠近上班点的刷卡Time, =1 表示取最早的刷卡Time
    Param_OnlyTwoTimesForNormalAttendance =   57 #;             //2011-8-11_10:37:15 正常考勤时 2次刷卡 每天两次, 取最早和最晚, 并Statistics  上班Time  Earliest time as on-duty, Latest time as off-duty
    Param_NormalWorkTime = 58 #;             //2011-8-11_10:37:15 正常考勤时 2次刷卡 正常工作Time (半小时为单位)
    Param_OnlyOnDutyForNormalAttendance = 59 #; //2011-10-25_09:08:29 只计上班考勤
    ParamLoc_AsFullAttendance = 174 #;      //2015-02-01 10:27:36 作为全勤的假期
    ParamLoc_ManualRecordAsFullAttendance = 175 #      //2015-04-07 12:35:53 签到作为全勤(=0表示不作为全勤, 否则作为全勤
    Param_FullAttendanceSpecialA = 210 #; //2018-12-02 17:42:24 全勤 只要有刷卡就算全勤
    ParamLoc_ActivateOpenInvalidCardMoreTimesWarn = 211 #;      //2018-12-28 19:03:49连接刷无效刷卡达到3次报警
    # groupMinNO = 0
    # groupName = f_GroupName  # "" # "b11"
    # groupNameCondition = "1=1"
    # if groupName != '':
    #     groupNameCondition = f"(f_GroupName == '{groupName}' or f_GroupName like '{groupName}/%')"
    strAllowOndutyTime =get_system_param_by_no(Param_OffDutyLatestTimeForNormalAttendance) #
    bEarliestAsOnDuty = get_system_param_by_no(Param_EarliestTimeAsOndutyForNormalAttendance) == "1"# //2011-8-20_10:21:16 最早作为上班点
    bChooseTwoTimes = get_system_param_by_no(Param_OnlyTwoTimesForNormalAttendance) == "1" #//2011-8-11_11:26:08 只取两次
    needDutyHour = Decimal("0"+get_system_param_by_no(Param_NormalWorkTime)) # decimal.Parse(((wgAppConfig.getSystemParamByNO(wgGlobal.Param_NormalWorkTime).ToString())), System.Globalization.CultureInfo.InvariantCulture /*2015-09-18 20:46:29*/); //2011-8-11_11:27:57 Time
    bChooseOnlyOnDuty = get_system_param_by_no(Param_OnlyOnDutyForNormalAttendance) == "1" #((wgAppConfig.getSystemParamByNO(wgGlobal.Param_OnlyOnDutyForNormalAttendance).ToString()) == "1"); //2011-10-25_09:09:14 只计上班考勤
    bManualRecordAsFullAttendance =get_system_param_by_no(ParamLoc_ManualRecordAsFullAttendance) != "0" # ((wgAppConfig.getSystemParamByNO(wgGlobal.ParamLoc_ManualRecordAsFullAttendance).ToString()) != "0"); //2015-04-07 12:42:48 考勤作签到处理
    bFullAttendanceSpecialA = get_system_param_by_no(Param_FullAttendanceSpecialA) == "1" #((wgAppConfig.getSystemParamByNO(wgGlobal.Param_FullAttendanceSpecialA).ToString()) == "1"); //2018-12-02 17:47:59
    strAllowOffdutyTime = "23:59:59" #; // 不能由外部改动
    tTwoReadMintime = 60 # 60秒间隔

    normalDay = 0 #; // 正常不跨夜上班, 值为0;    跨夜上班, 值为1
    str_not_read_card = "未刷卡"
    str_lateness = "迟到"
    str_absence = "旷工"
    str_leave_early = "早退"
    str_rest = "休息"
    str_overtime = "加班"

    indexConsumer = 0
    for itm in objt_b_Consumer:
        indexConsumer += 1
        #6 按人员进行操作...[-F1, -F2的处理放后面
        # //刷卡记录 不要,f_ConsumerName,f_GroupName,f_ConsumerNO
        currentConsumerID = itm.f_ConsumerID
        currentid  = itm.f_ConsumerID
        # # //*************************取刷卡记录[有Time约束]
        # strSql = " SELECT f_ReadDate,f_Character,'' as f_Type  FROM t_d_SwipeRecord2022 "
        # strSql += " where " + getSqlOfDateTime(beginYMD, endYMD) + f"and f_ConsumerID={currentid} " # " AND ([f_ReadDate]>= " + PrepareStr(startDateTime, true, "yyyy-MM-dd 00:00:00") + ") ";
        # strSql += " ORDER BY f_ReadDate ASC "
        # query_obj = db.session.execute(text(strSql))
        # colSwipeRecord = query_obj.keys()
        # objt_d_SwipeRecord = query_obj.fetchall()  # .all()
        #
        # # 手动签到记录
        # strSql = " SELECT f_ReadDate,f_Character,'签到' as f_Type  FROM t_d_ManualCardRecord "
        # strSql += " where " + getSqlOfDateTime(beginYMD, endYMD) + f"and f_ConsumerID={currentid} " # " AND ([f_ReadDate]>= " + PrepareStr(startDateTime, true, "yyyy-MM-dd 00:00:00") + ") ";
        # strSql += " ORDER BY f_ReadDate ASC "
        # query_obj = db.session.execute(text(strSql))
        # colManualCardRecord = query_obj.keys()
        # objt_d_ManualCardRecord = query_obj.fetchall()  # .all()




        # # 使用UNION ALL合并查询
        # union_query = text("""
        #     SELECT id, name, created_at, 'table1' as source FROM table1
        #     UNION ALL
        #     SELECT id, name, created_at, 'table2' as source FROM table2
        #     ORDER BY created_at DESC
        # """)
        # 初始化Statistics  计数器
        #int j;
        #
        # //'第一步读取有关的记录
        # //if (tReadCardTimes == 2) //两次刷卡的处理
        # //{
        # int indexRecord=0
        # DateTime indexDate; //查询的Time
        # DateTime readDate;
        #
        # DateTime attStart; //开始Time
        # DateTime attEnd; //结束Time
        # int dayShouldWork; //应该出勤天数
        # int dayRealWork; //实际出勤天
        # bool bdayShouldWork; //是否应该出勤天数
        # bool bdayRealWork; //是否实际出勤天
        #
        # int totalLate; //迟到次数
        # int totalLeaveEarly; //早退次数
        # decimal totalAbsence; //旷工天数[0.5天为单位]
        # decimal totalOvertime; //加班Time[小时计, 0.5小时为刻度]
        # int totalNotReadCard; //未刷卡次数
        # decimal[] totalSpecialDay = new decimal[32]; //32Unit特殊Statistics  [主要用于请假或出差中
        # int totalLateMinutes; //迟到Time[分钟]
        # int totalLeaveEarlyMinutes; //早退Time[分钟]
        j = 0
        indexRecord=0
        indexDate = vwAppConfig.strtotime("2025-09-26 11:00:15")
        readDate = vwAppConfig.strtotime("2025-09-26 11:00:15")
        attStart = vwAppConfig.strtotime("2025-09-26 11:00:15")
        attEnd = vwAppConfig.strtotime("2025-09-26 11:00:15")
        dayShouldWork = 0 # //应该出勤天数
        dayRealWork=0 #; //实际出勤天
        bdayShouldWork= False #; //是否应该出勤天数
        bdayRealWork = False #; //是否实际出勤天

        totalLate=0 #; //迟到次数
        totalLeaveEarly=0 #; //早退次数
        totalAbsence = Decimal(0) #(0.0) #; //旷工天数[0.5天为单位]
        totalOvertime = Decimal(0) #; //加班Time[小时计, 0.5小时为刻度]
        totalNotReadCard= 0 #; //未刷卡次数
        totalSpecialDay = [Decimal(0)]*32 #new decimal[32]; //32Unit特殊Statistics  [主要用于请假或出差中
        totalLateMinutes = 0 #; //迟到Time[分钟]
        totalLeaveEarlyMinutes= 0 #//早退Time[分钟]

        t_onduty1 = wgform.t_a_Attendence_OnDuty1.data  #: 上午上班Time
        t_offduty1 = wgform.t_a_Attendence_OffDuty1.data  #: 上午下班Time
        t_onduty2 = wgform.t_a_Attendence_OnDuty2.data  #: 下午上班Time
        t_offduty2 = wgform.t_a_Attendence_OffDuty2.data  #: 下午下班Time
        t_late_timeout = wgform.t_a_Attendence_LateTimeout.data  #: 迟到容忍Time（分钟）
        t_late_absence_timeout = wgform.t_a_Attendence_LateAbsenceTimeout.data  #: 旷工判定Time（分钟）
        t_leave_timeout = wgform.t_a_Attendence_LeaveTimeout.data  #: 早退容忍Time（分钟）
        t_leave_absence_timeout = wgform.t_a_Attendence_LeaveAbsenceTimeout.data  #: 早退容忍Time（分钟）
        b_earliest_as_on_duty = bEarliestAsOnDuty  #: 是否以最早刷卡为准
        t_overtime_timeout = wgform.t_a_Attendence_OvertimeTimeout.data
        str_not_read_card = "未刷卡"  #: 未刷卡描述
        str_lateness = "迟到"  #: 迟到描述
        str_absence = "旷工"  #: 旷工描述

        # DataRow dr = null;
        dr = None
        #// Statistics  初始化部分
        startDateTime = beginYMD
        endDateTime = endYMD
        attStart = vwAppConfig.strtotime(f"{startDateTime} {strAllowOndutyTime}") # Strings.Format(startDateTime, "yyyy-MM-dd " + strAllowOndutyTime));
        dayneed = datetime.timedelta(days=normalDay)
        attEnd = vwAppConfig.strtotime(f"{endDateTime} {strAllowOffdutyTime}").__add__(dayneed)
        indexDate =  vwAppConfig.strtotime(f"{startDateTime} {strAllowOndutyTime}") #vwAppConfig.strtotime(Strings.Format(startDateTime, "yyyy-MM-dd " + strAllowOndutyTime));
        dayShouldWork = 0
        dayRealWork = 0
        totalLate = 0 #; // 迟到次数
        totalLeaveEarly = 0#; // 早退次数
        totalAbsence = 0#; // 旷工天数[0.5  天为单位]
        totalOvertime = 0 #; // 加班Time[小时计, 0.5        小时为刻度]
        totalNotReadCard = 0 #; // 未刷卡次数
        totalLateMinutes = 0 #; // 迟到Time[分钟]
        totalLeaveEarlyMinutes = 0 # ; // 早退Time[分钟]
        for itm2 in range(32):
            totalSpecialDay[itm2] = Decimal(0) # 特殊Statistics  的值设为0
        # 2. 间隔小的数据Delete掉 ***************************
        # int i = 0;
        # while (dvCardRecord.Count > (i + 1))
        # {
        #     if (((DateTime)(dvCardRecord[i + 1][0])).Subtract((DateTime)(dvCardRecord[i][0])).TotalSeconds < tTwoReadMintime)
        #     {
        #         dvCardRecord[i + 1].Delete(); //Delete掉 第二Unit
        #     }
        #     else
        #     {
        #         i++; //加1
        #     }
        # }

        # # 合并结果
        # combined = list(objt_d_SwipeRecord) + list(objt_d_ManualCardRecord)
        #
        # # 按Time排序（假设第1列是Time字段）
        # # sorted_result = sorted(combined, key=lambda x: x[0], reverse=True)
        # sorted_result = sorted(combined, key=lambda x: x[0])
        # //*************************取刷卡记录[有Time约束]
        strSql = " SELECT f_ReadDate,f_Character,'' as f_Type  FROM t_d_SwipeRecord2022 "
        strSql += " where " + getSqlOfDateTime(beginYMD,
                                               endYMD) + f"and f_ConsumerID={currentid} "  # " AND ([f_ReadDate]>= " + PrepareStr(startDateTime, true, "yyyy-MM-dd 00:00:00") + ") ";
        # 手动签到记录
        strSql += " UNION ALL "
        strSql += " SELECT f_ReadDate,f_Character,'签到' as f_Type  FROM t_d_ManualCardRecord "
        strSql += " where " + getSqlOfDateTime(beginYMD,
                                               endYMD) + f"and f_ConsumerID={currentid} "  # " AND ([f_ReadDate]>= " + PrepareStr(startDateTime, true, "yyyy-MM-dd 00:00:00") + ") ";
        strSql += " ORDER BY f_ReadDate ASC "
        query_obj = db.session.execute(text(strSql))
        colSwipeRecord = query_obj.keys()
        objt_d_SwipeRecord = query_obj.fetchall()  # .all()
        if objt_d_SwipeRecord:
            filtered_results = []
            prev_record = objt_d_SwipeRecord[0]  # 第一条记录总是保留
            filtered_results.append(prev_record)

            # 处理后续记录
            for current_record in objt_d_SwipeRecord[1:]:
                time_diff = current_record.f_ReadDate - prev_record.f_ReadDate
                # 如果Time间隔大于等于60秒，保留当前记录
                # if time_diff.total_seconds() >= tTwoReadMintime or current_record.f_Type=='签到': #包括签到...
                if time_diff.total_seconds() >= tTwoReadMintime:
                    filtered_results.append(current_record)
                    prev_record = current_record
            objt_d_SwipeRecord = filtered_results

        index_recordStart001 = -1
        while indexDate <= vwAppConfig.strtotime(f"{endDateTime} {strAllowOffdutyTime}").__add__(dayneed):
            #单UnitDetails  记录初始化部分
            dr = {} #[dict] # [] #dtAttendenceData.NewRow();
            dr['f_ConsumerID'] = currentConsumerID
            dr["f_AttDate"] = indexDate
            dr["f_LateTime"] = 0
            dr["f_LeaveEarlyTime"] = 0
            dr["f_OvertimeTime"] = 0
            dr["f_AbsenceDay"] = 0
            bdayShouldWork = True
            bdayRealWork = True
            # 							//有Time和user选择
            # 							//dvCardRecord.RowFilter = " f_ConsumerID = " & dtConsumer.Rows(indexConsumer)("f_ConsumerID") & " and f_ReadDate >= #" & indexDate & "# and f_ReadDate<= " & Format(indexDate, "#yyyy-MM-dd 23:59:59#")
            #                             dvCardRecord.RowFilter = " f_ReadDate >= #" + indexDate.ToString("yyyy-MM-dd HH:mm:ss") + "# and f_ReadDate<= " + Strings.Format(indexDate.AddDays(normalDay), "#yyyy-MM-dd " + strAllowOffdutyTime + "#"); //2010-7-22 13:33:20
            # 							//请假
            #                             dvLeave.RowFilter = " f_ConsumerID = " + currentConsumerID.ToString();
            #
            # 							if (dvCardRecord.Count > 0)
            # // 存在刷卡记录时
            # // 先查上午上班Time
            # for (indexRecord = 0; indexRecord <= dvCardRecord.Count - 1; indexRecord++)
            currentday =  vwAppConfig.strtotime(f"{startDateTime} 23:59:59")
            index_record = 0
            if index_recordStart001>0:
                index_record = index_recordStart001 # 新的值
                index_recordStart001 = -1 #重新找值
            dv_card_record = objt_d_SwipeRecord
            record_count = len(dv_card_record)
            index_date = indexDate

            filtered_results = []

            while index_record < record_count:
                if currentday >= objt_d_SwipeRecord[index_record].f_ReadDate:  #没有超过当天
                    # 2025-09-26 13:02:47 有效的Time值
                    if objt_d_SwipeRecord[index_record].f_ReadDate >= indexDate: # 有效
                        pass
                        filtered_results.append(objt_d_SwipeRecord[index_record])
                        if index_recordStart001 == -1:
                            index_recordStart001 = index_record  # 重新找到的值 首Unit
                    else:
                        continue  # 下一条记录
                else:
                    break
                pass
            dv_card_record = filtered_results  # 过滤的数据         dvCardRecord.RowFilter = " f_ReadDate >= #" + indexDate.ToString("yyyy-MM-dd HH:mm:ss") + "# and f_ReadDate<= " + Strings.Format(indexDate.AddDays(normalDay), "#yyyy-MM-dd " + strAllowOffdutyTime + "#"); //2010-7-22 13:33:20
            record_count = len(dv_card_record)
            index_record = 0
            if record_count > 0:
                #//存在刷卡记录时
				#//先查上午上班Time
                while index_record < record_count:
                    # if currentday >= objt_d_SwipeRecord[index_record].f_ReadDate:  #没有超过当天
                    #     # 2025-09-26 13:02:47 有效的Time值
                    #     if objt_d_SwipeRecord[index_record].f_ReadDate >= indexDate: # 有效
                    #         pass
                    #     else:
                    #         continue  # 下一条记录
                    # else:
                    #     break
                    # pass
                    # 我们有一UnitC#代码段，需要转换为Python。这段代码处理刷卡记录，判断上午上班Time的情况。
                    # 主要逻辑：
                    # 遍历刷卡记录（dvCardRecord），对于每条记录：
                    # 判断刷卡日期是否与当前处理日期（indexDate）相同。
                    # 如果相同，再判断刷卡Time是否在上午上班Time（tOnduty1）之前。
                    # 2.1 如果是，并且是上班卡（f_DutyOnOff不为2，即不是下班卡），则设置上午上班Time。
                    # 2.2 如果已经设置过上班Time，并且设置了bEarliestAsOnDuty（最早刷卡作为上班Time），则不再更新。
                    # 2.3 如果刷卡记录总数是4条，则处理完第一条后，跳到下一条记录（即indexRecord++）并退出循环，Enter上午下班处理。
                    # 如果刷卡Time在上午上班Time之后，则判断是否已经设置过上班Time，如果设置过则退出循环。
                    # 3.1 如果没有设置过，且是下班卡，则标记为未刷卡并退出。
                    # 3.2 否则，判断是否在允许的迟到Time内（tLateTimeout），如果是则不算迟到，记录上班Time。
                    # 3.3 如果超过允许的迟到Time但未达到旷工Time（tlateAbsenceTimeout），则标记为迟到。
                    # 3.4 如果超过旷工Time，则判断刷卡Time是否在上午下班Time（tOffduty1）减去允许的早退Time（tLeaveTimeout）之后，如果是则标记为未刷卡，否则标记为旷工。
                    # 如果刷卡日期与当前处理日期不同（即第二天凌晨），则如果已经设置过上班Time就退出，否则标记为未刷卡并退出。
                    # """
                    #     处理上午上班Time的刷卡记录
                    #
                    #     Args:
                    #         dv_card_record: 刷卡记录列表
                    #         index_date: 当前处理日期
                    #         dr: 考勤结果字典
                    #         t_onduty1: 上午上班Time
                    #         t_offduty1: 上午下班Time
                    #         t_late_timeout: 迟到容忍Time（分钟）
                    #         t_late_absence_timeout: 旷工判定Time（分钟）
                    #         t_leave_timeout: 早退容忍Time（分钟）
                    #         b_earliest_as_on_duty: 是否以最早刷卡为准
                    #         str_not_read_card: 未刷卡描述
                    #         str_lateness: 迟到描述
                    #         str_absence: 旷工描述
                    #
                    #     Returns:
                    #         int: 处理到的记录索引
                    #     """

                    record = dv_card_record[index_record]
                    read_date = record.f_ReadDate  # 假设已经是datetime类型

                    # 检查是否为同一天
                    if read_date.date() == index_date.date(): # 正常上班Time
                        read_time = read_date.time()
                        onduty_time = t_onduty1.time()

                        if read_time <= onduty_time: #没有跨夜
                            # 上班卡（非下班卡）
                            if record.get("f_DutyOnOff") == 2:  # 下班卡
                                index_record += 1
                                continue  # 跳过下班卡，继续处理下一条

                            # 处理上班卡
                            if b_earliest_as_on_duty:
                                if not dr.get("f_Onduty1"):
                                    # 如果还没有上班Time，以最早的为准
                                    dr["f_Onduty1"] = read_date
                                    dr["f_Onduty1Desc"] = record.get("f_Type", "")
                            else:
                                dr["f_Onduty1"] = read_date
                                dr["f_Onduty1Desc"] = record.get("f_Type", "")

                            # 如果是4次刷卡模式，处理完第一条后Enter上午下班处理
                            if record_count == 4:
                                index_record += 1
                                break

                        else:
                            # 超过正常上班Time
                            if dr.get("f_Onduty1"):
                                # 已经有上班刷卡记录，Enter上午下班处理
                                break

                            # 检查是否为下班卡
                            if record.get("f_DutyOnOff") == 2:
                                dr["f_Onduty1Desc"] = str_not_read_card
                                break

                            # 检查是否在迟到容忍Time内
                            late_threshold = t_onduty1 + timedelta(minutes=t_late_timeout)
                            if read_time <= late_threshold.time():
                                dr["f_Onduty1"] = read_date
                                dr["f_Onduty1Desc"] = record.get("f_Type", "")
                                index_record += 1
                                break
                            else:
                                # 检查是否算迟到而非旷工
                                absence_threshold = t_onduty1 + timedelta(minutes=t_late_absence_timeout)
                                if read_time < absence_threshold.time():
                                    dr["f_Onduty1"] = read_date
                                    dr["f_Onduty1Desc"] = str_lateness
                                    index_record += 1
                                    break
                                else:
                                    # 检查是否算未打卡
                                    leave_threshold = t_offduty1 - timedelta(minutes=t_leave_timeout)
                                    if read_time > leave_threshold.time():
                                        dr["f_Onduty1Desc"] = str_not_read_card
                                    else:
                                        dr["f_Onduty1"] = read_date
                                        dr["f_Onduty1Desc"] = str_absence

                            # 如果是4次刷卡模式
                            if record_count == 4:
                                index_record += 1
                                break

                            break
                    else:
                        # 已经是第二天凌晨
                        # 过正常上班Time
                        if dr.get("f_Onduty1"):
                            # 已经有上班刷卡记录
                            break

                        dr["f_Onduty1Desc"] = str_not_read_card
                        break

                    index_record += 1

                index_record_start = index_record
                #上午上班尾

                #//Enter上午下班
                #//2004-12-14这部分的循环indexRecord由indexRecord2替换
                # 初始化变量
                index_record = index_record_start
                index_record_on_duty = 0  # 可能是上班的记录索引
                offduty_time_str = ""  # 下班Time字符串

                # 循环处理刷卡记录
                while index_record < record_count: #len(dv_card_record):
                    record = dv_card_record[index_record]
                    read_date = record.get("f_ReadDate")

                    if not read_date:
                        index_record += 1
                        continue

                    # 检查是否为同一天
                    if read_date.date() == index_date.date():
                        read_time = read_date.time()
                        offduty_time = t_offduty1.time()
                        onduty2_time = t_onduty2.time()

                        # 正常下班Time前刷卡或4次刷卡模式
                        if read_time <= offduty_time or len(dv_card_record) == 4:
                            # 跳过上班卡
                            if record.get("f_DutyOnOff") == 1:  # 上班卡
                                if read_time <= offduty_time:
                                    index_record += 1
                                    continue  # 跳过上班卡，继续处理下一条

                            # 处理下班卡
                            dr["f_Offduty1"] = read_date
                            dr["f_Offduty1Desc"] = record.get("f_Type", "")

                            # 检查是否早退或旷工
                            leave_threshold = t_offduty1 - timedelta(minutes=t_leave_timeout)
                            absence_threshold = t_offduty1 - timedelta(minutes=t_leave_absence_timeout)

                            if read_time < leave_threshold.time():
                                if read_time < absence_threshold.time():
                                    dr["f_Offduty1Desc"] = str_absence
                                else:
                                    dr["f_Offduty1Desc"] = str_leave_early

                            # 如果是4次刷卡模式，处理完第一条后Enter下午上班处理
                            if len(dv_card_record) == 4:
                                index_record += 1
                                break # return index_record

                        else:
                            # 超过正常下班Time
                            # 跳过上班卡
                            if record.get("f_DutyOnOff") == 1:  # 上班卡
                                if read_time >= onduty2_time:
                                    if not dr.get("f_Offduty1"):
                                        dr["f_Offduty1Desc"] = str_not_read_card
                                    break #return index_record
                                else:
                                    if index_record_on_duty == 0:
                                        index_record_on_duty = index_record
                                        offduty_time_str = str(dr.get("f_Offduty1", ""))
                                    index_record += 1
                                    continue

                            # 如果还没有下班记录
                            if not dr.get("f_Offduty1"):
                                if read_time >= onduty2_time:
                                    dr["f_Offduty1Desc"] = str_not_read_card
                                    break # return index_record
                                else:
                                    # 处于上午下班和下午上班之间
                                    if index_record + 1 < len(dv_card_record):
                                        next_record = dv_card_record[index_record + 1]
                                        next_read_date = next_record.get("f_ReadDate")

                                        if next_read_date:
                                            late_threshold = t_onduty2 + timedelta(minutes=t_late_timeout)

                                            if next_read_date.time() <= late_threshold.time():
                                                # 存在可用于下午上班的记录
                                                dr["f_Offduty1"] = read_date
                                                dr["f_Offduty1Desc"] = record.get("f_Type", "")
                                                index_record += 1
                                                break # return index_record

                                    # 如果没有上午上班记录
                                    if not dr.get("f_Onduty1"):
                                        dr["f_Offduty1Desc"] = str_not_read_card
                                    else:
                                        # 有上午上班记录，将此Time作为上午下班
                                        dr["f_Offduty1"] = read_date
                                        dr["f_Offduty1Desc"] = record.get("f_Type", "")

                            else:
                                # 已经有下班记录，检查下一Unit记录是否可用于下午上班
                                if index_record + 1 < len(dv_card_record):
                                    next_record = dv_card_record[index_record + 1]
                                    next_read_date = next_record.get("f_ReadDate")

                                    if next_read_date:
                                        late_threshold = t_onduty2 + timedelta(minutes=t_late_timeout)

                                        if next_read_date.time() <= late_threshold.time():
                                            # 检查当前记录是否为早退或旷工
                                            offduty_desc = dr.get("f_Offduty1Desc", "")

                                            if str_leave_early in offduty_desc:
                                                # 如果是早退，用当前记录更新
                                                dr["f_Offduty1"] = read_date
                                                dr["f_Offduty1Desc"] = record.get("f_Type", "")
                                            elif str_absence in offduty_desc and dr.get("f_Offduty1"):
                                                # 如果是旷工，用当前记录更新
                                                dr["f_Offduty1"] = read_date
                                                dr["f_Offduty1Desc"] = record.get("f_Type", "")

                            # 四次刷卡模式处理
                            if len(dv_card_record) == 4:
                                index_record += 1
                                break # return index_record

                            break # return index_record

                    else:
                        # 第二天凌晨的记录
                        if not dr.get("f_Offduty1"):
                            dr["f_Offduty1Desc"] = str_not_read_card
                        break # return index_record

                    index_record += 1
                #上午下班结尾

                # 上午只有一次刷卡, 只下班作未刷卡处理
                if (set_obj_to_str(dr.get("f_Offduty1")) == set_obj_to_str(dr.get("f_Onduty1")) and
                        (str_contains(set_obj_to_str(dr.get("f_Offduty1Desc")), str_leave_early) or
                         str_contains(set_obj_to_str(dr.get("f_Offduty1Desc")), str_absence))):
                    dr["f_Offduty1"] = None
                    dr["f_Offduty1Desc"] = str_not_read_card

                # 没有任何刷卡或签到, 则作为旷工处理
                if (not set_obj_to_str(dr.get("f_Offduty1")) and
                        not set_obj_to_str(dr.get("f_Onduty1"))):
                    dr["f_Onduty1Desc"] = str_absence
                    dr["f_Offduty1Desc"] = str_absence

                # 下班没有任何信息时
                if (not set_obj_to_str(dr.get("f_Offduty1")) and
                        not set_obj_to_str(dr.get("f_Offduty1Desc"))):
                    dr["f_Offduty1Desc"] = str_not_read_card

                # 如果有未刷卡则不能有旷工存在
                if (str_contains(set_obj_to_str(dr.get("f_OnDuty1Desc")), str_absence) and
                        str_contains(set_obj_to_str(dr.get("f_OffDuty1Desc")), str_not_read_card)):
                    dr["f_OnDuty1Desc"] = ""  # 不作旷工处理

                #下午上班
                #//查下午上班Time
                #处理下午上班Time的刷卡记录
                # 调整开始索引
                index_record2 = index_record #index_record_start
                if index_record_on_duty > 0:
                    current_offduty_str = set_obj_to_str(dr.get("f_Offduty1", ""))
                    if offduty_time_str == current_offduty_str:
                        index_record2 = index_record_on_duty  # 恢复到中午上班记录索引
                    index_record_on_duty = 0

                index_record = index_record2
                record_count = len(dv_card_record)

                # 循环处理刷卡记录
                while index_record < record_count:
                    record = dv_card_record[index_record]
                    read_date = record.get("f_ReadDate")

                    if not read_date:
                        index_record += 1
                        continue

                    # 检查是否为同一天
                    if read_date.date() == index_date.date():
                        read_time = read_date.time()
                        onduty2_time = t_onduty2.time()

                        # 正常上班Time前刷卡
                        if read_time <= onduty2_time:
                            # 跳过下班卡
                            if record.get("f_DutyOnOff") == 2:  # 下班卡
                                index_record += 1
                                continue  # 跳过下班卡，继续处理下一条

                            # 处理上班卡
                            dr["f_Onduty2"] = read_date
                            dr["f_Onduty2Desc"] = record.get("f_Type", "")

                            # 如果是4次刷卡模式，处理完第一条后Enter下午下班处理
                            if record_count == 4:
                                index_record += 1
                                break #return index_record

                        else:
                            # 超过正常上班Time
                            if dr.get("f_Onduty2"):
                                # 已经有上班刷卡记录
                                current_onduty2 = set_obj_to_str(dr.get("f_Onduty2", ""))
                                current_offduty1 = set_obj_to_str(dr.get("f_Offduty1", ""))

                                if current_onduty2 == current_offduty1:
                                    # 下午上班Time与上午下班Time相同，清空下午上班记录
                                    dr["f_Onduty2"] = None
                                    dr["f_Onduty2Desc"] = ""
                                else:
                                    # 已经有上班记录，Enter下午下班处理
                                    break

                            # 跳过下班卡
                            if record.get("f_DutyOnOff") == 2:
                                dr["f_Onduty2Desc"] = str_not_read_card
                                break

                            # 检查是否在迟到容忍Time内
                            late_threshold = t_onduty2 + timedelta(minutes=t_late_timeout)
                            if read_time <= late_threshold.time():
                                dr["f_Onduty2"] = read_date
                                dr["f_Onduty2Desc"] = record.get("f_Type", "")
                            else:
                                # 检查是否算迟到而非旷工
                                absence_threshold = t_onduty2 + timedelta(minutes=t_late_absence_timeout)
                                if read_time < absence_threshold.time():
                                    dr["f_Onduty2"] = read_date
                                    dr["f_Onduty2Desc"] = str_lateness
                                else:
                                    # 检查是否算未打卡或旷工
                                    leave_threshold = t_offduty2 - timedelta(minutes=t_leave_timeout)
                                    if read_time > leave_threshold.time():
                                        dr["f_Onduty2Desc"] = str_not_read_card
                                    else:
                                        dr["f_Onduty2"] = read_date
                                        dr["f_Onduty2Desc"] = str_absence

                            # 如果是4次刷卡模式
                            if record_count == 4:
                                index_record += 1
                                break #return index_record

                            break

                    else:
                        # 第二天凌晨的记录
                        # Time超过正常下班Time()且未有过记录, 算未刷卡
                        if not dr.get("f_Onduty2") and not dr.get("f_Onduty2Desc"):
                            dr["f_Onduty2Desc"] = str_not_read_card
                        break

                    index_record += 1
                # 下午上班尾

                # //Enter下午下班
                # index_record = index_record_start
                # record_count = len(dv_card_record)

                # 循环处理刷卡记录
                while index_record < record_count:
                    record = dv_card_record[index_record]

                    # 跳过上班卡
                    if record.get("f_DutyOnOff") == 1:  # 上班卡
                        index_record += 1
                        continue  # 跳过上班卡，继续处理下一条

                    read_date = record.get("f_ReadDate")

                    if not read_date:
                        index_record += 1
                        continue

                    # 检查是否为同一天
                    if read_date.date() == index_date.date():
                        read_time = read_date.time()
                        offduty2_time = t_offduty2.time()

                        # 正常下班Time前刷卡
                        if read_time <= offduty2_time:
                            # 处理下班卡
                            dr["f_Offduty2"] = read_date
                            dr["f_Offduty2Desc"] = record.get("f_Type", "")

                            # 检查是否早退或旷工
                            leave_threshold = t_offduty2 - timedelta(minutes=t_leave_timeout)
                            absence_threshold = t_offduty2 - timedelta(minutes=t_leave_absence_timeout)

                            if read_time < leave_threshold.time():
                                if read_time < absence_threshold.time():
                                    dr["f_Offduty2Desc"] = str_absence
                                else:
                                    dr["f_Offduty2Desc"] = str_leave_early

                        else:
                            # 超过正常下班Time
                            dr["f_Offduty2"] = read_date
                            dr["f_Offduty2Desc"] = record.get("f_Type", "")

                    else:
                        # 第二天凌晨的记录
                        dr["f_Offduty2"] = read_date
                        dr["f_Offduty2Desc"] = record.get("f_Type", "")

                    index_record += 1
                # 下午下班尾

                # 解决中午同一Time问题
                offduty1_str = set_obj_to_str(dr.get("f_Offduty1", ""))
                onduty2_str = set_obj_to_str(dr.get("f_Onduty2", ""))

                if offduty1_str == onduty2_str and offduty1_str != "":
                    # 下午上班Time与上午下班Time一样，则下午上班Time为空
                    dr["f_Onduty2"] = None
                    dr["f_Onduty2Desc"] = ""

                # 下午只有一次刷卡, 只下班作未刷卡处理
                offduty2_str = set_obj_to_str(dr.get("f_Offduty2", ""))
                onduty2_str = set_obj_to_str(dr.get("f_Onduty2", ""))
                offduty2_desc = set_obj_to_str(dr.get("f_Offduty2Desc", ""))

                if (offduty2_str == onduty2_str and
                        (str_contains(offduty2_desc, str_leave_early) or
                         str_contains(offduty2_desc, str_absence))):
                    dr["f_Offduty2"] = None
                    dr["f_Offduty2Desc"] = str_not_read_card

                # 下午没有任何刷卡或签到, 则作为旷工处理
                if (not set_obj_to_str(dr.get("f_Offduty2")) and
                        not set_obj_to_str(dr.get("f_Onduty2"))):
                    dr["f_Onduty2Desc"] = str_absence
                    dr["f_Offduty2Desc"] = str_absence

                # 下班没有任何信息时
                if (not set_obj_to_str(dr.get("f_Offduty2")) and
                        not set_obj_to_str(dr.get("f_Offduty2Desc"))):
                    dr["f_Offduty2Desc"] = str_not_read_card

                # 如果有未刷卡则不能有旷工存在
                onduty2_desc = set_obj_to_str(dr.get("f_OnDuty2Desc", ""))
                offduty2_desc = set_obj_to_str(dr.get("f_OffDuty2Desc", ""))

                if (str_contains(onduty2_desc, str_absence) and
                        str_contains(offduty2_desc, str_not_read_card)):
                    dr["f_OnDuty2Desc"] = ""  # 不作旷工处理


            else:  # if record_count > 0
                # // 无刷卡记录时, 全作旷工处理
                dr["f_OnDuty1Desc"] = str_absence
                dr["f_OffDuty1Desc"] = str_absence
                dr["f_OnDuty2Desc"] = str_absence
                dr["f_OffDuty2Desc"] = str_absence

            # //刷卡记录填写Finished后, Enter节假日处理部分
            # 							int dayOff; //如果=0, 表示全天休息, =1表示上午半天班, =2表示下午上半天班, =3表示上正常班
            # 							dayOff = 3;
            dayOff = 3
            # 检查是否全天休假或是上半天班
            # 定义常量
            REST_ONE_DAY = 0  # 全天休息
            REST_PM = 1  # 上午休息，下午上班
            REST_AM = 2  # 上午上班，下午休息
            WORK_ONE_DAY = 3  # 正常上班
            WORK_AM = 4  # 上午上班
            WORK_PM = 5  # 下午上班
            str_am = "上午"
            str_pm = "下午"

            day_off = WORK_ONE_DAY  # 默认正常上班
            holiday_data = objt_a_Holiday #: 节假日数据列表
            # 检查周六周日
            saturday_data = [d for d in holiday_data if d.get("f_NO") == 1]
            sunday_data = [d for d in holiday_data if d.get("f_NO") == 2]
            special_holiday_data = [d for d in holiday_data if d.get("f_TYPE") == 2]

            # 检查周六
            if index_date.weekday() == 5:  # 周六
                if saturday_data:
                    sat_value = saturday_data[0].get("f_Value", 3)
                    if sat_value == 0:
                        day_off = REST_ONE_DAY
                    elif sat_value == 1:
                        day_off = REST_PM
                    elif sat_value == 2:
                        day_off = REST_AM

            # 检查周日
            elif index_date.weekday() == 6:  # 周日
                if sunday_data:
                    sun_value = sunday_data[0].get("f_Value", 3)
                    if sun_value == REST_ONE_DAY:
                        day_off = REST_ONE_DAY
                    elif sun_value == WORK_AM:
                        day_off = REST_PM
                    elif sun_value == WORK_PM:
                        day_off = REST_AM

            # 检查特殊节假日
            for holiday in special_holiday_data:
                # 处理开始Time
                start_date_str = holiday.get("f_Value", "")
                start_date = parse_date_string(start_date_str, index_date.year)

                # 处理结束Time
                end_date_str = holiday.get("f_Value2", "")
                end_date = parse_date_string(end_date_str, index_date.year)

                if not start_date or not end_date:
                    continue

                # 处理Time段
                start_period = holiday.get("f_Value1", "")
                end_period = holiday.get("f_Value3", "")

                # 设置开始和结束Time
                start_time = set_period_time(start_date, start_period, str_am, str_pm)
                end_time = set_period_time(end_date, end_period, str_am, str_pm)

                if not start_time or not end_time:
                    continue

                # 检查是否在节假日范围内
                day_start = index_date.replace(hour=0, minute=0, second=0)
                day_end = index_date.replace(hour=23, minute=59, second=59)

                # 全天休息
                if start_time <= day_start and day_end <= end_time:
                    day_off = REST_ONE_DAY
                    break

                # 上午休息，下午上班
                if start_time <= day_start and day_start.replace(hour=12, minute=0, second=0) <= end_time:
                    day_off = REST_AM

                # 下午休息，上午上班
                if start_time <= day_start.replace(hour=12, minute=0, second=1) and day_end <= end_time:
                    day_off = REST_PM
            pass


            # 定义常量
            REST_ONE_DAY = 0  # 全天休息
            REST_PM = 1  # 上午休息，下午上班
            REST_AM = 2  # 上午上班，下午休息
            WORK_ONE_DAY = 3  # 正常上班
            WORK_AM = 4  # 上午上班
            WORK_PM = 5  # 下午上班



            bday_should_work = True
            bday_real_work = True

            # 如果不是全天上班，检查强制上班
            if day_off != WORK_ONE_DAY:
                must_work_data = [d for d in holiday_data if d.get("f_TYPE") == 3]

                for must_work in must_work_data:
                    # 处理开始Time
                    start_date_str = must_work.get("f_Value", "")
                    start_date = parse_date_string(start_date_str, index_date.year)

                    # 处理结束Time
                    end_date_str = must_work.get("f_Value2", "")
                    end_date = parse_date_string(end_date_str, index_date.year)

                    if not start_date or not end_date:
                        continue

                    # 处理Time段
                    start_period = must_work.get("f_Value1", "")
                    end_period = must_work.get("f_Value3", "")

                    # 设置开始和结束Time
                    start_time = set_period_time(start_date, start_period, str_am, str_pm)
                    end_time = set_period_time(end_date, end_period, str_am, str_pm)

                    if not start_time or not end_time:
                        continue

                    # 检查是否在强制上班范围内
                    day_start = index_date.replace(hour=0, minute=0, second=0)
                    day_end = index_date.replace(hour=23, minute=59, second=59)

                    # 全天上班
                    if start_time <= day_start and day_end <= end_time:
                        day_off = WORK_ONE_DAY
                        break

                    # 上午上班
                    if start_time <= day_start and day_start.replace(hour=12, minute=0, second=0) <= end_time:
                        if day_off == REST_AM:  # 如果原来是下午上半天, 上午休息
                            day_off = WORK_ONE_DAY  # 全天上班
                        else:
                            day_off = REST_PM  # 上午上半天

                    # 下午上班
                    if start_time <= day_start.replace(hour=12, minute=0, second=1) and day_end <= end_time:
                        if day_off == REST_PM:  # 如果原来是上午上半天, 下午休息
                            day_off = WORK_ONE_DAY  # 全天上班
                        else:
                            day_off = REST_AM  # 下午上半天

            # 处理节假日
            attendance_result = dr
            if day_off == REST_ONE_DAY:  # 全休假
                # 将所有记录标记为休息
                for key in ["f_OnDuty1Desc", "f_OffDuty1Desc", "f_OnDuty2Desc", "f_OffDuty2Desc"]:
                    if key in attendance_result:
                        attendance_result[key] = str_rest

                # 处理加班
                overtime_hours = 0
                if (attendance_result.get("f_Onduty1") and attendance_result.get("f_Offduty1")):
                    # 上午加班
                    onduty1 = attendance_result["f_Onduty1"]
                    offduty1 = attendance_result["f_Offduty1"]

                    if isinstance(onduty1, str):
                        onduty1 = datetime.strptime(onduty1, "%Y-%m-%d %H:%M:%S")
                    if isinstance(offduty1, str):
                        offduty1 = datetime.strptime(offduty1, "%Y-%m-%d %H:%M:%S")

                    # 计算加班Time（以0.5小时为刻度）
                    time_diff = (offduty1 - onduty1).total_seconds() / 60  # 分钟数
                    overtime_hours += math.floor(time_diff / 30) / 2  # 每30分钟为0.5小时

                    attendance_result["f_OnDuty1Desc"] = str_overtime
                    attendance_result["f_OffDuty1Desc"] = str_overtime

                if (attendance_result.get("f_Onduty2") and attendance_result.get("f_Offduty2")):
                    # 下午加班
                    onduty2 = attendance_result["f_Onduty2"]
                    offduty2 = attendance_result["f_Offduty2"]

                    if isinstance(onduty2, str):
                        onduty2 = datetime.strptime(onduty2, "%Y-%m-%d %H:%M:%S")
                    if isinstance(offduty2, str):
                        offduty2 = datetime.strptime(offduty2, "%Y-%m-%d %H:%M:%S")

                    # 计算加班Time（以0.5小时为刻度）
                    time_diff = (offduty2 - onduty2).total_seconds() / 60  # 分钟数
                    overtime_hours += math.floor(time_diff / 30) / 2  # 每30分钟为0.5小时

                    attendance_result["f_OnDuty2Desc"] = str_overtime
                    attendance_result["f_OffDuty2Desc"] = str_overtime

                # 设置加班Time
                if overtime_hours > 0:
                    attendance_result["f_OvertimeTime"] = overtime_hours

                # 不用上班
                bday_should_work = False
                bday_real_work = False

            elif day_off == REST_PM:  # 上午休息，下午上班
                # 下午记录标记为休息
                if "f_OnDuty2Desc" in attendance_result:
                    attendance_result["f_OnDuty2Desc"] = str_rest
                if "f_OffDuty2Desc" in attendance_result:
                    attendance_result["f_OffDuty2Desc"] = str_rest

                # 处理上午下班记录
                if (attendance_result.get("f_OffDuty1Desc") in [str_not_read_card, str_leave_early, str_absence] and
                        attendance_result.get("f_OffDuty2")):
                    # 用下午下班记录更新上午下班记录
                    attendance_result["f_OffDuty1"] = attendance_result["f_OffDuty2"]
                    attendance_result["f_OffDuty1Desc"] = ""

                    # 清空下午记录
                    attendance_result["f_OffDuty2"] = None
                    attendance_result["f_OnDuty2"] = None

                    # 重新判断是否早退或旷工
                    if attendance_result.get("f_OffDuty1"):
                        offduty1 = attendance_result["f_OffDuty1"]
                        if isinstance(offduty1, str):
                            offduty1 = datetime.strptime(offduty1, "%Y-%m-%d %H:%M:%S")

                        leave_threshold = t_offduty1 - timedelta(minutes=t_leave_timeout)
                        absence_threshold = t_offduty1 - timedelta(minutes=t_leave_absence_timeout)

                        if offduty1 < leave_threshold:
                            if offduty1 < absence_threshold:
                                attendance_result["f_OffDuty1Desc"] = str_absence
                            else:
                                attendance_result["f_OffDuty1Desc"] = str_leave_early

                # 处理下午加班
                if (attendance_result.get("f_Onduty2") and attendance_result.get("f_Offduty2")):
                    onduty2 = attendance_result["f_Onduty2"]
                    offduty2 = attendance_result["f_Offduty2"]

                    if isinstance(onduty2, str):
                        onduty2 = datetime.strptime(onduty2, "%Y-%m-%d %H:%M:%S")
                    if isinstance(offduty2, str):
                        offduty2 = datetime.strptime(offduty2, "%Y-%m-%d %H:%M:%S")

                    # 计算加班Time（以0.5小时为刻度）
                    time_diff = (offduty2 - onduty2).total_seconds() / 60  # 分钟数
                    overtime_hours = math.floor(time_diff / 30) / 2  # 每30分钟为0.5小时

                    if overtime_hours > 0:
                        attendance_result["f_OvertimeTime"] = overtime_hours
                        attendance_result["f_OnDuty2Desc"] = str_overtime
                        attendance_result["f_OffDuty2Desc"] = str_overtime

            elif day_off == REST_AM:  # 上午上班，下午休息
                # 上午记录标记为休息
                if "f_OnDuty1Desc" in attendance_result:
                    attendance_result["f_OnDuty1Desc"] = str_rest
                if "f_OffDuty1Desc" in attendance_result:
                    attendance_result["f_OffDuty1Desc"] = str_rest

                # 处理下午上班记录
                if (attendance_result.get("f_OnDuty2Desc") == str_absence and
                        attendance_result.get("f_OnDuty1")):
                    # 用上午上班记录更新下午上班记录
                    attendance_result["f_OnDuty2"] = attendance_result["f_OnDuty1"]
                    attendance_result["f_OnDuty1"] = None
                    attendance_result["f_OffDuty1"] = None

                # 处理上午加班
                if (attendance_result.get("f_Onduty1") and attendance_result.get("f_Offduty1")):
                    onduty1 = attendance_result["f_Onduty1"]
                    offduty1 = attendance_result["f_Offduty1"]

                    if isinstance(onduty1, str):
                        onduty1 = datetime.strptime(onduty1, "%Y-%m-%d %H:%M:%S")
                    if isinstance(offduty1, str):
                        offduty1 = datetime.strptime(offduty1, "%Y-%m-%d %H:%M:%S")

                    # 计算加班Time（以0.5小时为刻度）
                    time_diff = (offduty1 - onduty1).total_seconds() / 60  # 分钟数
                    overtime_hours = math.floor(time_diff / 30) / 2  # 每30分钟为0.5小时

                    if overtime_hours > 0:
                        attendance_result["f_OvertimeTime"] = overtime_hours
                        attendance_result["f_OnDuty1Desc"] = str_overtime
                        attendance_result["f_OffDuty1Desc"] = str_overtime

            elif day_off == WORK_ONE_DAY:  # 正常上班
                # 处理下午加班
                if (attendance_result.get("f_Onduty2") and attendance_result.get("f_Offduty2")):
                    onduty2 = attendance_result["f_Onduty2"]
                    offduty2 = attendance_result["f_Offduty2"]

                    if isinstance(onduty2, str):
                        onduty2 = datetime.strptime(onduty2, "%Y-%m-%d %H:%M:%S")
                    if isinstance(offduty2, str):
                        offduty2 = datetime.strptime(offduty2, "%Y-%m-%d %H:%M:%S")

                    # 检查是否加班
                    overtime_threshold = t_offduty2 + timedelta(minutes=t_overtime_timeout)

                    if offduty2 > overtime_threshold:
                        # 计算加班Time（以0.5小时为刻度）
                        time_diff = (offduty2 - t_offduty2).total_seconds() / 60  # 分钟数
                        overtime_hours = math.floor(time_diff / 30) / 2  # 每30分钟为0.5小时

                        if overtime_hours > 0:
                            attendance_result["f_OvertimeTime"] = overtime_hours
                            attendance_result["f_OffDuty2Desc"] = str_overtime

            # return day_off, bday_should_work, bday_real_work
            dv_leave = objt_d_Leave
            #//请假或出差处理
            if dv_leave:
                day_off = 3  # 默认正常上班 (3=正常, 0=全天假, 1=下午假, 2=上午假)
                holiday_type1 = ""  # 上午请假类型
                holiday_type2 = ""  # 下午请假类型

                # 处理每条请假记录
                for leave_record in dv_leave:
                    # 解析开始Time和结束Time
                    start_time, end_time, holiday_type = _parse_leave_times(leave_record, index_date)

                    if not start_time or not end_time:
                        continue

                    # 检查休假类型
                    day_off, holiday_type1, holiday_type2 = _check_leave_type(
                        start_time, end_time, index_date, day_off, holiday_type, holiday_type1, holiday_type2
                     )

                    # 如果确定全天休假，提前结束循环
                    if day_off == 0:
                        break

                # 根据休假类型更新考勤记录
                _update_attendance_record(dr, day_off, holiday_type1, holiday_type2,
                                                      t_leave_timeout, t_leave_absence_timeout,
                                                      t_late_timeout, t_late_absence_timeout)

            result = dr
            # 计算迟到Time
            if set_obj_to_str(result.get("f_OnDuty1Desc")) == str_lateness:
                late_minutes = calculate_time_difference_minutes(
                    result.get("f_OnDuty1"), t_onduty1
                )
                result["f_LateTime"] = result.get("f_LateTime", 0) + late_minutes

            if set_obj_to_str(result.get("f_OnDuty2Desc")) == str_lateness:
                late_minutes = calculate_time_difference_minutes(
                    result.get("f_OnDuty2"), t_onduty2
                )
                result["f_LateTime"] = result.get("f_LateTime", 0) + late_minutes

            # 计算早退Time
            if set_obj_to_str(result.get("f_OffDuty1Desc")) == str_leave_early:
                leave_early_minutes = calculate_time_difference_minutes(
                    t_offduty1, result.get("f_OffDuty1")
                )
                result["f_LeaveEarlyTime"] = result.get("f_LeaveEarlyTime", 0) + leave_early_minutes

            if set_obj_to_str(result.get("f_OffDuty2Desc")) == str_leave_early:
                leave_early_minutes = calculate_time_difference_minutes(
                    t_offduty2, result.get("f_OffDuty2")
                )
                result["f_LeaveEarlyTime"] = result.get("f_LeaveEarlyTime", 0) + leave_early_minutes

            # 计算旷工
            onduty1_desc = set_obj_to_str(result.get("f_OnDuty1Desc", ""))
            offduty1_desc = set_obj_to_str(result.get("f_OffDuty1Desc", ""))

            if str_absence in onduty1_desc and str_absence in offduty1_desc:
                result["f_AbsenceDay"] = result.get("f_AbsenceDay", decimal.Decimal(0)) +wgform.t_a_Attendence_LateAbsenceDay # t_late_absence_day
                bday_real_work = False
            else:
                if str_absence in onduty1_desc:
                    result["f_AbsenceDay"] = result.get("f_AbsenceDay", decimal.Decimal(0)) +wgform.t_a_Attendence_LateAbsenceDay #  t_late_absence_day
                    bday_real_work = False

                if str_absence in offduty1_desc:
                    result["f_AbsenceDay"] = result.get("f_AbsenceDay", decimal.Decimal(0)) + wgform.t_a_Attendence_LeaveAbsenceDay # t_leave_absence_day
                    bday_real_work = False

            # 下午旷工计算
            onduty2_desc = set_obj_to_str(result.get("f_OnDuty2Desc", ""))
            offduty2_desc = set_obj_to_str(result.get("f_OffDuty2Desc", ""))

            if str_absence in onduty2_desc and str_absence in offduty2_desc:
                result["f_AbsenceDay"] = result.get("f_AbsenceDay", decimal.Decimal(0)) + wgform.t_a_Attendence_LateAbsenceDay # t_late_absence_day
                bday_real_work = False
            else:
                if str_absence in onduty2_desc:
                    result["f_AbsenceDay"] = result.get("f_AbsenceDay", decimal.Decimal(0)) + wgform.t_a_Attendence_LateAbsenceDay # t_late_absence_day
                    bday_real_work = False

                if str_absence in offduty2_desc:
                    result["f_AbsenceDay"] = result.get("f_AbsenceDay", decimal.Decimal(0)) + wgform.t_a_Attendence_LeaveAbsenceDay # t_leave_absence_day
                    bday_real_work = False

            # 旷工天数调整
            absence_day = result.get("f_AbsenceDay", decimal.Decimal(0))
            if absence_day >= 1 and day_off != WORK_ONE_DAY:
                result["f_AbsenceDay"] = absence_day / decimal.Decimal(2)

            # 存储到数据库 ????
            # store_attendance_data(result, cn)

            # Statistics  处理
            b_not_exist_full_attendance_holiday = True
            b_not_exist_manual_record_full_attendance = True

            # 检查所有考勤记录
            for i in range(4):
                if i == 0:
                    desc = set_obj_to_str(result.get("f_OnDuty1Desc", ""))
                elif i == 1:
                    desc = set_obj_to_str(result.get("f_OnDuty2Desc", ""))
                elif i == 2:
                    desc = set_obj_to_str(result.get("f_OffDuty1Desc", ""))
                else:
                    desc = set_obj_to_str(result.get("f_OffDuty2Desc", ""))

                if desc == str_lateness:
                    total_late += 1
                    bday_real_work = False
                elif desc == str_leave_early:
                    total_leave_early += 1
                    bday_real_work = False
                elif desc == str_not_read_card:
                    total_not_read_card += 1
                    bday_real_work = False
                else:
                    if desc == str_sign_in:
                        b_not_exist_manual_record_full_attendance = False

                    # 检查特殊假期类型
                    for j, holiday_type in enumerate(dt_holiday_type):
                        if j >= len(total_special_day):
                            break

                        if desc == holiday_type.get("f_HolidayType", ""):
                            full_attendance = holiday_type.get("f_fullAttendance", 0)
                            if full_attendance < 1:
                                bday_real_work = False
                            else:
                                b_not_exist_full_attendance_holiday = False

                            total_special_day[j] += decimal.Decimal("0.25")
                            break

            # 检查全天无刷卡记录
            if (not result.get("f_OnDuty1") and not result.get("f_OffDuty1") and
                    not result.get("f_OnDuty2") and not result.get("f_OffDuty2") and
                    b_not_exist_full_attendance_holiday):
                bday_real_work = False

            if b_not_exist_manual_record_full_attendance or b_manual_record_as_full_attendance:
                pass
            else:
                bday_real_work = False

            # 更新Statistics  信息
            total_absence += result.get("f_AbsenceDay", decimal.Decimal(0))
            total_overtime += result.get("f_OvertimeTime", decimal.Decimal(0))
            total_late_minutes += result.get("f_LateTime", 0)
            total_leave_early_minutes += result.get("f_LeaveEarlyTime", 0)

            # 检查是否有迟到、早退或旷工
            if (result.get("f_LateTime", 0) > 0 or
                    result.get("f_LeaveEarlyTime", 0) > 0 or
                    result.get("f_AbsenceDay", decimal.Decimal(0)) > 0):
                bday_real_work = False

            # 全勤特殊处理
            if b_full_attendance_special_a:
                if (result.get("f_OnDuty1") or result.get("f_OffDuty1") or
                        result.get("f_OnDuty2") or result.get("f_OffDuty2")):
                    bday_real_work = True

            # 更新工作天数Statistics  
            if bday_real_work:
                day_real_work += 1

            if bday_should_work:
                day_should_work += 1



        # end while indexDate --这Unit是每天的处理
    # end for itm in objt_b_Consumer  -- 这Unit是下一Unituser的处理


    # # 4. 添加Total行到结果集
    # objConsumerStatistics.append(drtotal)
    # # nowstr = datetime.datetime.now().strftime("%Y-%m-%d_%H_%M_%S")
    # logtitle = f"{beginYMD} to {endYMD}"
    #
    objMealReport, objReaderStatistics, objt_b_group4Meal, objConsumerStatistics, logtitle = ""
    return createTestReport(objMealReport, objReaderStatistics, objt_b_group4Meal, objConsumerStatistics, logtitle)


def calculate_time_difference_minutes(time1: Any, time2: Any) -> int:
    """
    计算两UnitTime之间的分钟差

    Args:
        time1: 第一UnitTime
        time2: 第二UnitTime

    Returns:
        int: 分钟差
    """
    if not time1 or not time2:
        return 0

    try:
        # 确保是datetime对象
        if isinstance(time1, str):
            time1 = datetime.strptime(time1, "%Y-%m-%d %H:%M:%S")
        if isinstance(time2, str):
            time2 = datetime.strptime(time2, "%Y-%m-%d %H:%M:%S")

        # 计算Time差（分钟）
        time_diff = (time1 - time2).total_seconds() / 60
        return abs(int(time_diff))
    except (ValueError, TypeError):
        return 0

def _parse_leave_times(self, leave_record: Dict[str, Any], index_date: datetime) -> tuple:
    """解析请假开始和结束Time"""

    try:
        # 解析开始Time
        start_date_str = str(leave_record.get("f_Value", ""))
        start_date = self._parse_date_string(start_date_str, index_date.year)

        # 处理上午/下午标记
        start_period = str(leave_record.get("f_Value1", ""))
        start_time_str = self._add_time_period(start_date, start_period, is_start=True)
        start_time = datetime.strptime(start_time_str, "%Y-%m-%d %H:%M:%S")

        # 解析结束Time
        end_date_str = str(leave_record.get("f_Value2", ""))
        end_date = self._parse_date_string(end_date_str, index_date.year)

        # 处理上午/下午标记
        end_period = str(leave_record.get("f_Value3", ""))
        end_time_str = self._add_time_period(end_date, end_period, is_start=False)
        end_time = datetime.strptime(end_time_str, "%Y-%m-%d %H:%M:%S")

        holiday_type = str(leave_record.get("f_HolidayType", ""))

        return start_time, end_time, holiday_type

    except Exception as e:
        print(f"解析请假Time错误: {e}")
        return None, None, ""

def _parse_date_string(self, date_str: str, default_year: int) -> datetime:
    """解析日期字符串"""
    try:
        if not date_str:
            return datetime(default_year, 1, 1)

        # 尝试不同日期格式
        date_formats = [
            "%Y-%m-%d", "%Y/%m/%d", "%Y.%m.%d",
            "%m-%d", "%m/%d", "%m.%d"
        ]

        for fmt in date_formats:
            try:
                if fmt in ["%m-%d", "%m/%d", "%m.%d"]:
                    # 只有月日的情况，添加年份
                    date_str_with_year = f"{default_year}-{date_str}"
                    return datetime.strptime(date_str_with_year, "%Y-%m-%d")
                else:
                    return datetime.strptime(date_str, fmt)
            except ValueError:
                continue

        # 如果所有格式都 Failed ，返回默认日期
        return datetime(default_year, 1, 1)

    except Exception:
        return datetime(default_year, 1, 1)

def _add_time_period(self, base_date: datetime, period: str, is_start: bool) -> str:
    """根据上午/下午标记添加具体Time"""
    date_str = base_date.strftime("%Y-%m-%d")

    if period == self.STR_AM:
        time_str = "00:00:00" if is_start else "12:00:00"
    else:
        time_str = "12:00:00" if is_start else "23:59:59"

    return f"{date_str} {time_str}"

def _check_leave_type(self, start_time: datetime, end_time: datetime,
                      index_date: datetime, current_day_off: int,
                      holiday_type: str, holiday_type1: str, holiday_type2: str) -> tuple:
    """检查休假类型（全天、上午、下午）"""

    day_off = current_day_off
    new_holiday_type1 = holiday_type1
    new_holiday_type2 = holiday_type2

    try:
        # 全天休假检查
        day_start = index_date.replace(hour=0, minute=0, second=0, microsecond=0)
        day_end = index_date.replace(hour=23, minute=59, second=59, microsecond=0)

        if start_time <= day_start and day_end <= end_time:
            return 0, holiday_type, holiday_type  # 全天休假

        # 上午半天假检查
        noon_start = index_date.replace(hour=12, minute=0, second=0, microsecond=0)
        if start_time <= day_start and noon_start <= end_time:
            new_holiday_type1 = holiday_type
            if day_off == 1:  # 如果已经是下午休息
                return 0, new_holiday_type1, holiday_type2  # 转为全天休息
            else:
                day_off = 2  # 上午休息，下午上班

        # 下午半天假检查
        noon_end = index_date.replace(hour=12, minute=0, second=1, microsecond=0)
        day_2359 = index_date.replace(hour=23, minute=59, second=59, microsecond=0)

        if start_time <= noon_end and day_2359 <= end_time:
            new_holiday_type2 = holiday_type
            if day_off == 2:  # 如果已经是上午休息
                return 0, holiday_type1, new_holiday_type2  # 转为全天休息
            else:
                day_off = 1  # 上午上班，下午休息

        return day_off, new_holiday_type1, new_holiday_type2

    except Exception as e:
        print(f"检查休假类型错误: {e}")
        return current_day_off, holiday_type1, holiday_type2

def _update_attendance_record(self, dr: Dict[str, Any], day_off: int,
                              holiday_type1: str, holiday_type2: str,
                              t_leave_timeout: int, t_leave_absence_timeout: int,
                              t_late_timeout: int, t_late_absence_timeout: int) -> Dict[str, Any]:
    """根据休假类型更新考勤记录"""

    result = dr.copy()
    b_need_update_desc = False

    if day_off == 0:  # 全天休假
        result = self._handle_full_day_leave(result, holiday_type1, holiday_type2)

    elif day_off == 1:  # 下午休假，上午上班
        result = self._handle_afternoon_leave(result, holiday_type2, t_leave_timeout, t_leave_absence_timeout)

    elif day_off == 2:  # 上午休假，下午上班
        result = self._handle_morning_leave(result, holiday_type1, t_late_timeout, t_late_absence_timeout)

    # day_off == 3 正常上班，不需要特殊处理

    return result

def _handle_full_day_leave(self, dr: Dict[str, Any], holiday_type1: str, holiday_type2: str) -> Dict[str, Any]:
    """处理全天休假情况"""
    result = dr.copy()

    # 设置所有考勤描述为请假类型
    for key in ["f_OnDuty1Desc", "f_OffDuty1Desc", "f_OnDuty2Desc", "f_OffDuty2Desc"]:
        result[key] = holiday_type1

    # 清空所有考勤Time
    for key in ["f_OnDuty1", "f_OnDuty2", "f_OffDuty1", "f_OffDuty2"]:
        result[key] = None

    return result

def _handle_afternoon_leave(self, dr: Dict[str, Any], holiday_type2: str,
                            t_leave_timeout: int, t_leave_absence_timeout: int) -> Dict[str, Any]:
    """处理下午休假情况"""
    result = dr.copy()

    # 检查上午下班Status
    offduty1_desc = self._set_obj_to_str(result.get("f_OffDuty1Desc", ""))

    if offduty1_desc in [self.STR_NOT_READ_CARD, self.STR_LEAVE_EARLY, self.STR_ABSENCE]:
        offduty2 = self._set_obj_to_str(result.get("f_OffDuty2", ""))

        if offduty2:
            # 用下午下班记录更新上午下班记录
            result["f_OffDuty1"] = result["f_OffDuty2"]
            result["f_OffDuty1Desc"] = ""

            # 清空下午记录
            result["f_OffDuty2"] = None
            result["f_OnDuty2"] = None

            # 重新计算Status
            result = self._recalculate_offduty_status(result, t_leave_timeout, t_leave_absence_timeout)
        else:
            onduty2 = self._set_obj_to_str(result.get("f_OnDuty2", ""))
            if onduty2:
                # 用下午上班记录更新上午下班记录
                result["f_OffDuty1"] = result["f_OnDuty2"]
                result["f_OffDuty1Desc"] = ""

                # 清空下午记录
                result["f_OnDuty2"] = None
                result["f_OffDuty2"] = None

                # 重新计算Status
                result = self._recalculate_offduty_status(result, t_leave_timeout, t_leave_absence_timeout)

    # 设置下午请假
    result["f_OnDuty2Desc"] = holiday_type2
    result["f_OffDuty2Desc"] = holiday_type2
    result["f_OnDuty2"] = None
    result["f_OffDuty2"] = None

    return result

def _handle_morning_leave(self, dr: Dict[str, Any], holiday_type1: str,
                          t_late_timeout: int, t_late_absence_timeout: int) -> Dict[str, Any]:
    """处理上午休假情况"""
    result = dr.copy()

    # 检查下午上班Status
    onduty2_desc = self._set_obj_to_str(result.get("f_OnDuty2Desc", ""))

    if onduty2_desc in [self.STR_NOT_READ_CARD, self.STR_LATENESS, self.STR_ABSENCE]:
        onduty1 = self._set_obj_to_str(result.get("f_OnDuty1", ""))

        if onduty1:
            # 用上午上班记录更新下午上班记录
            result["f_OnDuty2"] = result["f_OnDuty1"]
            result["f_OnDuty2Desc"] = ""

            # 清空上午记录
            result["f_OnDuty1"] = None
            result["f_OffDuty1"] = None

            # 重新计算Status
            result = self._recalculate_onduty_status(result, t_late_timeout, t_late_absence_timeout)
        else:
            offduty1 = self._set_obj_to_str(result.get("f_OffDuty1", ""))
            if offduty1:
                # 用上午下班记录更新下午上班记录
                result["f_OnDuty2"] = result["f_OffDuty1"]
                result["f_OnDuty2Desc"] = ""

                # 清空上午记录
                result["f_OffDuty1"] = None

                # 重新计算Status
                result = self._recalculate_onduty_status(result, t_late_timeout, t_late_absence_timeout)

    # 设置上午请假
    result["f_OnDuty1Desc"] = holiday_type1
    result["f_OffDuty1Desc"] = holiday_type1
    result["f_OnDuty1"] = None
    result["f_OffDuty1"] = None

    return result

def _recalculate_offduty_status(self, dr: Dict[str, Any],
                                t_leave_timeout: int, t_leave_absence_timeout: int) -> Dict[str, Any]:
    """重新计算下班Status（早退/旷工）"""
    result = dr.copy()

    offduty1 = result.get("f_OffDuty1")
    if not offduty1:
        return result

    try:
        # 转换为datetime对象
        if isinstance(offduty1, str):
            offduty1_time = datetime.strptime(offduty1, "%Y-%m-%d %H:%M:%S")
        else:
            offduty1_time = offduty1

        # 计算标准下班Time（这里需要根据实际情况调整）
        standard_offduty = self.DEFAULT_WORK_END

        # 计算Time差
        time_diff = (offduty1_time - standard_offduty).total_seconds() / 60  # 转换为分钟

        if time_diff < -t_leave_absence_timeout:  # 早于旷工阈值
            result["f_OffDuty1Desc"] = self.STR_ABSENCE
        elif time_diff < -t_leave_timeout:  # 早于早退阈值
            result["f_OffDuty1Desc"] = self.STR_LEAVE_EARLY
        else:
            result["f_OffDuty1Desc"] = ""

    except Exception as e:
        print(f"重新计算下班Status错误: {e}")

    return result

def _recalculate_onduty_status(self, dr: Dict[str, Any],
                               t_late_timeout: int, t_late_absence_timeout: int) -> Dict[str, Any]:
    """重新计算上班Status（迟到/旷工）"""
    result = dr.copy()

    onduty2 = result.get("f_OnDuty2")
    if not onduty2:
        return result

    try:
        # 转换为datetime对象
        if isinstance(onduty2, str):
            onduty2_time = datetime.strptime(onduty2, "%Y-%m-%d %H:%M:%S")
        else:
            onduty2_time = onduty2

        # 计算标准上班Time（这里需要根据实际情况调整）
        standard_onduty = self.DEFAULT_WORK_START

        # 计算Time差
        time_diff = (onduty2_time - standard_onduty).total_seconds() / 60  # 转换为分钟

        if time_diff > t_late_absence_timeout:  # 晚于旷工阈值
            result["f_OnDuty2Desc"] = self.STR_ABSENCE
        elif time_diff > t_late_timeout:  # 晚于迟到阈值
            result["f_OnDuty2Desc"] = self.STR_LATENESS
        else:
            result["f_OnDuty2Desc"] = ""

    except Exception as e:
        print(f"重新计算上班Status错误: {e}")

    return result

# 辅助函数：安全获取对象字符串表示
def set_obj_to_str(obj, default=""):
    """安全地将对象转换为字符串"""
    if obj is None:
        return default
    return str(obj)

# 辅助函数：检查字符串是否包含子串
def str_contains(haystack, needle):
    """检查字符串是否包含子串"""
    if haystack is None or needle is None:
        return False
    return needle in haystack


def parse_date_string(date_str: str, default_year: int) -> Optional[datetime]:
    """
    解析日期字符串

    Args:
        date_str: 日期字符串
        default_year: 默认年份

    Returns:
        Optional[datetime]: 解析后的日期对象
    """
    if not date_str:
        return None

    try:
        # 尝试直接解析日期
        if "-" in date_str and len(date_str) > 7:
            return datetime.datetime.strptime(date_str, "%Y-%m-%d")
        else:
            # 只有月日，添加年份
            return datetime.datetime.strptime(f"{default_year}-{date_str}", "%Y-%m-%d")
    except ValueError:
        # 处理异常日期格式
        try:
            # 尝试Others  格式
            return datetime.datetime.strptime(date_str, "%m/%d/%Y")
        except ValueError:
            return None


def set_period_time(base_date: datetime, period: str, str_am: str, str_pm: str) -> Optional[datetime]:
    """
    设置Time段的具体Time

    Args:
        base_date: 基准日期
        period: Time段描述
        str_am: 上午描述
        str_pm: 下午描述

    Returns:
        Optional[datetime]: 设置Time后的日期对象
    """
    if not base_date:
        return None

    if period == str_am:
        return base_date.replace(hour=0, minute=0, second=0)
    elif period == str_pm:
        return base_date.replace(hour=12, minute=0, second=0)
    else:
        # 默认处理
        return base_date.replace(hour=0, minute=0, second=0)


def createTestReport(objMealReport, objReaderStatistics, objt_b_group4Meal, objConsumerStatistics, logtitle):
    return None
    # # 创建新的工作簿
    # try:
    #     from openpyxl.reader.excel import load_workbook  # 2025-11-15 16:08:06
    #     from openpyxl import Workbook  # 2025-11-15 16:08:06        from openpyxl.reader.excel import load_workbook,Workbook  # 2025-11-15 16:08:06
    # except Exception as e:
    #     return None
    # workbook = Workbook()
    # sheet = workbook.active  # 获取默认工作表
    # sheet.title = "Details  "  # 设置工作表名称
    # # mealkeys = list(objt_b_group4Meal[0].keys())
    # mealkeysTitle = ['ID', 'Department', 'User ID', 'Name', 'Time', 'Meal name  ', 'Meal amount (yuan)  ', 'Location  ']
    # mealkeys = ['f_RecID', 'f_GroupName', 'f_ConsumerNO', 'f_ConsumerName', 'f_ReadDate', 'f_MealName', 'f_Cost',
    #             'f_ReaderName']

    # ws = sheet
    # widthunit = 10
    # ws.column_dimensions['A'].width = 85 / widthunit
    # ws.column_dimensions['B'].width = 100 / widthunit
    # ws.column_dimensions['C'].width = 100 / widthunit
    # ws.column_dimensions['D'].width = 100 / widthunit
    # ws.column_dimensions['E'].width = 200 / widthunit
    # ws.column_dimensions['F'].width = 100 / widthunit
    # ws.column_dimensions['G'].width = 120 / widthunit
    # ws.column_dimensions['H'].width = 145 / widthunit

    # sheet.append(mealkeysTitle)
    # for row in objMealReport:
    #     sheet.append([row[x] for x in mealkeys])

    # sheet = workbook.create_sheet()  # 获取默认工作表
    # sheet.title = "Subtotal by meal location  "  # 设置工作表名称
    # mealkeysTitle = ['Location  ', 'Total number of meals  ', 'Total meal amount ($)  ']
    # mealkeys = ['f_ReaderName', 'f_CostCount', 'f_CostTotal4Reader']
    # ws = sheet
    # widthunit = 10
    # ws.column_dimensions['A'].width = 145 / widthunit
    # ws.column_dimensions['B'].width = 120 / widthunit
    # ws.column_dimensions['C'].width = 120 / widthunit
    # sheet.append(mealkeysTitle)
    # for row in objReaderStatistics:
    #     sheet.append([row[x] for x in mealkeys])

    # sheet = workbook.create_sheet()  # 获取默认工作表
    # sheet.title = "Department subtotal  "  # 设置工作表名称
    # mealkeysTitle = ['Department', 'Breakfast count  ', 'Lunch count  ', 'Dinner count  ', 'Night snack count  ', 'Total number of meals  ', 'Total meal amount ($)  ']
    # mealkeys = ['f_GroupName', 'f_MorningCount', 'f_LunchCount', 'f_EveningCount', 'f_OtherCount', 'f_groupTotalTimes',
    #             'f_groupTotal']
    # ws = sheet
    # widthunit = 10
    # ws.column_dimensions['A'].width = 145 / widthunit
    # ws.column_dimensions['B'].width = 80 / widthunit
    # ws.column_dimensions['C'].width = 80 / widthunit
    # ws.column_dimensions['D'].width = 80 / widthunit
    # ws.column_dimensions['E'].width = 80 / widthunit
    # ws.column_dimensions['F'].width = 120 / widthunit
    # ws.column_dimensions['G'].width = 120 / widthunit
    # sheet.append(mealkeysTitle)
    # for row in objt_b_group4Meal:
    #     sheet.append([row[x] for x in mealkeys])

    # # 每人的Statistics  
    # sheet = workbook.create_sheet()  # 获取默认工作表
    # sheet.title = "Statistics  "  # 设置工作表名称
    # mealkeysTitle = ['Department', 'User ID', 'Name', 'Breakfast count  ', 'Lunch count  ', 'Dinner count  ', 'Night snack count  ', 'Total number of meals  ',
    #                  'Total meal amount ($)  ']
    # mealkeys = ['f_GroupName', 'f_ConsumerNO', 'f_ConsumerName', 'f_CostMorningCount', 'f_CostLunchCount',
    #             'f_CostEveningCount', 'f_CostOtherCount', 'f_CostTotalCount', 'f_CostTotal']
    # ws = sheet
    # widthunit = 10
    # ws.column_dimensions['A'].width = 145 / widthunit
    # ws.column_dimensions['C'].width = 100 / widthunit
    # ws.column_dimensions['D'].width = 100 / widthunit
    # ws.column_dimensions['E'].width = 80 / widthunit
    # ws.column_dimensions['F'].width = 80 / widthunit
    # ws.column_dimensions['G'].width = 80 / widthunit
    # ws.column_dimensions['H'].width = 80 / widthunit
    # ws.column_dimensions['I'].width = 120 / widthunit
    # ws.column_dimensions['J'].width = 120 / widthunit
    # sheet.append(mealkeysTitle)

    # for row in objConsumerStatistics:
    #     sheet.append([row[x] for x in mealkeys])

    # # 日志
    # sheet = workbook.create_sheet()  # 获取默认工作表
    # sheet.title = logtitle  # 设置工作表名称
    # nowstr = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    # loginfo = f"Generation time  : {nowstr}"
    # sheet.append([loginfo])

    # filename = f"{vwAppConfigParam.wgcloudpathdir}MealReport.xlsx  "
    # # vwAppConfig.vwlog(f"wgcloudpathdir =  {vwAppConfigParam.wgcloudpathdir}")
    # # vwAppConfig.vwlog(f"消费表格创建  {filename}")
    # # 保存文件
    # workbook.save(filename)  # f"{vwAppConfigParam.wgcloudpathdir}MealReport.xlsx  ")
    # # vwAppConfig.vwlog(f"消费表格已创建并保存为  {filename}")
    # # print("消费表格已创建并保存为 '消费记录表.xlsx'")
    # file_path = f"{vwAppConfigParam.wgcloudpathdir}MealReport.xlsx  "
    # response = make_response(send_file(file_path))
    # safe_name = quote("MealReport.xlsx  ")  # 文件名URL编码
    # response.headers["Content-Disposition"] = "attachment; filename*=UTF-8''{}".format(safe_name)
    # return response


def get_report_att_setup():  # 获取规则信息
    wgform = report_attSetupForm()

    #            //从规则库中取记录有效规则 和 Time点
    valid_span = 0  # 不受间隔限制
    mealMode = 0  # 就餐模式0= 表示刷一次算一次,1=刷多次只算一次,2=小于指定秒内只算一次
    b_department_mode = False  # ; //2013-9-27_08:28:35 Department计价方式

    # 参数值
    strSql = "SELECT * from t_a_Attendence"
    query_obj = db.session.execute(text(strSql))
    objtable = query_obj.fetchall()
    if len(objtable) == 0:
        return wgform
    columns = query_obj.keys()  # 获取列名
    objedit = [dict(zip(columns, row)) for row in objtable]
    for rd in objedit:
        if rd["f_No"] == 1:
            wgform.t_a_Attendence_LateTimeout.data = rd["f_Value"]
        elif rd["f_No"] == 2:
            wgform.t_a_Attendence_LateAbsenceTimeout.data = rd["f_Value"]
        elif rd["f_No"] == 3:
            # wgform.t_a_Attendence_LateAbsenceDay.data = rd["f_Value"] * 2 # 单位由天 改为 半天
            wgform.t_a_Attendence_LateAbsenceDay.data = rd["f_Value"]  #  2025-09-23 14:46:34 单位由天 改为 半天
        elif rd["f_No"] == 4:
            wgform.t_a_Attendence_LeaveTimeout.data = rd["f_Value"]
        elif rd["f_No"] == 5:
            wgform.t_a_Attendence_LeaveAbsenceTimeout.data = rd["f_Value"]
        elif rd["f_No"] == 6:
            # wgform.t_a_Attendence_LeaveAbsenceDay.data = rd["f_Value"] * 2
            wgform.t_a_Attendence_LeaveAbsenceDay.data = rd["f_Value"] #  2025-09-23 14:46:34 单位由天 改为 半天
        elif rd["f_No"] == 7:
            wgform.t_a_Attendence_OvertimeTimeout.data = rd["f_Value"]
        elif rd["f_No"] in [8,9,10,11,12,13]:
            # 处理开始Time (清除秒数)
            vtime = rd["f_Value"]
            if isinstance(vtime, str):
                # hhmmdata = vtime
                try:
                    vtime = datetime.datetime.strptime(vtime, '%Y-%m-%d %H:%M:%S')
                    hhmmdata = vwAppConfig.getStrOfDatetimeHHMM(vtime.replace(second=0).time())
                except Exception as e:
                    # vtime = datetime.datetime.strptime(vtime, '%H:%M')
                    hhmmdata = vtime
            else:
                hhmmdata = vwAppConfig.getStrOfDatetimeHHMM(vtime.replace(second=0).time())  # strftime('%H:%M:%S')
            if rd["f_No"] == 8:
                # wgform.t_a_Attendence_OnDuty0.data = rd["f_Value"]
                wgform.t_a_Attendence_OnDuty0.data = hhmmdata
            elif rd["f_No"] == 9:
                wgform.t_a_Attendence_OffDuty0.data = hhmmdata
                # wgform.t_a_Attendence_OffDuty0.data = rd["f_Value"]
            elif rd["f_No"] == 10:
                wgform.t_a_Attendence_OnDuty1.data = hhmmdata # rd["f_Value"]
            elif rd["f_No"] == 11:
                wgform.t_a_Attendence_OffDuty1.data = hhmmdata # rd["f_Value"]
            elif rd["f_No"] == 12:
                wgform.t_a_Attendence_OnDuty2.data = hhmmdata # rd["f_Value"]
            elif rd["f_No"] == 13:
                wgform.t_a_Attendence_OffDuty2.data = hhmmdata #  rd["f_Value"]

        elif rd["f_No"] == 14:
            # wgform.t_a_Attendence_ReadCardFourTimes.data = True
            wgform.t_a_Attendence_ReadCardTwoTimes.data = 4
            if int(rd["f_Value"]) != 4:
                # wgform.t_a_Attendence_ReadCardFourTimes.data = False
                wgform.t_a_Attendence_ReadCardTwoTimes.data = 2

    return wgform


@bp.route('/report_att_setup', methods=['POST', 'GET'])
@login_required
def web_report_att_setup():  # Edit就餐设置  就餐规则
    url4htmltemplate = "wgcomm/report-att-setup.html"  # template 实际操作界面
    url4redirect = "wgcomm_report_att.web_report_att_setup"  # redirect 返回上一Unit界面 主界面
    wgform = report_attSetupForm()
    if not request.form.get('t_a_Attendence_LateTimeout'):
        # 加载数据
        pass
        wgform = get_report_att_setup()
    else:
        if request.form.get('t_a_Attendence_LateTimeout'):  # 如果有日期 生成新的报表
            wgform = report_attSetupForm(request.form)
            if wgform.submitcancel.data:  # 取消时 退出到列表
                # return redirect(url_for(url4redirect))  # url_for("wgcomm.controller_list"))  # 要跳转 controller_list()
                return redirect(url_for(
                    "wgcomm_report_att.web_report_att"))  # 回到生成部分 url_for("wgcomm.controller_list"))  # 要跳转 controller_list()
            if wgform.validate():
                # 参数值
                arrval = [
                    wgform.t_a_Attendence_LateTimeout.data,
                    wgform.t_a_Attendence_LateAbsenceTimeout.data,
                    wgform.t_a_Attendence_LateAbsenceDay.data,  # 单位由天 改为 半天
                    wgform.t_a_Attendence_LeaveTimeout.data,
                    wgform.t_a_Attendence_LeaveAbsenceTimeout.data,
                    wgform.t_a_Attendence_LeaveAbsenceDay.data, # 单位由天 改为 半天
                    wgform.t_a_Attendence_OvertimeTimeout.data,
                    wgform.t_a_Attendence_OnDuty0.data,
                    wgform.t_a_Attendence_OffDuty0.data,
                    wgform.t_a_Attendence_OnDuty1.data,
                    wgform.t_a_Attendence_OffDuty1.data,
                    wgform.t_a_Attendence_OnDuty2.data,
                    wgform.t_a_Attendence_OffDuty2.data,
                    wgform.t_a_Attendence_ReadCardTwoTimes.data

                ]
                for i in range(14):
                    f_NO = i+1
                    f_Value = arrval[i] # wgform.t_a_Attendence_LateTimeout.data
                    TAAttendence.query.filter_by(f_NO=f_NO).update({'f_Value': f_Value}, synchronize_session=False)

                try:
                    db.session.commit()
                    # vwAppConfig.setusercardChangeTime()  # 2024-05-10 06:14:09 user变更
                    vwAppConfig.vwlog('{}--{} '.format(g.user.f_OperatorName,
                                                       "考勤设置 Edit Successfully ！".format(
                                                       )))  # 2024-01-13 12:59:02 增加日志
                    flash("考勤设置 Edit Successfully ！")
                    # return startDeal(vwAppConfig.getStrOfDatetimeYMD(f_BeginYMD),vwAppConfig.getStrOfDatetimeYMD(f_EndYMD), f_GroupName)  #  2025-07-12 17:27:07
                    return redirect(url_for(url4redirect))
                except Exception as e:
                    print(e)
                    # 加入数据库commit提交 Failed ，必须回滚！！！
                    db.session.rollback()
                    flash("考勤设置 Edit Failed ！")
                    vwAppConfig.vwlog('{}--{}[{}] '.format(g.user.f_OperatorName, "考勤设置 Edit Failed ！", e))
                    wgform = MealSetupForm(request.form)
            else:
                for message in wgform.messages:
                    flash(message)
                wgform = MealSetupForm(request.form)
    context = {
        "wgform": wgform,
    }
    return render_template(url4htmltemplate, **context)  # wgform=wgform


# import pyodbc
# import datetime
# from dataclasses import dataclass
# from typing import List, Optional, Dict, Any


# 配置类
# @dataclass
# class AppConfig:
#     db_con_string: str
#     # Others  配置参数...


# 全局常量
class GlobalConstants:
    ParamLoc_ActivateOtherShiftSchedule = 1
    Param_InvalidSwipeNotAsAttend = "1"
    # Others  常量...


# 数据库连接和工具函数
class DatabaseHelper:
    def __init__(self, connection_string):
        self.connection_string = connection_string

    # def get_connection(self):
    #     return pyodbc.connect(self.connection_string)

    def execute_query(self, query, params=None):
        if params:
            db.session.execute(query, params)
        else:
            db.session.execute(query)

        # with self.get_connection() as conn:
        #     with conn.cursor() as cursor:
        #         if params:
        #             cursor.execute(query, params)
        #         else:
        #             cursor.execute(query)
        #         return cursor.fetchall()


# 主要考勤处理类
class AttendanceProcessor:
    def __init__(self):
        # self.config = config
        self.db_helper = DatabaseHelper("") #config.db_con_string)

    def make_four_times(self):
        """处理四次打卡的考勤逻辑"""
        try:
            # 1. 获取考勤参数
            self.get_attendance_params()

            # 2. 清空现有考勤数据
            self.clear_attendance_data()

            # 3. 获取节假日数据
            holidays = self.get_holidays()

            # 4. 获取员工数据
            consumers = self.get_consumers()

            # 5. 处理每Unit员工的考勤
            for consumer in consumers:
                self.process_consumer_attendance(consumer)

            # 6. 生成考勤报告
            self.generate_attendance_report()

        except Exception as e:
            print(f"处理考勤时发生错误: {e}")
            # 记录错误日志等

    def get_attendance_params(self):
        """获取考勤参数"""
        # 实现获取考勤参数的逻辑
        pass

    def get_holidays(self):
        """获取节假日数据"""
        query = "SELECT * FROM t_a_Holiday ORDER BY f_NO ASC"
        return self.db_helper.execute_query(query)

    def get_consumers(self):
        """获取需要处理考勤的员工"""
        query = "SELECT f_ConsumerID FROM v_b_ConsumerIDCard WHERE f_AttendEnabled > 0"
        return self.db_helper.execute_query(query)

    def process_consumer_attendance(self, consumer):
        """处理单Unit员工的考勤"""
        consumer_id = consumer[0]  # 假设第一列是ID

        # 获取员工的打卡记录
        card_records = self.get_card_records(consumer_id)

        # 处理每天的考勤
        start_date = datetime.datetime.now() - datetime.timedelta(days=30)  # 示例：过去30天
        end_date = datetime.datetime.now()

        current_date = start_date
        while current_date <= end_date:
            daily_records = self.filter_daily_records(card_records, current_date)
            self.process_daily_attendance(consumer_id, current_date, daily_records)
            current_date += datetime.timedelta(days=1)

    def get_card_records(self, consumer_id):
        """获取员工的打卡记录"""
        query = """
            SELECT f_ReadDate, f_Character, f_Type 
            FROM t_d_SwipeRecord 
            WHERE f_ConsumerID = ? 
            AND f_ReadDate BETWEEN ? AND ?
            ORDER BY f_ReadDate ASC
        """
        start_date = datetime.datetime.now() - datetime.timedelta(days=30)
        end_date = datetime.datetime.now()
        return self.db_helper.execute_query(query, (consumer_id, start_date, end_date))

    def filter_daily_records(self, records, date):
        """过滤出某天的打卡记录"""
        return [record for record in records
                if record[0].date() == date.date()]

    def process_daily_attendance(self, consumer_id, date, records):
        """处理单日考勤"""
        # 这里实现四次打卡的逻辑处理
        # 1. 判断是否工作日/节假日
        # 2. 处理四次打卡Time
        # 3. 计算迟到、早退、缺勤等
        # 4. 保存考勤结果

        attendance_result = {
            'consumer_id': consumer_id,
            'date': date,
            'onduty1': None,
            'offduty1': None,
            'onduty2': None,
            'offduty2': None,
            'late_minutes': 0,
            'leave_early_minutes': 0,
            'absence': False
        }

        # 简化的四次打卡处理逻辑
        if len(records) >= 4:
            # 有四次打卡记录
            attendance_result.update({
                'onduty1': records[0][0],
                'offduty1': records[1][0],
                'onduty2': records[2][0],
                'offduty2': records[3][0]
            })
        elif len(records) > 0:
            # 处理不完整打卡的情况
            pass

        # 保存考勤结果
        self.save_attendance_result(attendance_result)

    def save_attendance_result(self, result):
        """保存考勤结果到数据库"""
        query = """
            INSERT INTO t_d_AttendenceData 
            (f_ConsumerID, f_AttDate, f_Onduty1, f_Offduty1, f_Onduty2, f_Offduty2, 
             f_LateTime, f_LeaveEarlyTime, f_AbsenceDay)
            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
        """
        params = (
            result['consumer_id'], result['date'],
            result['onduty1'], result['offduty1'],
            result['onduty2'], result['offduty2'],
            result['late_minutes'], result['leave_early_minutes'],
            1 if result['absence'] else 0
        )
        self.db_helper.execute_query(query, params)


# # 使用示例
# if __name__ == "__main__":
#     config = AppConfig(
#         db_con_string="DRIVER={SQL Server};SERVER=localhost;DATABASE=test;UID=user;PWD=password"
#     )
#     processor = AttendanceProcessor(config)
#     processor.make_four_times()