159 lines
5.6 KiB
Python
159 lines
5.6 KiB
Python
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())
|