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

192 lines
6 KiB
Python

from __future__ import annotations
import json
import re
from datetime import date, datetime
from decimal import Decimal, InvalidOperation
from typing import Any, Dict, List, Optional, Tuple
import jdatetime
from adapters.api.v1.schemas import BusinessField, BusinessType
from app.services.legacy_import.constants import DEFAULT_LEGACY_PERSON_TYPE_ID_MAP
def normalize_server_url(url: str) -> str:
raw = (url or "").strip()
if not raw:
from app.services.legacy_import.constants import DEFAULT_LEGACY_SERVER_URL
return DEFAULT_LEGACY_SERVER_URL
if not raw.startswith(("http://", "https://")):
raw = f"https://{raw}"
return raw.rstrip("/")
def split_person_name(name: str | None) -> Tuple[str | None, str | None]:
if not name or not str(name).strip():
return None, None
parts = str(name).strip().split()
if len(parts) == 1:
return parts[0], None
return parts[0], " ".join(parts[1:])
def person_alias(nikename: Any, name: Any) -> str:
for val in (nikename, name):
if val and str(val).strip():
return str(val).strip()
return "شخص بدون نام"
def map_legacy_person_types(
type_ids: List[int] | None,
*,
type_id_to_label: Dict[int, str] | None = None,
) -> List[str]:
mapping = type_id_to_label or DEFAULT_LEGACY_PERSON_TYPE_ID_MAP
labels: List[str] = []
for tid in type_ids or []:
label = mapping.get(int(tid))
if label and label not in labels:
labels.append(label)
if not labels:
labels = ["مشتری"]
return labels
def safe_int(value: Any, default: int | None = None) -> int | None:
if value is None or value == "":
return default
try:
iv = int(value)
if iv > 2147483647 or iv < -2147483648:
return default
return iv
except (TypeError, ValueError):
return default
def safe_decimal(value: Any, default: Decimal = Decimal("0")) -> Decimal:
if value is None or value == "":
return default
try:
return Decimal(str(value).replace(",", ""))
except (InvalidOperation, ValueError):
return default
def parse_legacy_date(value: Any) -> date:
"""Parse Jalali YYYY/MM/DD, epoch seconds, or ISO date."""
if isinstance(value, date) and not isinstance(value, datetime):
return value
if isinstance(value, datetime):
return value.date()
if value is None:
return datetime.utcnow().date()
s = str(value).strip()
if not s:
return datetime.utcnow().date()
# Unix timestamp (legacy dateSubmit)
if s.isdigit() and len(s) >= 9:
try:
return datetime.utcfromtimestamp(int(s)).date()
except (OSError, ValueError, OverflowError):
pass
if len(s) == 10 and s.count("/") == 2:
parts = s.split("/")
try:
y, m, d = int(parts[0]), int(parts[1]), int(parts[2])
# سال‌های ۱۰۰۰–۱۷۹۹ در دادهٔ قدیمی معمولاً شمسی هستند؛ ۱۹۰۰+ میلادی
if y < 1800:
return jdatetime.date(y, m, d).togregorian()
return date(y, m, d)
except ValueError:
pass
try:
return datetime.fromisoformat(s.replace("Z", "+00:00")).date()
except ValueError:
return datetime.utcnow().date()
def epoch_to_date(value: Any) -> date | None:
if value is None or value == "":
return None
try:
return datetime.utcfromtimestamp(int(str(value).strip())).date()
except (OSError, ValueError, OverflowError, TypeError):
return None
def map_business_type(old_type: str | None) -> BusinessType:
mapping = {
"فروشگاه": BusinessType.STORE,
"مغازه": BusinessType.SHOP,
"شخصی": BusinessType.INDIVIDUAL,
"شرکت": BusinessType.COMPANY,
"موسسه": BusinessType.INSTITUTE,
"باشگاه": BusinessType.CLUB,
"اتحادیه": BusinessType.UNION,
}
if old_type and old_type in mapping:
return mapping[old_type]
return BusinessType.SHOP
def map_business_field(old_field: str | None) -> BusinessField:
if not old_field:
return BusinessField.OTHER
low = old_field.lower().strip()
if any(k in low for k in ("تولید", "ساخت")):
return BusinessField.MANUFACTURING
if any(k in low for k in ("بازرگانی", "فروش", "خرید", "تجارت")):
return BusinessField.COMMERCIAL
if any(k in low for k in ("خدمات", "خدماتی", "مشاوره", "آموزش")):
return BusinessField.SERVICE
return BusinessField.OTHER
def khadamat_to_item_type(khadamat: Any) -> str:
if khadamat in (True, 1, "1", "true"):
return "service"
return "product"
def legacy_money_code_to_currency_query(code: str | None) -> str:
c = (code or "IRR").strip().upper()
return c if c else "IRR"
def sanitize_business_name(name: str, *, suffix: str = "") -> str:
base = (name or "کسب‌وکار منتقل‌شده").strip()[:240]
if suffix and suffix not in base:
return f"{base}{suffix}"[:255]
return base[:255]
def person_types_json(types: List[str]) -> str:
return json.dumps(types, ensure_ascii=False)
def extract_archive_counts(data_files: Dict[str, list]) -> Dict[str, int]:
return {
"persons": len(data_files.get("persons.json") or []),
"commodities": len(data_files.get("commodities.json") or []),
"documents": len(data_files.get("hesabdari_docs.json") or []),
"document_rows": len(data_files.get("hesabdari_rows.json") or []),
"storerooms": len(data_files.get("storerooms.json") or []),
"storeroom_tickets": len(data_files.get("storeroom_tickets.json") or []),
"bank_accounts": len(data_files.get("bank_accounts.json") or []),
"fiscal_years": len(data_files.get("years.json") or []),
}
def mask_api_key(key: str) -> str:
k = (key or "").strip()
if len(k) <= 8:
return "****"
return f"{k[:4]}...{k[-4:]}"