from __future__ import annotations import argparse import json import sys from datetime import datetime from pathlib import Path from typing import Any from openpyxl import load_workbook from sqlalchemy import create_engine, text sys.path.insert(0, str(Path(__file__).resolve().parents[1])) from runtime_config import DB_PATH from wehago_compare import activate_erp_voucher_existence_snapshot, detect_file_kind, import_voucher_rows from scripts.redownload_and_fix_hanmac_ledger_account import upsert_fix_source_file def import_voucher_file( db_path: Path, source_path: Path, *, year_hint: int | None = None, activate_existence_snapshot: bool = False, ) -> dict[str, Any]: file_kind, header, sheet_name = detect_file_kind(source_path) if file_kind != "voucher": raise ValueError(f"전표내역조회 형식이 아닙니다: {source_path} ({file_kind})") workbook = load_workbook(source_path, read_only=True, data_only=True) engine = create_engine(f"sqlite:///{db_path}", connect_args={"check_same_thread": False}) try: sheet = workbook.worksheets[0] with engine.begin() as conn: conn.execute(text("PRAGMA busy_timeout = 300000")) source_id, _changed = upsert_fix_source_file( conn, source_path.resolve(), file_kind, int(year_hint) if year_hint else None, sheet_name, header, ) deleted = int( conn.execute( text("DELETE FROM wehago_voucher_rows WHERE source_file_id = :source_id"), {"source_id": source_id}, ).rowcount or 0 ) inserted = import_voucher_rows( conn, source_id, sheet_name, sheet.iter_rows(min_row=2, values_only=True), int(year_hint) if year_hint else None, ) conn.execute( text( """ UPDATE wehago_source_files SET row_count = :row_count, imported_at = CURRENT_TIMESTAMP WHERE id = :source_id """ ), {"row_count": inserted, "source_id": source_id}, ) existence_snapshot = None if activate_existence_snapshot: snapshot_year = int(year_hint or 0) if snapshot_year <= 0: year_values = [ int(row["fiscal_year"]) for row in conn.execute( text( """ SELECT DISTINCT fiscal_year FROM wehago_voucher_rows WHERE source_file_id = :source_id AND fiscal_year IS NOT NULL ORDER BY fiscal_year """ ), {"source_id": source_id}, ).mappings() if int(row["fiscal_year"] or 0) > 0 ] if len(year_values) == 1: snapshot_year = year_values[0] if snapshot_year <= 0: raise ValueError("--activate-existence-snapshot requires --year-hint or a single fiscal year in the file.") existence_snapshot = activate_erp_voucher_existence_snapshot( conn, snapshot_year, source_file_id=source_id, source_label=source_path.name, snapshot_mode="full", ) year_rows = [ dict(row) for row in conn.execute( text( """ SELECT fiscal_year, COUNT(*) AS row_count FROM wehago_voucher_rows WHERE source_file_id = :source_id GROUP BY fiscal_year ORDER BY fiscal_year """ ), {"source_id": source_id}, ).mappings() ] finally: workbook.close() engine.dispose() return { "db_path": str(db_path), "source_path": str(source_path.resolve()), "source_id": source_id, "deleted_rows": deleted, "inserted_rows": inserted, "year_rows": year_rows, "existence_snapshot": existence_snapshot, "imported_at": datetime.now().isoformat(timespec="seconds"), } def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser(description="한맥 ERP 전표내역조회 엑셀을 WEHAGO 비교 DB의 voucher 테이블에 반영합니다.") parser.add_argument("source", type=Path) parser.add_argument("--db", type=Path, default=DB_PATH) parser.add_argument("--year-hint", type=int) parser.add_argument( "--activate-existence-snapshot", action="store_true", help="이 파일을 해당 연도의 최신 ERP 전표 존재 목록으로 활성화합니다.", ) return parser.parse_args() def main() -> int: args = parse_args() result = import_voucher_file( args.db, args.source, year_hint=args.year_hint, activate_existence_snapshot=args.activate_existence_snapshot, ) print(json.dumps(result, ensure_ascii=False, indent=2)) return 0 if __name__ == "__main__": raise SystemExit(main())