TorbatYar/backend/services/accounting/app/models/fixed_assets.py
Mortezakoohjani 12c8615615 Ship enterprise Accounting FE/API with CRUD parity and production wiring.
Adds accounting-service PATCH/archive, fiscal helpers, COA templates and setup status, plus SuperApp Accounting UI (DS, scoreboard, masters, vouchers, ledger, ops modules) with session refresh and HTTPS public API URLs.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-24 15:26:43 +03:30

172 lines
8.9 KiB
Python

"""Phase 5.8 — Fixed Assets domain models."""
from __future__ import annotations
import uuid
from datetime import date
from decimal import Decimal
from sqlalchemy import Boolean, Date, Index, Integer, Numeric, String, Text, UniqueConstraint
from sqlalchemy.orm import Mapped, mapped_column
from app.core.database import Base
from app.models.base import TenantMixin, TimestampMixin, UUIDPrimaryKeyMixin
from app.models.types import AssetStatus, DepreciationMethod, GUID
class AssetCategory(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
__tablename__ = "asset_categories"
code: Mapped[str] = mapped_column(String(50), nullable=False)
name: Mapped[str] = mapped_column(String(255), nullable=False)
default_useful_life_months: Mapped[int] = mapped_column(Integer, default=60, nullable=False)
default_depreciation_method: Mapped[DepreciationMethod] = mapped_column(
default=DepreciationMethod.STRAIGHT_LINE, nullable=False
)
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
__table_args__ = (UniqueConstraint("tenant_id", "code", name="uq_asset_category_tenant_code"),)
class AssetGroup(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
__tablename__ = "asset_groups"
code: Mapped[str] = mapped_column(String(50), nullable=False)
name: Mapped[str] = mapped_column(String(255), nullable=False)
category_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
__table_args__ = (UniqueConstraint("tenant_id", "code", name="uq_asset_group_tenant_code"),)
class Asset(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
__tablename__ = "assets"
code: Mapped[str] = mapped_column(String(50), nullable=False)
name: Mapped[str] = mapped_column(String(255), nullable=False)
category_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
group_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
serial_number: Mapped[str | None] = mapped_column(String(100), nullable=True)
location: Mapped[str | None] = mapped_column(String(255), nullable=True)
department_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
cost_center_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
project_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
acquisition_date: Mapped[date | None] = mapped_column(Date, nullable=True)
acquisition_cost: Mapped[Decimal] = mapped_column(Numeric(18, 4), default=Decimal("0"), nullable=False)
current_book_value: Mapped[Decimal] = mapped_column(Numeric(18, 4), default=Decimal("0"), nullable=False)
residual_value: Mapped[Decimal] = mapped_column(Numeric(18, 4), default=Decimal("0"), nullable=False)
useful_life_months: Mapped[int] = mapped_column(Integer, default=60, nullable=False)
depreciation_method: Mapped[DepreciationMethod] = mapped_column(
default=DepreciationMethod.STRAIGHT_LINE, nullable=False
)
status: Mapped[AssetStatus] = mapped_column(default=AssetStatus.DRAFT, nullable=False)
voucher_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
__table_args__ = (
UniqueConstraint("tenant_id", "code", name="uq_asset_tenant_code"),
Index("ix_assets_tenant_status", "tenant_id", "status"),
)
class AssetDepreciation(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
__tablename__ = "asset_depreciations"
asset_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
fiscal_period_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
depreciation_date: Mapped[date] = mapped_column(Date, nullable=False)
amount: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
accumulated_depreciation: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
book_value_after: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
voucher_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
__table_args__ = (Index("ix_asset_depreciation_asset", "tenant_id", "asset_id"),)
class DepreciationSchedule(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
__tablename__ = "depreciation_schedules"
asset_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
period_number: Mapped[int] = mapped_column(Integer, nullable=False)
schedule_date: Mapped[date] = mapped_column(Date, nullable=False)
amount: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
is_posted: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
voucher_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
class AssetTransfer(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
__tablename__ = "asset_transfers"
asset_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
transfer_date: Mapped[date] = mapped_column(Date, nullable=False)
from_location: Mapped[str | None] = mapped_column(String(255), nullable=True)
to_location: Mapped[str | None] = mapped_column(String(255), nullable=True)
from_department_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
to_department_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
voucher_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
class AssetDisposal(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
__tablename__ = "asset_disposals"
asset_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
disposal_date: Mapped[date] = mapped_column(Date, nullable=False)
disposal_type: Mapped[str] = mapped_column(String(50), nullable=False)
sale_amount: Mapped[Decimal | None] = mapped_column(Numeric(18, 4), nullable=True)
gain_loss: Mapped[Decimal | None] = mapped_column(Numeric(18, 4), nullable=True)
voucher_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
class AssetRevaluation(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
__tablename__ = "asset_revaluations"
asset_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
revaluation_date: Mapped[date] = mapped_column(Date, nullable=False)
old_book_value: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
new_book_value: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
difference: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
voucher_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
class AssetImpairment(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
__tablename__ = "asset_impairments"
asset_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
impairment_date: Mapped[date] = mapped_column(Date, nullable=False)
impairment_amount: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
reason: Mapped[str | None] = mapped_column(Text, nullable=True)
voucher_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
class AssetAccountingProfile(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
__tablename__ = "asset_accounting_profiles"
name: Mapped[str] = mapped_column(String(255), nullable=False)
category_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
asset_account_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
depreciation_account_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
accumulated_depreciation_account_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
disposal_gain_account_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
disposal_loss_account_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
is_default: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
class AssetHistory(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
__tablename__ = "asset_history"
asset_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
event_type: Mapped[str] = mapped_column(String(50), nullable=False)
event_date: Mapped[date] = mapped_column(Date, nullable=False)
description: Mapped[str | None] = mapped_column(Text, nullable=True)
before_value: Mapped[str | None] = mapped_column(Text, nullable=True)
after_value: Mapped[str | None] = mapped_column(Text, nullable=True)
__table_args__ = (Index("ix_asset_history_asset", "tenant_id", "asset_id"),)
class AssetLocation(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
__tablename__ = "asset_locations"
code: Mapped[str] = mapped_column(String(50), nullable=False)
name: Mapped[str] = mapped_column(String(255), nullable=False)
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
__table_args__ = (UniqueConstraint("tenant_id", "code", name="uq_asset_location_tenant_code"),)