Files
njts-accounting-core/backend/app/routers/journal_entries.py
FNOS-WIN11ENT\choshoukaku 87fa1aaa1f 修正
2025-12-18 15:30:39 +09:00

213 lines
6.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from fastapi import APIRouter, HTTPException
from app.core.database import get_connection
from pydantic import BaseModel
from typing import List
from decimal import Decimal
from datetime import date
router = APIRouter(prefix="/journal-entries", tags=["仕訳"])
class JournalLine(BaseModel):
account_id: int
debit: Decimal = Decimal("0")
credit: Decimal = Decimal("0")
class JournalEntryRequest(BaseModel):
entry_date: date
description: str
lines: List[JournalLine]
class JournalEntryUpdateRequest(BaseModel):
description: str
lines: List[JournalLine]
updated_reason: str
class JournalEntryDeleteRequest(BaseModel):
deleted_reason: str
@router.post("", summary="仕訳登録")
def create_journal_entry(req: JournalEntryRequest):
from app.utils.month_lock import is_month_locked
if is_month_locked(req.entry_date):
raise HTTPException(
status_code=400,
detail="该月份已锁定,不能新增仕訳"
)
if not req.lines:
raise HTTPException(status_code=400, detail="仕訳明細不能为空")
# (可选)简单的借贷平衡校验
total_debit = sum(l.debit for l in req.lines)
total_credit = sum(l.credit for l in req.lines)
if total_debit != total_credit:
raise HTTPException(status_code=400, detail="借方和贷方不平衡")
fiscal_year = req.entry_date.year
with get_connection() as conn:
with conn.cursor() as cur:
# 仕訳ヘッダ
cur.execute("""
INSERT INTO journal_entries (
journal_date,
description,
fiscal_year,
version,
is_deleted,
created_by
)
VALUES (%s, %s, %s, 1, false, %s)
RETURNING journal_id
""", (
req.entry_date,
req.description,
fiscal_year,
"system" # 以后可以换成登录用户
))
entry_id = cur.fetchone()["journal_id"]
# 明細
for line in req.lines:
cur.execute("""
INSERT INTO journal_lines (
journal_id,
account_id,
debit,
credit
)
VALUES (%s, %s, %s, %s)
""", (
entry_id,
line.account_id,
line.debit,
line.credit
))
conn.commit()
return {
"status": "ok",
"journal_entry_id": entry_id
}
@router.put("/{journal_id}")
def update_journal_entry(journal_id: int, req: JournalEntryUpdateRequest):
from app.utils.month_lock import is_month_locked
with get_connection() as conn:
with conn.cursor() as cur:
# ① 取得现有仕訳
cur.execute("""
SELECT journal_date, is_deleted, version
FROM journal_entries
WHERE journal_id = %s
""", (journal_id,))
row = cur.fetchone()
if not row:
raise HTTPException(status_code=404, detail="仕訳が存在しません")
if row["is_deleted"]:
raise HTTPException(status_code=400, detail="削除済み仕訳は更新できません")
journal_date = row["journal_date"]
# 借贷平衡校验update
total_debit = sum(l.debit for l in req.lines)
total_credit = sum(l.credit for l in req.lines)
if total_debit != total_credit:
raise HTTPException(status_code=400, detail="借方和贷方不平衡")
# ② 月次锁定检查
if is_month_locked(journal_date):
raise HTTPException(status_code=400, detail="该月份已锁定,不能修改仕訳")
# ③ 更新 header
cur.execute("""
UPDATE journal_entries
SET description = %s,
version = version + 1,
updated_at = NOW(),
updated_reason = %s
WHERE journal_id = %s
""", (
req.description,
req.updated_reason,
journal_id
))
# ④ 删除原有明细物理删除OK
cur.execute("""
DELETE FROM journal_lines
WHERE journal_id = %s
""", (journal_id,))
# ⑤ 插入新明细
for line in req.lines:
cur.execute("""
INSERT INTO journal_lines (
journal_id,
account_id,
debit,
credit
) VALUES (%s, %s, %s, %s)
""", (
journal_id,
line.account_id,
line.debit,
line.credit
))
conn.commit()
return {
"status": "ok",
"journal_id": journal_id,
"message": "仕訳を更新しました"
}
@router.delete("/{journal_id}", summary="仕訳删除(软删除)")
def delete_journal_entry(journal_id: int, req: JournalEntryDeleteRequest):
with get_connection() as conn:
with conn.cursor() as cur:
cur.execute("""
UPDATE journal_entries
SET is_deleted = true,
deleted_at = NOW(),
deleted_reason = %s
WHERE journal_id = %s
AND is_deleted = false
RETURNING journal_id
""", (
req.deleted_reason,
journal_id
))
row = cur.fetchone()
if not row:
raise HTTPException(
status_code=404,
detail="仕訳不存在,或已被删除"
)
conn.commit()
return {
"status": "deleted",
"journal_entry_id": journal_id
}