Watch
1
0
Fork
You've already forked Seyyed_arc
0
forked from hesabix/arc
Seyyed_arc/hesabixAPI/app/services/fx_data_quality_service.py

144 lines
5.5 KiB
Python

"""کیفیت داده چندارزی: خطوط سند بدون معادل پایه."""
from __future__ import annotations
from datetime import date, datetime
from typing import Any, Dict, List, Optional
from sqlalchemy import and_, or_
from sqlalchemy.orm import Session
from adapters.db.models.business import Business
from adapters.db.models.currency import Currency
from adapters.db.models.document import Document
from adapters.db.models.document_line import DocumentLine
from adapters.db.models.fiscal_year import FiscalYear
from app.services.fx_rate_provider_service import business_is_multi_currency
def _parse_date(value: Optional[str]) -> Optional[date]:
if not value:
return None
try:
return datetime.strptime(str(value)[:10], "%Y-%m-%d").date()
except Exception:
return None
def get_missing_base_amount_warnings(
db: Session,
business_id: int,
*,
fiscal_year_id: Optional[int] = None,
date_from: Optional[str] = None,
date_to: Optional[str] = None,
sample_limit: int = 20,
) -> Dict[str, Any]:
"""
هشدار حسابداری: اسناد ارزی که debit_base/credit_base ندارند.
بدون معادل پایه، تجمیع «همه ارزها → پایه» ناقص یا گمراه‌کننده است.
"""
is_mc = business_is_multi_currency(db, business_id)
biz = db.get(Business, business_id)
base_id = int(biz.default_currency_id) if biz and biz.default_currency_id else None
base_cur = db.get(Currency, base_id) if base_id else None
empty = {
"is_multi_currency": is_mc,
"base_currency_id": base_id,
"base_currency_code": getattr(base_cur, "code", None),
"missing_line_count": 0,
"affected_document_count": 0,
"severity": "ok",
"samples": [],
"message_fa": "مشکل کیفیت داده چندارزی یافت نشد.",
"message_en": "No multi-currency base-amount data quality issues found.",
}
if not is_mc or base_id is None:
empty["severity"] = "n/a"
empty["message_fa"] = "کسب‌وکار تک‌ارزی است یا ارز پایه تعریف نشده."
return empty
fy = None
if fiscal_year_id:
fy = db.query(FiscalYear).filter(FiscalYear.id == fiscal_year_id).first()
if fy is None:
fy = (
db.query(FiscalYear)
.filter(FiscalYear.business_id == business_id, FiscalYear.is_last == True) # noqa: E712
.first()
)
date_from_obj = _parse_date(date_from) or (fy.start_date if fy else None)
date_to_obj = _parse_date(date_to) or (fy.end_date if fy and fy.end_date else date.today())
q = (
db.query(DocumentLine, Document)
.join(Document, DocumentLine.document_id == Document.id)
.filter(
and_(
Document.business_id == business_id,
Document.is_proforma == False, # noqa: E712
Document.currency_id.isnot(None),
Document.currency_id != base_id,
or_(
and_(DocumentLine.debit.isnot(None), DocumentLine.debit != 0, DocumentLine.debit_base.is_(None)),
and_(DocumentLine.credit.isnot(None), DocumentLine.credit != 0, DocumentLine.credit_base.is_(None)),
),
)
)
)
if fy:
q = q.filter(Document.fiscal_year_id == fy.id)
if date_from_obj:
q = q.filter(Document.document_date >= date_from_obj)
if date_to_obj:
q = q.filter(Document.document_date <= date_to_obj)
rows = q.order_by(Document.document_date.desc(), Document.id.desc()).all()
doc_ids = {int(doc.id) for _line, doc in rows}
samples: List[Dict[str, Any]] = []
for line, doc in rows[: max(0, int(sample_limit))]:
samples.append(
{
"document_id": doc.id,
"document_code": doc.code or "",
"document_date": doc.document_date.isoformat() if doc.document_date else None,
"document_currency_id": doc.currency_id,
"line_id": line.id,
"account_id": line.account_id,
"debit": float(line.debit or 0),
"credit": float(line.credit or 0),
"debit_base": float(line.debit_base) if line.debit_base is not None else None,
"credit_base": float(line.credit_base) if line.credit_base is not None else None,
}
)
missing = len(rows)
severity = "ok"
if missing > 0:
severity = "warning" if missing < 50 else "high"
message_fa = (
"مشکل کیفیت داده چندارزی یافت نشد."
if missing == 0
else f"{missing} خط سند ارزی بدون معادل پایه (*_base) یافت شد؛ گزارش‌های «همه ارزها» ممکن است ناقص باشند."
)
return {
"is_multi_currency": True,
"base_currency_id": base_id,
"base_currency_code": getattr(base_cur, "code", None),
"missing_line_count": missing,
"affected_document_count": len(doc_ids),
"severity": severity,
"samples": samples,
"date_from": date_from_obj.isoformat() if date_from_obj else None,
"date_to": date_to_obj.isoformat() if date_to_obj else None,
"fiscal_year_id": fy.id if fy else None,
"message_fa": message_fa,
"message_en": (
"No issues."
if missing == 0
else f"{missing} foreign-currency lines lack base equivalents."
),
}