merge: integrate advisor capabilities on latest qyqy base

This commit is contained in:
Windows
2026-09-11 21:03:58 +08:00
109 changed files with 10709 additions and 49 deletions
+284
View File
@@ -0,0 +1,284 @@
"""ORM mappings for additive advisory product reference tables.
The immutable ``fin_product`` catalogue remains mapped by ``FundProduct`` in
``app.model.fund``. This module only maps the new advisory evidence tables.
"""
from datetime import date, datetime
from decimal import Decimal
from typing import Any
from sqlalchemy import JSON, BigInteger, Date, DateTime, Numeric, String, Text
from sqlalchemy.orm import Mapped, mapped_column
from app.model.base import Base
class AdvisorProductIndustryExposure(Base):
__tablename__ = "advisor_product_industry_exposure"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
product_id: Mapped[int] = mapped_column(BigInteger, nullable=False)
industry_code: Mapped[str] = mapped_column(String(32), nullable=False)
industry_name: Mapped[str] = mapped_column(String(128), nullable=False)
exposure_weight_pct: Mapped[Decimal] = mapped_column(Numeric(7, 4), nullable=False)
as_of_date: Mapped[date] = mapped_column(Date, nullable=False)
source: Mapped[str] = mapped_column(String(64), nullable=False)
status: Mapped[str] = mapped_column(String(16), nullable=False)
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
class AdvisorProductReferenceSnapshot(Base):
__tablename__ = "advisor_product_reference_snapshot"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
product_id: Mapped[int] = mapped_column(BigInteger, nullable=False)
as_of_date: Mapped[date] = mapped_column(Date, nullable=False)
fund_type: Mapped[str] = mapped_column(String(64), nullable=False)
fund_asset_scale_billion: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
source: Mapped[str] = mapped_column(String(64), nullable=False)
risk_mapping_version: Mapped[str] = mapped_column(String(32), nullable=False)
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
class AdvisorProductPriceHistory(Base):
__tablename__ = "advisor_product_price_history"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
product_id: Mapped[int] = mapped_column(BigInteger, nullable=False)
trade_date: Mapped[date] = mapped_column(Date, nullable=False)
price_kind: Mapped[str] = mapped_column(String(16), nullable=False)
close_price: Mapped[Decimal] = mapped_column(Numeric(18, 6), nullable=False)
turnover_amount: Mapped[Decimal | None] = mapped_column(Numeric(24, 2))
source: Mapped[str] = mapped_column(String(64), nullable=False)
source_updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
class AdvisorProductMetricSnapshot(Base):
__tablename__ = "advisor_product_metric_snapshot"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
product_id: Mapped[int] = mapped_column(BigInteger, nullable=False)
as_of_date: Mapped[date] = mapped_column(Date, nullable=False)
trailing_20d_return_pct: Mapped[Decimal | None] = mapped_column(Numeric(10, 4))
trailing_120d_return_pct: Mapped[Decimal | None] = mapped_column(Numeric(10, 4))
annualized_volatility_pct: Mapped[Decimal | None] = mapped_column(Numeric(10, 4))
max_drawdown_pct: Mapped[Decimal | None] = mapped_column(Numeric(10, 4))
average_daily_turnover_amount: Mapped[Decimal | None] = mapped_column(Numeric(24, 2))
observation_count: Mapped[int] = mapped_column(BigInteger, nullable=False)
source: Mapped[str] = mapped_column(String(64), nullable=False)
calculation_version: Mapped[str] = mapped_column(String(16), nullable=False)
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
class AdvisorProductAssetClassification(Base):
__tablename__ = "advisor_product_asset_classification"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
product_id: Mapped[int] = mapped_column(BigInteger, nullable=False)
as_of_date: Mapped[date] = mapped_column(Date, nullable=False)
asset_class: Mapped[str] = mapped_column(String(32), nullable=False)
source: Mapped[str] = mapped_column(String(64), nullable=False)
status: Mapped[str] = mapped_column(String(16), nullable=False)
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
class AdvisorProductDataQualitySnapshot(Base):
__tablename__ = "advisor_product_data_quality_snapshot"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
product_id: Mapped[int] = mapped_column(BigInteger, nullable=False)
as_of_date: Mapped[date] = mapped_column(Date, nullable=False)
observation_count: Mapped[int] = mapped_column(BigInteger, nullable=False)
expected_trading_days: Mapped[int] = mapped_column(BigInteger, nullable=False)
price_coverage_pct: Mapped[Decimal] = mapped_column(Numeric(7, 4), nullable=False)
turnover_coverage_pct: Mapped[Decimal] = mapped_column(Numeric(7, 4), nullable=False)
max_abs_daily_return_pct: Mapped[Decimal | None] = mapped_column(Numeric(10, 4))
status: Mapped[str] = mapped_column(String(16), nullable=False)
reason_codes: Mapped[list[str]] = mapped_column(JSON, nullable=False)
rule_version: Mapped[str] = mapped_column(String(16), nullable=False)
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
class AdvisorAllocationBacktestRun(Base):
__tablename__ = "advisor_allocation_backtest_run"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
backtest_no: Mapped[str] = mapped_column(String(36), unique=True, nullable=False)
started_on: Mapped[date] = mapped_column(Date, nullable=False)
ended_on: Mapped[date] = mapped_column(Date, nullable=False)
profile_risk_level: Mapped[str] = mapped_column(String(8), nullable=False)
return_target_lower_pct: Mapped[Decimal] = mapped_column(Numeric(7, 4), nullable=False)
max_drawdown_pct: Mapped[Decimal] = mapped_column(Numeric(7, 4), nullable=False)
liquidity_requirement: Mapped[str] = mapped_column(String(32), nullable=False)
status: Mapped[str] = mapped_column(String(32), nullable=False)
observation_count: Mapped[int] = mapped_column(BigInteger, nullable=False)
static_total_return_pct: Mapped[Decimal | None] = mapped_column(Numeric(12, 4))
dynamic_total_return_pct: Mapped[Decimal | None] = mapped_column(Numeric(12, 4))
static_max_drawdown_pct: Mapped[Decimal | None] = mapped_column(Numeric(12, 4))
dynamic_max_drawdown_pct: Mapped[Decimal | None] = mapped_column(Numeric(12, 4))
dynamic_rebalance_count: Mapped[int] = mapped_column(BigInteger, nullable=False)
liquidity_history_coverage_pct: Mapped[Decimal] = mapped_column(Numeric(7, 4), nullable=False)
limitations: Mapped[list[str]] = mapped_column(JSON, nullable=False)
strategy_version: Mapped[str] = mapped_column(String(32), nullable=False)
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
class AdvisorProductSuitabilityReference(Base):
__tablename__ = "advisor_product_suitability_reference"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
product_id: Mapped[int] = mapped_column(BigInteger, nullable=False)
sales_institution: Mapped[str] = mapped_column(String(128), nullable=False)
risk_level: Mapped[str] = mapped_column(String(8), nullable=False)
effective_from: Mapped[date] = mapped_column(Date, nullable=False)
effective_until: Mapped[date | None] = mapped_column(Date)
source_url: Mapped[str] = mapped_column(String(1024), nullable=False)
document_title: Mapped[str] = mapped_column(String(256), nullable=False)
document_published_at: Mapped[date | None] = mapped_column(Date)
document_sha256: Mapped[str] = mapped_column(String(64), nullable=False)
source: Mapped[str] = mapped_column(String(64), nullable=False)
review_status: Mapped[str] = mapped_column(String(16), nullable=False)
verified_by: Mapped[str | None] = mapped_column(String(128))
verified_at: Mapped[datetime | None] = mapped_column(DateTime)
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
class AdvisorProductContractSnapshot(Base):
__tablename__ = "advisor_product_contract_snapshot"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
product_id: Mapped[int] = mapped_column(BigInteger, nullable=False)
effective_from: Mapped[date] = mapped_column(Date, nullable=False)
effective_until: Mapped[date | None] = mapped_column(Date)
fund_type: Mapped[str] = mapped_column(String(64), nullable=False)
investment_scope: Mapped[str] = mapped_column(Text, nullable=False)
performance_benchmark: Mapped[str | None] = mapped_column(String(256))
risk_return_characteristics: Mapped[str] = mapped_column(Text, nullable=False)
custodian_name: Mapped[str | None] = mapped_column(String(128))
management_fee_rate_pct: Mapped[Decimal | None] = mapped_column(Numeric(9, 6))
custodian_fee_rate_pct: Mapped[Decimal | None] = mapped_column(Numeric(9, 6))
inception_date: Mapped[date | None] = mapped_column(Date)
source_url: Mapped[str] = mapped_column(String(1024), nullable=False)
document_title: Mapped[str] = mapped_column(String(256), nullable=False)
document_published_at: Mapped[date | None] = mapped_column(Date)
document_sha256: Mapped[str] = mapped_column(String(64), nullable=False)
source: Mapped[str] = mapped_column(String(64), nullable=False)
review_status: Mapped[str] = mapped_column(String(16), nullable=False)
verified_by: Mapped[str | None] = mapped_column(String(128))
verified_at: Mapped[datetime | None] = mapped_column(DateTime)
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
class AdvisorProductGovernanceCandidate(Base):
__tablename__ = "advisor_product_governance_candidate_snapshot"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
product_id: Mapped[int] = mapped_column(BigInteger, nullable=False)
data_kind: Mapped[str] = mapped_column(String(16), nullable=False)
sales_institution: Mapped[str | None] = mapped_column(String(128))
observed_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
effective_from: Mapped[date] = mapped_column(Date, nullable=False)
risk_level: Mapped[str | None] = mapped_column(String(8))
payload: Mapped[dict[str, Any]] = mapped_column(JSON, nullable=False)
source_url: Mapped[str] = mapped_column(String(1024), nullable=False)
document_title: Mapped[str] = mapped_column(String(256), nullable=False)
document_published_at: Mapped[date | None] = mapped_column(Date)
document_sha256: Mapped[str] = mapped_column(String(64), nullable=False)
source: Mapped[str] = mapped_column(String(64), nullable=False)
review_status: Mapped[str] = mapped_column(String(16), nullable=False)
reviewed_by: Mapped[int | None] = mapped_column(BigInteger)
reviewed_at: Mapped[datetime | None] = mapped_column(DateTime)
review_comment: Mapped[str | None] = mapped_column(String(1000))
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
class AdvisorProductGovernanceSyncRun(Base):
__tablename__ = "advisor_product_governance_sync_run"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
run_no: Mapped[str] = mapped_column(String(36), nullable=False, unique=True)
source: Mapped[str] = mapped_column(String(64), nullable=False)
status: Mapped[str] = mapped_column(String(16), nullable=False)
product_count: Mapped[int] = mapped_column(BigInteger, nullable=False)
change_count: Mapped[int] = mapped_column(BigInteger, nullable=False)
error_count: Mapped[int] = mapped_column(BigInteger, nullable=False)
detail: Mapped[dict[str, Any]] = mapped_column(JSON, nullable=False)
started_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
completed_at: Mapped[datetime | None] = mapped_column(DateTime)
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
class AdvisorProductMarketQuoteSnapshot(Base):
__tablename__ = "advisor_product_market_quote_snapshot"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
product_id: Mapped[int] = mapped_column(BigInteger, nullable=False)
observed_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
last_price: Mapped[Decimal] = mapped_column(Numeric(18, 6), nullable=False)
previous_close: Mapped[Decimal | None] = mapped_column(Numeric(18, 6))
change_pct: Mapped[Decimal | None] = mapped_column(Numeric(10, 4))
volume: Mapped[Decimal | None] = mapped_column(Numeric(24, 4))
turnover_amount: Mapped[Decimal | None] = mapped_column(Numeric(24, 2))
quote_status: Mapped[str] = mapped_column(String(16), nullable=False)
source: Mapped[str] = mapped_column(String(64), nullable=False)
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
class AdvisorMarketQuoteSourceRun(Base):
__tablename__ = "advisor_market_quote_source_run"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
sync_run_no: Mapped[str] = mapped_column(String(36), nullable=False)
source: Mapped[str] = mapped_column(String(64), nullable=False)
priority_order: Mapped[int] = mapped_column(BigInteger, nullable=False)
status: Mapped[str] = mapped_column(String(16), nullable=False)
requested_count: Mapped[int] = mapped_column(BigInteger, nullable=False)
quote_count: Mapped[int] = mapped_column(BigInteger, nullable=False)
error_type: Mapped[str | None] = mapped_column(String(128))
started_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
completed_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
class AdvisorMarketQuoteSourceHealth(Base):
__tablename__ = "advisor_market_quote_source_health"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
source: Mapped[str] = mapped_column(String(64), nullable=False, unique=True)
status: Mapped[str] = mapped_column(String(16), nullable=False)
consecutive_failure_count: Mapped[int] = mapped_column(BigInteger, nullable=False)
last_success_at: Mapped[datetime | None] = mapped_column(DateTime)
last_failure_at: Mapped[datetime | None] = mapped_column(DateTime)
last_error_type: Mapped[str | None] = mapped_column(String(128))
last_requested_count: Mapped[int] = mapped_column(BigInteger, nullable=False)
last_quote_count: Mapped[int] = mapped_column(BigInteger, nullable=False)
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
class AdvisorMarketQuoteAlert(Base):
__tablename__ = "advisor_market_quote_alert"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
alert_no: Mapped[str] = mapped_column(String(36), nullable=False, unique=True)
source: Mapped[str] = mapped_column(String(64), nullable=False)
alert_type: Mapped[str] = mapped_column(String(64), nullable=False)
severity: Mapped[str] = mapped_column(String(16), nullable=False)
status: Mapped[str] = mapped_column(String(16), nullable=False)
occurrence_count: Mapped[int] = mapped_column(BigInteger, nullable=False)
detail: Mapped[dict[str, Any]] = mapped_column(JSON, nullable=False)
first_observed_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
last_observed_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
resolved_at: Mapped[datetime | None] = mapped_column(DateTime)
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
+26
View File
@@ -0,0 +1,26 @@
"""Internal persistence model for conversational investment-goal extraction."""
from datetime import datetime
from typing import Any
from sqlalchemy import JSON, BigInteger, DateTime, String
from sqlalchemy.orm import Mapped, mapped_column
from app.model.base import Base
class AdvisorGoalConversationExtraction(Base):
"""Append-only extraction snapshot; never exposed in the customer API."""
__tablename__ = "advisor_goal_conversation_extraction"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
message_id: Mapped[int] = mapped_column(BigInteger, unique=True, nullable=False)
session_id: Mapped[str] = mapped_column(String(64), nullable=False)
customer_id: Mapped[int] = mapped_column(BigInteger, nullable=False)
extraction_version: Mapped[str] = mapped_column(String(32), nullable=False)
extracted_fields: Mapped[dict[str, Any]] = mapped_column(JSON, nullable=False)
missing_fields: Mapped[list[str]] = mapped_column(JSON, nullable=False)
status: Mapped[str] = mapped_column(String(32), nullable=False)
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
+53
View File
@@ -0,0 +1,53 @@
"""Persistence models for goals and their client-facing goal books."""
from datetime import datetime
from decimal import Decimal
from typing import Any
from sqlalchemy import JSON, BigInteger, DateTime, Integer, Numeric, String, Text
from sqlalchemy.orm import Mapped, mapped_column
from app.model.base import Base
class ClientFacingContent(Base):
"""Unchanged baseline content-review resource used by goal books."""
__tablename__ = "client_facing_content"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
customer_id: Mapped[int] = mapped_column(BigInteger, nullable=False)
content_type: Mapped[str] = mapped_column(String(32), nullable=False)
draft_content: Mapped[dict[str, Any]] = mapped_column(JSON, nullable=False)
generated_by_portal: Mapped[str] = mapped_column(String(32), nullable=False)
review_status: Mapped[str] = mapped_column(String(16), nullable=False)
reviewer_user_id: Mapped[int | None] = mapped_column(BigInteger)
reviewed_at: Mapped[datetime | None] = mapped_column(DateTime)
published_at: Mapped[datetime | None] = mapped_column(DateTime)
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
class AdvisorInvestmentGoal(Base):
"""Collected goal version; only a confirmed version feeds advisory tools."""
__tablename__ = "advisor_investment_goal"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
goal_no: Mapped[str] = mapped_column(String(36), unique=True, nullable=False)
customer_id: Mapped[int] = mapped_column(BigInteger, nullable=False)
status: Mapped[str] = mapped_column(String(24), nullable=False)
annualized_return_lower_pct: Mapped[Decimal] = mapped_column(Numeric(7, 4), nullable=False)
annualized_return_upper_pct: Mapped[Decimal] = mapped_column(Numeric(7, 4), nullable=False)
max_drawdown_pct: Mapped[Decimal] = mapped_column(Numeric(7, 4), nullable=False)
liquidity_requirement: Mapped[str] = mapped_column(String(32), nullable=False)
investment_horizon_months: Mapped[int] = mapped_column(Integer, nullable=False)
benchmark_name: Mapped[str] = mapped_column(String(128), nullable=False)
notes: Mapped[str | None] = mapped_column(Text)
source: Mapped[str] = mapped_column(String(16), nullable=False)
goal_book_content_id: Mapped[int] = mapped_column(BigInteger, nullable=False, unique=True)
created_by: Mapped[int] = mapped_column(BigInteger, nullable=False)
confirmed_by: Mapped[int | None] = mapped_column(BigInteger)
confirmed_at: Mapped[datetime | None] = mapped_column(DateTime)
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
+57
View File
@@ -0,0 +1,57 @@
"""Append-only evidence and review records for internal customer-profile tags."""
from datetime import datetime
from decimal import Decimal
from typing import Any
from sqlalchemy import JSON, BigInteger, DateTime, Numeric, String
from sqlalchemy.orm import Mapped, mapped_column
from app.model.base import Base
class AdvisorProfileDriftReview(Base):
"""A candidate profile held for review when its tags materially drift."""
__tablename__ = "advisor_profile_drift_review"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
drift_no: Mapped[str] = mapped_column(String(36), nullable=False, unique=True)
customer_id: Mapped[int] = mapped_column(BigInteger, nullable=False)
source_assessment_id: Mapped[int] = mapped_column(BigInteger, nullable=False)
candidate_profile_uuid: Mapped[str] = mapped_column(String(36), nullable=False, unique=True)
candidate_profile_version: Mapped[int] = mapped_column(BigInteger, nullable=False)
candidate_snapshot: Mapped[dict[str, Any]] = mapped_column(JSON, nullable=False)
candidate_generation_basis: Mapped[dict[str, Any]] = mapped_column(JSON, nullable=False)
changed_tags: Mapped[list[dict[str, Any]]] = mapped_column(JSON, nullable=False)
status: Mapped[str] = mapped_column(String(16), nullable=False)
reviewer_user_id: Mapped[int | None] = mapped_column(BigInteger)
reviewed_at: Mapped[datetime | None] = mapped_column(DateTime)
review_comment: Mapped[str | None] = mapped_column(String(1000))
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
class AdvisorProfileTag(Base):
"""A tag value with evidence lineage. Exactly one active row exists per tag key."""
__tablename__ = "advisor_profile_tag"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
tag_uuid: Mapped[str] = mapped_column(String(36), nullable=False, unique=True)
customer_id: Mapped[int] = mapped_column(BigInteger, nullable=False)
tag_key: Mapped[str] = mapped_column(String(64), nullable=False)
tag_value: Mapped[Any] = mapped_column(JSON, nullable=False)
tag_value_hash: Mapped[str] = mapped_column(String(64), nullable=False)
confidence: Mapped[Decimal] = mapped_column(Numeric(5, 4), nullable=False)
source_type: Mapped[str] = mapped_column(String(32), nullable=False)
source_reference: Mapped[str] = mapped_column(String(128), nullable=False)
source_confidence: Mapped[Decimal] = mapped_column(Numeric(5, 4), nullable=False)
profile_version: Mapped[int] = mapped_column(BigInteger, nullable=False)
drift_review_id: Mapped[int | None] = mapped_column(BigInteger)
previous_tag_id: Mapped[int | None] = mapped_column(BigInteger)
drift_reason: Mapped[str | None] = mapped_column(String(32))
status: Mapped[str] = mapped_column(String(16), nullable=False)
active_customer_tag: Mapped[str | None] = mapped_column(String(160), unique=True)
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
+30
View File
@@ -0,0 +1,30 @@
"""Additive projection model for opening-risk questionnaire profiles."""
from datetime import datetime
from typing import Any
from sqlalchemy import JSON, BigInteger, DateTime, String
from sqlalchemy.orm import Mapped, mapped_column
from app.model.base import Base
from app.model.fund import FundRiskAssessment as RiskAssessment
class ProfileSnapshot(Base):
__tablename__ = "profile_snapshots"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
profile_uuid: Mapped[str | None] = mapped_column(String(36), unique=True)
customer_id: Mapped[int] = mapped_column(BigInteger, nullable=False)
version: Mapped[int] = mapped_column(BigInteger, nullable=False)
snapshot: Mapped[dict[str, Any]] = mapped_column(JSON, nullable=False)
generation_basis: Mapped[dict[str, Any] | None] = mapped_column(JSON)
snapshot_hash: Mapped[str | None] = mapped_column(String(64))
is_current: Mapped[bool] = mapped_column(nullable=False, default=False)
current_customer_id: Mapped[int | None] = mapped_column(BigInteger)
generated_at: Mapped[datetime | None] = mapped_column(DateTime)
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
__all__ = ["ProfileSnapshot", "RiskAssessment"]