March 16 2026: fix export overnight calculation issue

This commit is contained in:
2026-03-16 18:11:40 -04:00
parent d129d46b08
commit 28194fc638
+82 -2
View File
@@ -9870,6 +9870,16 @@ def export_time_attendance_excel(records, project_name_for_filename, date_range_
if _out_t_d.hour <= 3:
if _in_t_d.hour < 18:
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:
continue
_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_in_t.hour < 18:
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:
continue
_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 _in_t.hour < 18:
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:
continue # same-day OUT must be strictly after IN
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 _in_t2.hour < 18:
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:
continue
# 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]
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:
ri['used'] = True
pairs.append({'check_in': ri['record'], 'check_out': None, 'is_miss_punch': True})
if earliest_out is not None:
latest_in['used'] = True
earliest_out['used'] = True
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
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']:
continue
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({
'check_in': record_info[i]['record'],
'check_out': record_info[j]['record'],