138 lines
5.1 KiB
Python
138 lines
5.1 KiB
Python
"""R10 适配器:收入与成本跨期匹配。
|
|
|
|
源明细:SrcRevenueRecognition / SrcCostAmortization
|
|
映射到:MetricEvent(收入确认/成本摊销时序) + Entity(CONTRACT) 关联补强
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import datetime as dt
|
|
import uuid
|
|
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.datahub.graph_repo import upsert_entity
|
|
from app.datahub.models import MetricEvent
|
|
from app.datahub.ontology import EntityType
|
|
from app.datahub.staging import SrcCostAmortization, SrcRevenueRecognition
|
|
from app.ingest.base import BaseAdapter, IngestResult
|
|
from app.ingest.registry import register_adapter
|
|
|
|
|
|
@register_adapter
|
|
class RevenueRecognitionAdapter(BaseAdapter):
|
|
"""SrcRevenueRecognition → MetricEvent(收入确认时序)。"""
|
|
|
|
source_system = "FIN"
|
|
staging_table = "src_revenue_recognition"
|
|
|
|
def ingest(
|
|
self,
|
|
session: Session,
|
|
data_version_id: uuid.UUID | None = None,
|
|
batch_size: int = 1000,
|
|
) -> IngestResult:
|
|
result = IngestResult()
|
|
query = session.query(SrcRevenueRecognition)
|
|
if data_version_id:
|
|
query = query.filter(SrcRevenueRecognition.data_version_id == data_version_id)
|
|
rows = query.limit(batch_size).all()
|
|
|
|
for row in rows:
|
|
try:
|
|
# 确保合同实体存在
|
|
if row.contract_no:
|
|
upsert_entity(
|
|
session,
|
|
entity_type=EntityType.CONTRACT,
|
|
business_key=row.contract_no,
|
|
data_version_id=data_version_id,
|
|
)
|
|
|
|
if row.recognition_date:
|
|
event_time = dt.datetime.combine(
|
|
row.recognition_date, dt.time.min, tzinfo=dt.timezone.utc
|
|
)
|
|
event = MetricEvent(
|
|
event_time=event_time,
|
|
subject_type="contract",
|
|
subject_key=row.contract_no or row.voucher_no,
|
|
metric_name="revenue_recognition",
|
|
metric_value=row.recognition_amount,
|
|
attributes={
|
|
"voucher_no": row.voucher_no,
|
|
"billing_mode": row.billing_mode,
|
|
"period_start": str(row.period_start) if row.period_start else None,
|
|
"period_end": str(row.period_end) if row.period_end else None,
|
|
"prepaid_flag": row.prepaid_flag,
|
|
},
|
|
data_version_id=data_version_id,
|
|
)
|
|
session.add(event)
|
|
result.metric_events.append(event)
|
|
|
|
result.row_count += 1
|
|
except Exception:
|
|
result.error_count += 1
|
|
|
|
return result
|
|
|
|
|
|
@register_adapter
|
|
class CostAmortizationAdapter(BaseAdapter):
|
|
"""SrcCostAmortization → MetricEvent(成本摊销时序)。"""
|
|
|
|
source_system = "FIN"
|
|
staging_table = "src_cost_amortization"
|
|
|
|
def ingest(
|
|
self,
|
|
session: Session,
|
|
data_version_id: uuid.UUID | None = None,
|
|
batch_size: int = 1000,
|
|
) -> IngestResult:
|
|
result = IngestResult()
|
|
query = session.query(SrcCostAmortization)
|
|
if data_version_id:
|
|
query = query.filter(SrcCostAmortization.data_version_id == data_version_id)
|
|
rows = query.limit(batch_size).all()
|
|
|
|
for row in rows:
|
|
try:
|
|
if row.contract_no:
|
|
upsert_entity(
|
|
session,
|
|
entity_type=EntityType.CONTRACT,
|
|
business_key=row.contract_no,
|
|
data_version_id=data_version_id,
|
|
)
|
|
|
|
if row.amortization_date:
|
|
event_time = dt.datetime.combine(
|
|
row.amortization_date, dt.time.min, tzinfo=dt.timezone.utc
|
|
)
|
|
event = MetricEvent(
|
|
event_time=event_time,
|
|
subject_type="contract",
|
|
subject_key=row.contract_no or row.voucher_no,
|
|
metric_name="cost_amortization",
|
|
metric_value=row.amortization_amount,
|
|
attributes={
|
|
"voucher_no": row.voucher_no,
|
|
"cost_type": row.cost_type,
|
|
"total_periods": row.total_periods,
|
|
"current_period": row.current_period,
|
|
"delivery_date": str(row.delivery_date) if row.delivery_date else None,
|
|
"acceptance_date": str(row.acceptance_date) if row.acceptance_date else None,
|
|
},
|
|
data_version_id=data_version_id,
|
|
)
|
|
session.add(event)
|
|
result.metric_events.append(event)
|
|
|
|
result.row_count += 1
|
|
except Exception:
|
|
result.error_count += 1
|
|
|
|
return result
|