""" =============================================================================== FILE: services/reports/calculators.py ROLE: Расчет баланса рабочего времени, обеденного перерыва и отклонений от нормы. =============================================================================== """ import pandas as pd def calculate_deviation(time_in_building_str, reason="", norm_hours=8, lunch_minutes=30): """ Расчет отклонения от нормы. Для удаленщиков при наличии физического времени в здании вычисляется реальное отклонение. """ reason_clean = str(reason).strip().lower() if pd.notna(reason) else "" is_remote = "удален" in reason_clean or "дистанцион" in reason_clean has_building_time = isinstance(time_in_building_str, str) and time_in_building_str not in ['00:00', '0', '', 'None', 'nan', 'NaN'] # Если есть уважительная причина (больничный, отпуск, командировка и т.д.) не удаленка if reason_clean != "" and not is_remote: return "0:00" # Если удаленщик работал исключительно из дома (00:00 в здании) if is_remote and not has_building_time: return "0:00" # Если сотрудника не было в здании и нет уважительной причины if not has_building_time: return f"-{norm_hours}:00" try: parts = time_in_building_str.strip().split(':') hh = int(parts[0]) mm = int(parts[1]) if len(parts) > 1 else 0 total_in_building_minutes = hh * 60 + mm if total_in_building_minutes == 0: return "0:00" if is_remote else f"-{norm_hours}:00" work_minutes = max(0, total_in_building_minutes - lunch_minutes) norm_minutes = norm_hours * 60 diff = work_minutes - norm_minutes if diff == 0: return "0:00" sign = "-" if diff < 0 else "" abs_diff = abs(diff) res_hh = abs_diff // 60 res_mm = abs_diff % 60 return f"{sign}{res_hh}:{res_mm:02d}" except Exception: return "0:00" if is_remote else f"-{norm_hours}:00"