@startuml "Diagrama de Clases - Sistema (Core) + Modulo Financiero (SFM)"
skinparam classAttributeIconSize 0
skinparam packageStyle rectangle
skinparam class {
    BackgroundColor<<Core>> #E8F0FE
    BackgroundColor<<SFM>> #FFF4E0
    BackgroundColor<<Vendor>> #EFEFEF
    BorderColor Black
}

note as Scope
  Alcance de este diagrama:
  - Paquete "Core (Sistema)": autenticacion y roles/permisos (Spatie),
    base sobre la que corre TODO el sistema modular.
  - Paquete "SimpleFinancialManagement (Finanzas)": el modelo de dominio
    completo del modulo de finanzas personales.
  - NO incluye el resto de modulos de negocio (Hoteles, Restaurante,
    Cursos, Ministerio de Medios, Bitacora) ni componentes de interfaz
    (Livewire, controladores) — se enfoca en clases de dominio (modelos
    Eloquent) y una clase de comportamiento clave (Observer).
  - Los metodos de importacion/exportacion CSV de la interfaz "Importable"
    (csvImportFields, csvImportRules, csvImportTransform, exportHeaders,
    exportRow) se omiten en cada clase para no saturar el diagrama; se
    indican solo con la relacion de realizacion "..|>" hacia Importable.
  - Todas las clases del modulo Finanzas usan ademas el trait
    App\Traits\WithAuditLog (bitacora de auditoria), omitido como relacion
    individual por claridad.
end note

package "Core (Sistema)" #E8F0FE {

    interface Importable <<interface>> {
        + {static} csvImportFields() : array
        + {static} csvImportRules() : array
        + {static} csvImportUniqueBy() : ?string
    }

    class User <<Core>> {
        + id : bigint <<PK>>
        --
        name : string
        email : string
        username : string
        password : string <<hidden>>
        type_layout : string
        default_dashboard : string
        avatar : string
        is_admin : boolean
        email_verified_at : datetime
        --
        + initials() : string
        + hasRole(role) : bool
        + hasPermissionTo(permission) : bool
        .. traits ..
        HasApiTokens, HasRoles, Notifiable,
        SoftDeletes, WithAuditLog
    }

    class Role <<Vendor>> {
        + id : bigint <<PK>>
        --
        name : string
        guard_name : string
    }

    class Permission <<Vendor>> {
        + id : bigint <<PK>>
        --
        name : string
        guard_name : string
    }

    note right of Role
      spatie/laravel-permission (vendor).
      Relacion User <-> Role <-> Permission
      via tablas pivote model_has_roles /
      role_has_permissions (no son modelos
      propios del proyecto).
    end note
}

