diff --git a/mysql_preview_server.py b/mysql_preview_server.py index e0937b5..e262ffd 100644 --- a/mysql_preview_server.py +++ b/mysql_preview_server.py @@ -294,6 +294,7 @@ def init_db(conn): def ensure_db_schema(conn): try: init_db(conn) + cleanup_site_records_after_retire(conn) except sqlite3.OperationalError as error: if 'locked' in str(error).lower(): return False @@ -322,6 +323,61 @@ def _as_text(v): return str(v) +def _date_value(v): + m = re.match(r'^(\d{4})-(\d{2})-(\d{2})', _as_text(v).strip()) + if not m: + return None + if m.group(0) in ('0000-00-00',): + return None + try: + return datetime(int(m.group(1)), int(m.group(2)), int(m.group(3))).date() + except Exception: + return None + + +def _member_rows_for_site_work_day(name_to_members, kor_name, work_date): + # 사업관리 원본은 이름만 있어 재입사자가 같은 이름으로 중복 매칭될 수 있다. + # 같은 이름의 사번이 여러 개이면 작업일 기준으로 퇴사 전 사번/현재 사번을 나눠 붙인다. + members = name_to_members.get(_as_text(kor_name).strip()) or [] + if len(members) <= 1: + return members + day = _date_value(work_date) + if not day: + return members + retired_valid = [m for m in members if m.get('retireDate') and day <= m['retireDate']] + if retired_valid: + return retired_valid + return [m for m in members if not m.get('retireDate') or day <= m['retireDate']] + + +def cleanup_site_records_after_retire(conn): + # 이미 저장된 사업관리 기록 중 같은 이름의 현재 사번이 존재하는데 + # 퇴사 사번의 퇴사일 이후로 붙은 기록은 중복 집계 원인이므로 제거한다. + conn.execute( + ''' + DELETE FROM site_worksheet_record + WHERE rowid IN ( + SELECT s.rowid + FROM site_worksheet_record s + JOIN member oldm ON oldm.MemberNo = s.memberNo + WHERE IFNULL(oldm.retireFlag, '') NOT IN ('', '0000-00-00', '0000-00-00 00:00:00') + AND date(s.workDate) > date(substr(oldm.retireFlag, 1, 10)) + AND EXISTS ( + SELECT 1 + FROM member newm + WHERE newm.korName = oldm.korName + AND newm.MemberNo <> oldm.MemberNo + AND ( + IFNULL(newm.retireFlag, '') IN ('', '0000-00-00', '0000-00-00 00:00:00') + OR date(s.workDate) <= date(substr(newm.retireFlag, 1, 10)) + ) + ) + ) + ''' + ) + conn.commit() + + def _find_col_key(cols, aliases): if not cols: return '' @@ -1594,7 +1650,7 @@ def get_site_worksheet_records_by_days(conn, start_date, end_date, member_nos, r ph = ','.join(['?'] * len(member_nos)) people_cur = conn.execute( f''' - SELECT MemberNo, IFNULL(korName, '') AS korName + SELECT MemberNo, IFNULL(korName, '') AS korName, IFNULL(retireFlag, '') AS retireFlag FROM member WHERE MemberNo IN ({ph}) AND IFNULL(korName, '') <> '' @@ -1602,8 +1658,11 @@ def get_site_worksheet_records_by_days(conn, start_date, end_date, member_nos, r member_nos ) name_to_members = {} - for member_no, kor_name in people_cur.fetchall(): - name_to_members.setdefault(_as_text(kor_name).strip(), []).append(normalize_member_no(member_no)) + for member_no, kor_name, retire_flag in people_cur.fetchall(): + name_to_members.setdefault(_as_text(kor_name).strip(), []).append({ + 'memberNo': normalize_member_no(member_no), + 'retireDate': _date_value(retire_flag), + }) if not name_to_members: return {'rows': [], 'source': 'empty'} @@ -1772,8 +1831,9 @@ def get_site_worksheet_records_by_days(conn, start_date, end_date, member_nos, r found = 0 for kor_name, job_type, work_text, note, person_count in cur.fetchall(): found += 1 - matched_member_nos = name_to_members.get(_as_text(kor_name).strip()) or [] - for matched_member_no in matched_member_nos: + matched_members = _member_rows_for_site_work_day(name_to_members, kor_name, work_date) + for matched_member in matched_members: + matched_member_no = matched_member.get('memberNo') if isinstance(matched_member, dict) else matched_member conn.execute( ''' INSERT OR REPLACE INTO site_worksheet_record @@ -1926,6 +1986,7 @@ def get_site_worksheet_records_by_days(conn, start_date, end_date, member_nos, r if progress is not None: progress['phase'] = '달력 테이블 정리' conn.commit() + cleanup_site_records_after_retire(conn) rebuild_work_calendar_tables(conn) return { 'rows': _site_record_rows_from_cache_for_members(conn, start_date, end_date, member_nos), @@ -2147,6 +2208,7 @@ def get_member_site_worksheet_records(conn, start_date, end_date, member_no, ref if progress is not None: progress['phase'] = '달력 테이블 정리' conn.commit() + cleanup_site_records_after_retire(conn) rebuild_work_calendar_tables(conn) return {'rows': _site_record_rows_from_cache(conn, start_date, end_date, member_no), 'source': 'erp_incremental', 'added': len(out)} diff --git a/project-codes.html b/project-codes.html index 27547c6..21e0f10 100644 --- a/project-codes.html +++ b/project-codes.html @@ -540,8 +540,8 @@ - - + + @@ -662,6 +662,7 @@ const text = String(value || '').trim(); if (!text) return '-'; return text + .replace(/\+/g, '+\n') .replace(/~/g, '~\n') .replace(/=/g, '=\n') .replace(/\//g, '/\n')