March 16 2026: fix export overnight calculation issue
This commit is contained in:
@@ -9870,6 +9870,16 @@ def export_time_attendance_excel(records, project_name_for_filename, date_range_
|
|||||||
if _out_t_d.hour <= 3:
|
if _out_t_d.hour <= 3:
|
||||||
if _in_t_d.hour < 18:
|
if _in_t_d.hour < 18:
|
||||||
continue
|
continue
|
||||||
|
# Orphan guard: an early-morning OUT whose check_in_date
|
||||||
|
# matches the current day is an orphan from the PREVIOUS
|
||||||
|
# overnight shift — it must NOT steal an evening IN.
|
||||||
|
# Only OUTs moved in by overnight detection (check_in_date
|
||||||
|
# is later than the current day) are valid partners.
|
||||||
|
_out_orig_date = _out_r.check_in_date
|
||||||
|
if hasattr(_out_orig_date, 'date'):
|
||||||
|
_out_orig_date = _out_orig_date.date()
|
||||||
|
if _out_orig_date <= date_obj.date():
|
||||||
|
continue
|
||||||
elif _out_t_d <= _in_t_d:
|
elif _out_t_d <= _in_t_d:
|
||||||
continue
|
continue
|
||||||
_in_ts = datetime.combine(_in_r.check_in_date, _in_r.check_in_time)
|
_in_ts = datetime.combine(_in_r.check_in_date, _in_r.check_in_time)
|
||||||
@@ -9920,6 +9930,14 @@ def export_time_attendance_excel(records, project_name_for_filename, date_range_
|
|||||||
if _cb_out_t.hour <= 3:
|
if _cb_out_t.hour <= 3:
|
||||||
if _cb_in_t.hour < 18:
|
if _cb_in_t.hour < 18:
|
||||||
continue
|
continue
|
||||||
|
# Orphan guard: same-day early-morning OUT is from previous
|
||||||
|
# overnight shift — skip it. Only moved OUTs (check_in_date
|
||||||
|
# later than current day) are valid overnight partners.
|
||||||
|
_cb_out_orig = _cb_out.check_in_date
|
||||||
|
if hasattr(_cb_out_orig, 'date'):
|
||||||
|
_cb_out_orig = _cb_out_orig.date()
|
||||||
|
if _cb_out_orig <= date_obj.date():
|
||||||
|
continue
|
||||||
elif _cb_out_t <= _cb_in_t:
|
elif _cb_out_t <= _cb_in_t:
|
||||||
continue
|
continue
|
||||||
_cb_in_ts = datetime.combine(_cb_in.check_in_date, _cb_in.check_in_time)
|
_cb_in_ts = datetime.combine(_cb_in.check_in_date, _cb_in.check_in_time)
|
||||||
@@ -10134,6 +10152,15 @@ def export_time_attendance_excel(records, project_name_for_filename, date_range_
|
|||||||
if _out_t.hour <= 3:
|
if _out_t.hour <= 3:
|
||||||
if _in_t.hour < 18:
|
if _in_t.hour < 18:
|
||||||
continue # early-morning OUT cannot pair with non-evening IN
|
continue # early-morning OUT cannot pair with non-evening IN
|
||||||
|
# Orphan guard: same-day early-morning OUT is from a
|
||||||
|
# previous overnight shift — not a valid partner for
|
||||||
|
# this evening IN. Only moved OUTs (check_in_date
|
||||||
|
# later than current day) should pair.
|
||||||
|
_out_orig_d = out_ri['record'].check_in_date
|
||||||
|
if hasattr(_out_orig_d, 'date'):
|
||||||
|
_out_orig_d = _out_orig_d.date()
|
||||||
|
if _out_orig_d <= date_obj.date():
|
||||||
|
continue
|
||||||
elif _out_t <= _in_t:
|
elif _out_t <= _in_t:
|
||||||
continue # same-day OUT must be strictly after IN
|
continue # same-day OUT must be strictly after IN
|
||||||
if out_ri['work_type'] == in_ri['work_type']:
|
if out_ri['work_type'] == in_ri['work_type']:
|
||||||
@@ -10170,6 +10197,15 @@ def export_time_attendance_excel(records, project_name_for_filename, date_range_
|
|||||||
if _out_t2.hour <= 3:
|
if _out_t2.hour <= 3:
|
||||||
if _in_t2.hour < 18:
|
if _in_t2.hour < 18:
|
||||||
continue
|
continue
|
||||||
|
# Orphan guard: same-day early-morning OUT is from a
|
||||||
|
# previous overnight shift — not a valid partner for
|
||||||
|
# this evening IN. Only moved OUTs (check_in_date
|
||||||
|
# later than current day) should pair.
|
||||||
|
_out_orig_d2 = out_ri['record'].check_in_date
|
||||||
|
if hasattr(_out_orig_d2, 'date'):
|
||||||
|
_out_orig_d2 = _out_orig_d2.date()
|
||||||
|
if _out_orig_d2 <= date_obj.date():
|
||||||
|
continue
|
||||||
elif _out_t2 <= _in_t2:
|
elif _out_t2 <= _in_t2:
|
||||||
continue
|
continue
|
||||||
# Cross-type pair: one side is regular, other is special
|
# Cross-type pair: one side is regular, other is special
|
||||||
@@ -11101,17 +11137,45 @@ def export_time_attendance_by_building_excel(records, project_name_for_filename,
|
|||||||
|
|
||||||
latest_in = ins_sorted[-1]
|
latest_in = ins_sorted[-1]
|
||||||
excess_ins = ins_sorted[:-1]
|
excess_ins = ins_sorted[:-1]
|
||||||
earliest_out = outs_sorted[0]
|
|
||||||
|
# Orphan guard: when the latest IN is evening (>=18h), skip
|
||||||
|
# early-morning OUTs (<=3h) whose check_in_date matches the
|
||||||
|
# current day — they are orphans from a previous overnight shift.
|
||||||
|
_oi_in_hour = latest_in['record'].check_in_time.hour
|
||||||
|
earliest_out = None
|
||||||
|
_oi_skip = []
|
||||||
|
for _oi_ri in outs_sorted:
|
||||||
|
if (earliest_out is None
|
||||||
|
and _oi_in_hour >= 18
|
||||||
|
and _oi_ri['record'].check_in_time.hour <= 3):
|
||||||
|
_oi_out_d = _oi_ri['record'].check_in_date
|
||||||
|
if hasattr(_oi_out_d, 'date'):
|
||||||
|
_oi_out_d = _oi_out_d.date()
|
||||||
|
if _oi_out_d <= date_obj.date():
|
||||||
|
_oi_skip.append(_oi_ri)
|
||||||
|
continue
|
||||||
|
if earliest_out is None:
|
||||||
|
earliest_out = _oi_ri
|
||||||
|
break
|
||||||
|
|
||||||
for ri in excess_ins:
|
for ri in excess_ins:
|
||||||
ri['used'] = True
|
ri['used'] = True
|
||||||
pairs.append({'check_in': ri['record'], 'check_out': None, 'is_miss_punch': True})
|
pairs.append({'check_in': ri['record'], 'check_out': None, 'is_miss_punch': True})
|
||||||
|
|
||||||
|
if earliest_out is not None:
|
||||||
latest_in['used'] = True
|
latest_in['used'] = True
|
||||||
earliest_out['used'] = True
|
earliest_out['used'] = True
|
||||||
pairs.append({'check_in': latest_in['record'], 'check_out': earliest_out['record'], 'is_miss_punch': False})
|
pairs.append({'check_in': latest_in['record'], 'check_out': earliest_out['record'], 'is_miss_punch': False})
|
||||||
|
else:
|
||||||
|
latest_in['used'] = True
|
||||||
|
pairs.append({'check_in': latest_in['record'], 'check_out': None, 'is_miss_punch': True})
|
||||||
|
|
||||||
for ri in outs_sorted[1:]:
|
for ri in outs_sorted:
|
||||||
|
if not ri['used'] and ri not in _oi_skip:
|
||||||
|
ri['used'] = True
|
||||||
|
pairs.append({'check_in': None, 'check_out': ri['record'], 'is_miss_punch': True})
|
||||||
|
# Orphan OUTs that were skipped
|
||||||
|
for ri in _oi_skip:
|
||||||
ri['used'] = True
|
ri['used'] = True
|
||||||
pairs.append({'check_in': None, 'check_out': ri['record'], 'is_miss_punch': True})
|
pairs.append({'check_in': None, 'check_out': ri['record'], 'is_miss_punch': True})
|
||||||
|
|
||||||
@@ -11129,6 +11193,22 @@ def export_time_attendance_by_building_excel(records, project_name_for_filename,
|
|||||||
if record_info[j]['used']:
|
if record_info[j]['used']:
|
||||||
continue
|
continue
|
||||||
if record_info[j]['is_out']:
|
if record_info[j]['is_out']:
|
||||||
|
# Orphan guard: when this IN is an evening
|
||||||
|
# check-in (>=18h) and the candidate OUT is
|
||||||
|
# early-morning (<=3h), the OUT is only a
|
||||||
|
# valid partner if it was moved in by overnight
|
||||||
|
# detection (check_in_date > current day).
|
||||||
|
# Same-day early-morning OUTs are orphans from
|
||||||
|
# a previous overnight shift.
|
||||||
|
_in_rec = record_info[i]['record']
|
||||||
|
_out_rec = record_info[j]['record']
|
||||||
|
if (_in_rec.check_in_time.hour >= 18
|
||||||
|
and _out_rec.check_in_time.hour <= 3):
|
||||||
|
_out_bb_date = _out_rec.check_in_date
|
||||||
|
if hasattr(_out_bb_date, 'date'):
|
||||||
|
_out_bb_date = _out_bb_date.date()
|
||||||
|
if _out_bb_date <= date_obj.date():
|
||||||
|
continue # orphan — skip
|
||||||
pairs.append({
|
pairs.append({
|
||||||
'check_in': record_info[i]['record'],
|
'check_in': record_info[i]['record'],
|
||||||
'check_out': record_info[j]['record'],
|
'check_out': record_info[j]['record'],
|
||||||
|
|||||||
Reference in New Issue
Block a user