package "SimpleFinancialManagement (Finanzas)" #FFF4E0 {

    class "SFM\User" as SfmUser <<SFM>> {
        + wallets() : HasMany
        + buckets() : HasMany
        + creditCards() : HasMany
        + transactions() : HasMany
    }

    note bottom of SfmUser
      Modules\SimpleFinancialManagement\Models\User
      extiende a Core\User agregando relaciones de
      conveniencia. Estado real: no se encontro ninguna
      referencia a esta clase en el resto del modulo
      (no se usa hoy). Ademas conserva un metodo
      tags() -> Tag::class que ya no existe en el
      proyecto (codigo huerfano), por lo que se omitio
      aqui para no representar una clase inexistente.
    end note

    class Wallet <<SFM>> {
        + id : bigint <<PK>>
        --
        user_id : bigint <<FK>>
        name : string
        type : string
        currency : string
        balance : decimal(14,2)
        low_balance_threshold : decimal(14,2)
        is_default : boolean
        --
        + transactions() : HasMany
        + auditLabel() : string
    }

    class Bucket <<SFM>> {
        + id : bigint <<PK>>
        --
        user_id : bigint <<FK>>
        name : string
        description : string
        kind : string
        monthly_target : decimal(14,2)
        is_default : boolean
        {static} KINDS : array = [need, want, savings, giving, debt]
        --
        + transactions() : HasMany
        + {static} budgetProgress(kind, target, spent) : array
        + auditLabel() : string
    }

    class Transaction <<SFM>> {
        + id : bigint <<PK>>
        --
        user_id : bigint <<FK>>
        wallet_id : bigint <<FK>>
        transfer_wallet_id : bigint <<FK>>
        bucket_id : bigint <<FK>>
        credit_card_id : bigint <<FK>>
        saving_goal_id : bigint <<FK>>
        recurring_expense_id : bigint <<FK>>
        related_transaction_id : bigint <<FK>>
        type : string
        origin : string
        card_tx_type : string
        amount : decimal(14,2)
        converted_amount : decimal(14,2)
        exchange_rate_used : decimal(18,6)
        currency : string
        description : string
        date : datetime
        reconciled : boolean
        metadata : json
        attachment_url : string
        --
        + wallet() : BelongsTo
        + transferWallet() : BelongsTo
        + bucket() : BelongsTo
        + creditCard() : BelongsTo
        + savingGoal() : BelongsTo
        + recurringExpense() : BelongsTo
        + splitParts() : HasMany
        + splitParent() : BelongsTo
        + auditLabel() : string
    }

    class RecurringExpense <<SFM>> {
        + id : bigint <<PK>>
        --
        user_id : bigint <<FK>>
        name : string
        expected_amount : decimal(14,2)
        wallet_id : bigint <<FK>>
        bucket_id : bigint <<FK>>
        day_of_month : tinyint
        active : boolean
        --
        + wallet() : BelongsTo
        + bucket() : BelongsTo
        + transactions() : HasMany
        + auditLabel() : string
    }

    class SavingGoal <<SFM>> {
        + id : bigint <<PK>>
        --
        user_id : bigint <<FK>>
        name : string
        description : string
        target_amount : decimal(14,2)
        saved_amount : decimal(14,2)
        deadline : date
        color : string
        achieved_at : datetime
        --
        + transactions() : HasMany
        + getIsOpenSavingsAttribute() : bool
        + getProgressPctAttribute() : float
        + getIsAchievedAttribute() : bool
        + getDaysRemainingAttribute() : ?int
        + getMonthlySuggestionAttribute() : ?float
        + auditLabel() : string
    }

    class Budget <<SFM>> {
        + id : bigint <<PK>>
        --
        user_id : bigint <<FK>>
        bucket_id : bigint <<FK>>
        month : date
        target_amount : decimal(14,2)
        --
        + bucket() : BelongsTo
    }

    class CreditCard <<SFM>> {
        + id : bigint <<PK>>
        --
        user_id : bigint <<FK>>
        name : string
        bank : string
        currency : string
        credit_limit : decimal(14,2)
        billing_day : tinyint
        payment_due_day : tinyint
        annual_rate_pct : decimal(6,3)
        monthly_rate_pct : decimal(6,3)
        active : boolean
        --
        + transactions() : HasMany
        + auditLabel() : string
    }

    class CardCycle <<SFM>> {
        + id : bigint <<PK>>
        --
        credit_card_id : bigint <<FK>>
        period_start : date
        period_end : date
        statement_amount : decimal(14,2)
        statement_date : date
        due_date : date
        paid_amount : decimal(14,2)
        paid_date : date
        interest_charged : decimal(14,2)
        --
        + creditCard() : BelongsTo
        + isPaid() : bool
        + isPartiallyPaid() : bool
    }

    class ExchangeRate <<SFM>> {
        + id : bigint <<PK>>
        --
        currency_code : string
        rate_to_base : decimal(18,6)
        source : string
        fetched_at : datetime
    }

    note bottom of ExchangeRate
      Sin scope de usuario: tasas compartidas por toda
      la instalacion. No tiene FK directa hacia
      Transaction; se consulta por currency_code desde
      ExchangeRateService al registrar/importar
      transferencias entre monedas distintas.
    end note

    class Household <<SFM>> {
        + id : bigint <<PK>>
        --
        owner_id : bigint <<FK>>
        name : string
        currency : string
        invite_code : string
        --
        + owner() : BelongsTo
        + members() : BelongsToMany
        + expenses() : HasMany
    }

    class HouseholdExpense <<SFM>> {
        + id : bigint <<PK>>
        --
        household_id : bigint <<FK>>
        name : string
        expected_amount : decimal(14,2)
        day_of_month : tinyint
        active : boolean
        --
        + household() : BelongsTo
        + payments() : HasMany
    }

    class HouseholdPayment <<SFM>> {
        + id : bigint <<PK>>
        --
        household_expense_id : bigint <<FK>>
        paid_by : bigint <<FK>>
        amount : decimal(14,2)
        paid_date : date
        note : string
        transaction_id : bigint <<FK>>
        --
        + expense() : BelongsTo
        + paidBy() : BelongsTo
        + transaction() : BelongsTo
    }

    class TransactionObserver <<SFM>> {
        - snapshot : array {static}
        --
        + saving(tx : Transaction) : void
        + created(tx : Transaction) : void
        + updated(tx : Transaction) : void
        + deleted(tx : Transaction) : void
        - applyEffect(walletId, type, amount) : void
        - applyTransfer(fromId, toId, fromAmount, toAmount) : void
        - applyToGoal(goalId, type, amount, sign) : void
        - checkLowBalance(walletId, deducted, userId) : void
    }

    note bottom of TransactionObserver
      Se registra sobre Transaction via static::observe().
      Mantiene sincronizados Wallet.balance y
      SavingGoal.saved_amount en cada create/update/delete
      de una Transaction individual (no cubre operaciones
      de bulk update/delete, deliberadamente evitadas en
      el resto del modulo).
    end note
}

