Updated time attendance exporting function
This commit is contained in:
@@ -7432,7 +7432,7 @@ def export_time_attendance_excel(records, project_name_for_filename, date_range_
|
|||||||
|
|
||||||
# Setup styles
|
# Setup styles
|
||||||
header_font = Font(name='Arial', size=11, bold=True, color='FFFFFF')
|
header_font = Font(name='Arial', size=11, bold=True, color='FFFFFF')
|
||||||
header_fill = PatternFill(start_color='366092', end_color='366092', fill_type='solid')
|
header_fill = PatternFill(start_color='000000', end_color='000000', fill_type='solid')
|
||||||
data_font = Font(name='Arial', size=10)
|
data_font = Font(name='Arial', size=10)
|
||||||
bold_font = Font(name='Arial', size=10, bold=True)
|
bold_font = Font(name='Arial', size=10, bold=True)
|
||||||
border = Border(
|
border = Border(
|
||||||
@@ -7631,19 +7631,36 @@ def export_time_attendance_excel(records, project_name_for_filename, date_range_
|
|||||||
all_records_for_day = []
|
all_records_for_day = []
|
||||||
for loc_data in date_locations.values():
|
for loc_data in date_locations.values():
|
||||||
all_records_for_day.extend(loc_data['records'])
|
all_records_for_day.extend(loc_data['records'])
|
||||||
|
|
||||||
if len(all_records_for_day) >= 2:
|
if len(all_records_for_day) >= 2:
|
||||||
sorted_all_records = sorted(all_records_for_day, key=lambda x: x.check_in_time)
|
sorted_all_records = sorted(all_records_for_day, key=lambda x: x.check_in_time)
|
||||||
first_time = sorted_all_records[0].check_in_time
|
|
||||||
last_time = sorted_all_records[-1].check_in_time
|
|
||||||
|
|
||||||
first_datetime = datetime.combine(date_obj, first_time)
|
# NEW: Check if all records are IN or all OUT
|
||||||
last_datetime = datetime.combine(date_obj, last_time)
|
record_types = []
|
||||||
calculated_hours = (last_datetime - first_datetime).total_seconds() / 3600.0
|
for record in sorted_all_records:
|
||||||
calculated_hours = round(calculated_hours, 2)
|
action_desc = record.action_description.lower() if record.action_description else ''
|
||||||
|
if 'out' in action_desc or 'checkout' in action_desc:
|
||||||
|
record_types.append('OUT')
|
||||||
|
else:
|
||||||
|
record_types.append('IN')
|
||||||
|
|
||||||
weekly_total_hours += calculated_hours
|
# If all same type (all IN or all OUT), hours = 0
|
||||||
total_hours = calculated_hours
|
if len(set(record_types)) == 1:
|
||||||
|
print(f"⚠️ {date_str}: All {len(sorted_all_records)} records are {record_types[0]} - 0 working hours")
|
||||||
|
calculated_hours = 0.0
|
||||||
|
total_hours = 0.0
|
||||||
|
else:
|
||||||
|
# Mixed IN/OUT - calculate from first to last
|
||||||
|
first_time = sorted_all_records[0].check_in_time
|
||||||
|
last_time = sorted_all_records[-1].check_in_time
|
||||||
|
|
||||||
|
first_datetime = datetime.combine(date_obj, first_time)
|
||||||
|
last_datetime = datetime.combine(date_obj, last_time)
|
||||||
|
calculated_hours = (last_datetime - first_datetime).total_seconds() / 3600.0
|
||||||
|
calculated_hours = round(calculated_hours, 2)
|
||||||
|
|
||||||
|
weekly_total_hours += calculated_hours
|
||||||
|
total_hours = calculated_hours
|
||||||
|
|
||||||
# Daily total display (only shown on last location's last row)
|
# Daily total display (only shown on last location's last row)
|
||||||
daily_total_display = round(total_hours, 2) if total_hours > 0 else ''
|
daily_total_display = round(total_hours, 2) if total_hours > 0 else ''
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ class SingleCheckInCalculator:
|
|||||||
'check_in_time': record_time,
|
'check_in_time': record_time,
|
||||||
'location_name': location,
|
'location_name': location,
|
||||||
'work_type': work_type,
|
'work_type': work_type,
|
||||||
|
'action_description': getattr(record, 'action_description', '') if hasattr(record, '__dict__') else record.get('action_description', ''),
|
||||||
'timestamp': datetime.combine(record_date, record_time) if record_date and record_time else datetime.now()
|
'timestamp': datetime.combine(record_date, record_time) if record_date and record_time else datetime.now()
|
||||||
}
|
}
|
||||||
records_by_type[work_type].append(processed_record)
|
records_by_type[work_type].append(processed_record)
|
||||||
@@ -228,6 +229,21 @@ class SingleCheckInCalculator:
|
|||||||
if len(sorted_records) == 1:
|
if len(sorted_records) == 1:
|
||||||
return 0.0, True
|
return 0.0, True
|
||||||
|
|
||||||
|
# NEW: Check if all records are IN or all OUT using action_description
|
||||||
|
if len(sorted_records) >= 2:
|
||||||
|
record_types = []
|
||||||
|
for record in sorted_records:
|
||||||
|
action_desc = record.get('action_description', '').lower()
|
||||||
|
if 'out' in action_desc or 'checkout' in action_desc:
|
||||||
|
record_types.append('OUT')
|
||||||
|
else:
|
||||||
|
record_types.append('IN')
|
||||||
|
|
||||||
|
# If all are IN or all are OUT, return 0 hours with miss punch
|
||||||
|
if len(set(record_types)) == 1:
|
||||||
|
print(f"⚠️ All {len(sorted_records)} records are {record_types[0]} - 0 working hours")
|
||||||
|
return 0.0, True
|
||||||
|
|
||||||
# Calculate complete pairs only
|
# Calculate complete pairs only
|
||||||
num_complete_pairs = len(sorted_records) // 2
|
num_complete_pairs = len(sorted_records) // 2
|
||||||
total_hours = 0.0
|
total_hours = 0.0
|
||||||
@@ -238,21 +254,14 @@ class SingleCheckInCalculator:
|
|||||||
end_time = sorted_records[i + 1]['timestamp']
|
end_time = sorted_records[i + 1]['timestamp']
|
||||||
|
|
||||||
duration = end_time - start_time
|
duration = end_time - start_time
|
||||||
period_hours = duration.total_seconds() / 3600.0
|
hours = duration.total_seconds() / 3600.0
|
||||||
|
total_hours += hours
|
||||||
# Validate reasonable work period
|
|
||||||
if 0 < period_hours <= self.max_work_period_hours:
|
|
||||||
total_hours += period_hours
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"⚠️ Error calculating work period: {e}")
|
print(f"⚠️ Error calculating pair hours: {e}")
|
||||||
continue
|
return 0.0, True
|
||||||
|
|
||||||
# Round to nearest quarter hour
|
# Odd number of records = miss punch
|
||||||
total_hours = round(total_hours * 4) / 4
|
is_miss_punch = (len(sorted_records) % 2 != 0)
|
||||||
|
|
||||||
# Determine if miss punch
|
|
||||||
is_miss_punch = len(sorted_records) % 2 != 0
|
|
||||||
|
|
||||||
return total_hours, is_miss_punch
|
return total_hours, is_miss_punch
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user