diff --git a/app/modules/expense/router.py b/app/modules/expense/router.py
index c17ea0c..f9e48ef 100644
--- a/app/modules/expense/router.py
+++ b/app/modules/expense/router.py
@@ -472,13 +472,33 @@ async def approved_attachments_zip(
year, mon, raw = _parse_month(request)
atts = store.approved_attachments(year=year, month=mon)
names = _name_map(request)
+ upload_root = _upload_dir(request)
+ def _resolve(a: dict[str, Any]) -> Path | None:
+ """stored_path 우선, 없으면 현재 업로드 루트/item_id/파일명 으로 폴백."""
+ raw_path = a.get("stored_path") or ""
+ p = Path(raw_path)
+ if p.exists():
+ return p
+ # DATA_DIR 변경 등으로 절대경로가 깨진 경우 — basename 으로 재구성
+ item_id = a.get("item_id", "")
+ base = os.path.basename(raw_path) or a.get("filename", "")
+ if item_id and base:
+ alt = upload_root / item_id / base
+ if alt.exists():
+ return alt
+ return None
+
+ total = len(atts)
+ added = 0
+ missing = 0
buf = io.BytesIO()
used: dict[str, int] = {}
with zipfile.ZipFile(buf, "w", zipfile.ZIP_DEFLATED) as zf:
for a in atts:
- path = Path(a.get("stored_path", ""))
- if not path.exists():
+ path = _resolve(a)
+ if path is None:
+ missing += 1
continue
nm = _display_name(a.get("owner", ""), names)
fname = _att_download_name(a.get("uploaded_at"), nm, a.get("filename", ""))
@@ -491,12 +511,17 @@ async def approved_attachments_zip(
used[fname] = 1
arcname = fname
zf.write(str(path), arcname=arcname)
+ added += 1
buf.seek(0)
zipname = f"expense_approved_{raw}.zip"
return StreamingResponse(
buf,
media_type="application/zip",
- headers={"Content-Disposition": f'attachment; filename="{zipname}"'},
+ headers={
+ "Content-Disposition": f'attachment; filename="{zipname}"',
+ # 진단용: 첨부 총개수 / 담긴 개수 / 디스크에서 못 찾은 개수
+ "X-Attachment-Stats": f"total={total};added={added};missing={missing}",
+ },
)
diff --git a/app/modules/expense/templates/expense/approved.html b/app/modules/expense/templates/expense/approved.html
index 97a7e0a..ad18ad1 100644
--- a/app/modules/expense/templates/expense/approved.html
+++ b/app/modules/expense/templates/expense/approved.html
@@ -11,7 +11,7 @@
엑셀 다운
- 첨부 일괄(zip)
+ 전부 다운로드(zip)