' ── Herencia ─────────────────────────────────────────────────────────
SfmUser --|> User

' ── Realizacion (Importable) ────────────────────────────────────────
Wallet ..|> Importable
Bucket ..|> Importable
Transaction ..|> Importable
RecurringExpense ..|> Importable
SavingGoal ..|> Importable
CreditCard ..|> Importable

' ── Roles / permisos (Core) ─────────────────────────────────────────
User "0..*" -- "0..*" Role : tiene >
Role "0..*" -- "0..*" Permission : otorga >

' ── Propiedad (User dueño de cada entidad, columna user_id) ─────────
User "1" -- "0..*" Wallet : posee >
User "1" -- "0..*" Bucket : posee >
User "1" -- "0..*" CreditCard : posee >
User "1" -- "0..*" Transaction : registra >
User "1" -- "0..*" RecurringExpense : posee >
User "1" -- "0..*" SavingGoal : posee >
User "1" -- "0..*" Budget : define >
User "1" -- "0..*" Household : funda (owner_id) >
User "0..*" -- "0..*" Household : es miembro de\n(sfm_household_members) >
User "1" -- "0..*" HouseholdPayment : realiza (paid_by) >

' ── Dominio financiero ───────────────────────────────────────────────
Wallet "1" -- "0..*" Transaction : origen (wallet_id) >
Wallet "1" -- "0..*" Transaction : destino (transfer_wallet_id) >
Bucket "1" -- "0..*" Transaction : categoriza >
Bucket "1" -- "0..*" RecurringExpense : categoriza >
Bucket "1" -- "0..*" Budget : presupuesta >
CreditCard "1" -- "0..*" Transaction : opera >
CreditCard "1" -- "0..*" CardCycle : genera >
SavingGoal "1" -- "0..*" Transaction : recibe aportes >
RecurringExpense "1" -- "0..*" Transaction : genera pagos >
Transaction "1" -- "0..*" Transaction : divide en\n(related_transaction_id) >
Household "1" -- "0..*" HouseholdExpense : agrupa >
HouseholdExpense "1" -- "0..*" HouseholdPayment : recibe >
HouseholdPayment "0..1" -- "0..1" Transaction : concilia con >

' ── Comportamiento (Observer) ────────────────────────────────────────
TransactionObserver ..> Transaction : observa (create/update/delete)
TransactionObserver ..> Wallet : ajusta balance
TransactionObserver ..> SavingGoal : ajusta saved_amount

@enduml
