webmoney/backend/app/Models/AssetAccount.php
marco 54cccdd095 refactor: migração para desenvolvimento direto no servidor
- Removido README.md padrão do Laravel (backend)
- Removidos scripts de deploy (não mais necessários)
- Atualizado copilot-instructions.md para novo fluxo
- Adicionada documentação de auditoria do servidor
- Sincronizado código de produção com repositório

Novo workflow:
- Trabalhamos diretamente em /root/webmoney (symlink para /var/www/webmoney)
- Mudanças PHP são instantâneas
- Mudanças React requerem 'npm run build'
- Commit após validação funcional
2025-12-19 11:45:32 +01:00

352 lines
11 KiB
PHP
Executable File

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class AssetAccount extends Model
{
use HasFactory, SoftDeletes;
/**
* Status do ativo
*/
public const STATUS_ACTIVE = 'active';
public const STATUS_SOLD = 'sold';
public const STATUS_DEPRECIATED = 'depreciated';
public const STATUS_WRITTEN_OFF = 'written_off';
public const STATUSES = [
self::STATUS_ACTIVE => 'Activo',
self::STATUS_SOLD => 'Vendido',
self::STATUS_DEPRECIATED => 'Depreciado',
self::STATUS_WRITTEN_OFF => 'Dado de baja',
];
/**
* Tipos de ativo
*/
public const ASSET_TYPE_REAL_ESTATE = 'real_estate';
public const ASSET_TYPE_VEHICLE = 'vehicle';
public const ASSET_TYPE_INVESTMENT = 'investment';
public const ASSET_TYPE_EQUIPMENT = 'equipment';
public const ASSET_TYPE_INVENTORY = 'inventory';
public const ASSET_TYPE_RECEIVABLE = 'receivable';
public const ASSET_TYPE_CASH = 'cash';
public const ASSET_TYPE_OTHER = 'other';
public const ASSET_TYPES = [
self::ASSET_TYPE_REAL_ESTATE => [
'name' => 'Inmueble',
'description' => 'Casa, apartamento, terreno, local comercial',
'icon' => 'home',
'is_depreciable' => true,
'fields' => ['property_type', 'address', 'property_area_m2', 'registry_number'],
],
self::ASSET_TYPE_VEHICLE => [
'name' => 'Vehículo',
'description' => 'Coche, moto, camión, maquinaria móvil',
'icon' => 'truck',
'is_depreciable' => true,
'fields' => ['vehicle_brand', 'vehicle_model', 'vehicle_year', 'vehicle_plate', 'vehicle_mileage'],
],
self::ASSET_TYPE_INVESTMENT => [
'name' => 'Inversión',
'description' => 'Acciones, fondos, bonos, criptomonedas',
'icon' => 'chart-bar',
'is_depreciable' => false,
'fields' => ['investment_type', 'institution', 'ticker', 'quantity', 'unit_price'],
],
self::ASSET_TYPE_EQUIPMENT => [
'name' => 'Equipamiento',
'description' => 'Maquinaria, herramientas, ordenadores',
'icon' => 'cog',
'is_depreciable' => true,
'fields' => ['equipment_brand', 'equipment_model', 'serial_number', 'warranty_expiry'],
],
self::ASSET_TYPE_INVENTORY => [
'name' => 'Inventario',
'description' => 'Stock, mercancías, materias primas',
'icon' => 'archive-box',
'is_depreciable' => false,
'fields' => ['quantity'],
],
self::ASSET_TYPE_RECEIVABLE => [
'name' => 'Cuenta por Cobrar',
'description' => 'Créditos, préstamos otorgados',
'icon' => 'banknotes',
'is_depreciable' => false,
'fields' => ['debtor_name', 'receivable_due_date', 'receivable_amount'],
],
self::ASSET_TYPE_CASH => [
'name' => 'Efectivo/Caja',
'description' => 'Dinero en efectivo, caja chica',
'icon' => 'currency-euro',
'is_depreciable' => false,
'fields' => [],
],
self::ASSET_TYPE_OTHER => [
'name' => 'Otro',
'description' => 'Otros tipos de activos',
'icon' => 'document-text',
'is_depreciable' => false,
'fields' => [],
],
];
/**
* Tipos de imóvel
*/
public const PROPERTY_TYPES = [
'house' => 'Casa',
'apartment' => 'Apartamento/Piso',
'land' => 'Terreno',
'commercial' => 'Local Comercial',
'industrial' => 'Nave Industrial',
'office' => 'Oficina',
'parking' => 'Plaza de Garaje',
'warehouse' => 'Almacén',
'rural' => 'Finca Rústica',
];
/**
* Tipos de investimento
*/
public const INVESTMENT_TYPES = [
'stocks' => ['name' => 'Acciones', 'description' => 'Participaciones en empresas cotizadas'],
'bonds' => ['name' => 'Bonos', 'description' => 'Títulos de deuda pública o privada'],
'funds' => ['name' => 'Fondos de Inversión', 'description' => 'Fondos mutuos, ETFs'],
'fixed_income' => ['name' => 'Renta Fija', 'description' => 'Depósitos, letras del tesoro'],
'crypto' => ['name' => 'Criptomonedas', 'description' => 'Bitcoin, Ethereum, etc.'],
'real_estate_fund' => ['name' => 'Fondo Inmobiliario', 'description' => 'REITs, SOCIMIs'],
'pension' => ['name' => 'Plan de Pensiones', 'description' => 'Ahorro para jubilación'],
'savings' => ['name' => 'Cuenta de Ahorro', 'description' => 'Cuentas remuneradas'],
];
/**
* Métodos de depreciação
*/
public const DEPRECIATION_METHODS = [
'linear' => ['name' => 'Lineal', 'description' => 'Depreciación constante cada año'],
'declining_balance' => ['name' => 'Saldo Decreciente', 'description' => 'Mayor depreciación al inicio'],
'units_production' => ['name' => 'Unidades Producidas', 'description' => 'Según uso/producción'],
'sum_years' => ['name' => 'Suma de Años', 'description' => 'Depreciación acelerada'],
];
/**
* Tipos de indexadores (para investimentos)
*/
public const INDEX_TYPES = [
'fixed' => ['name' => 'Tasa Fija', 'description' => 'Rentabilidad fija'],
'cdi' => ['name' => 'CDI', 'description' => '% del CDI (Brasil)'],
'selic' => ['name' => 'SELIC', 'description' => 'Tasa SELIC (Brasil)'],
'ipca' => ['name' => 'IPCA+', 'description' => 'Inflación + spread (Brasil)'],
'euribor' => ['name' => 'Euribor+', 'description' => 'Euribor + spread (UE)'],
'ibex' => ['name' => 'IBEX 35', 'description' => 'Índice bursátil español'],
'sp500' => ['name' => 'S&P 500', 'description' => 'Índice bursátil americano'],
];
protected $fillable = [
'user_id',
'business_id',
'asset_type',
'name',
'description',
'currency',
'color',
// Valores
'acquisition_value',
'current_value',
'acquisition_date',
// Depreciação
'is_depreciable',
'depreciation_method',
'useful_life_years',
'residual_value',
'accumulated_depreciation',
// Imóveis
'property_type',
'address',
'city',
'state',
'postal_code',
'country',
'property_area_m2',
'registry_number',
// Veículos
'vehicle_brand',
'vehicle_model',
'vehicle_year',
'vehicle_plate',
'vehicle_vin',
'vehicle_mileage',
// Investimentos
'investment_type',
'institution',
'account_number',
'quantity',
'unit_price',
'ticker',
'maturity_date',
'interest_rate',
'index_type',
// Equipamentos
'equipment_brand',
'equipment_model',
'serial_number',
'warranty_expiry',
// Recebíveis
'debtor_name',
'debtor_document',
'receivable_due_date',
'receivable_amount',
// Garantias
'is_collateral',
'collateral_for',
'linked_liability_id',
// Seguros
'has_insurance',
'insurance_company',
'insurance_policy',
'insurance_value',
'insurance_expiry',
// Gestão
'alert_days_before',
'internal_responsible',
'internal_notes',
'document_number',
// Status
'status',
'disposal_date',
'disposal_value',
'disposal_reason',
];
protected $casts = [
'acquisition_value' => 'decimal:2',
'current_value' => 'decimal:2',
'residual_value' => 'decimal:2',
'accumulated_depreciation' => 'decimal:2',
'property_area_m2' => 'decimal:2',
'useful_life_years' => 'decimal:2',
'unit_price' => 'decimal:6',
'insurance_value' => 'decimal:2',
'receivable_amount' => 'decimal:2',
'disposal_value' => 'decimal:2',
'interest_rate' => 'decimal:4',
'is_depreciable' => 'boolean',
'is_collateral' => 'boolean',
'has_insurance' => 'boolean',
'acquisition_date' => 'date',
'maturity_date' => 'date',
'warranty_expiry' => 'date',
'receivable_due_date' => 'date',
'insurance_expiry' => 'date',
'disposal_date' => 'date',
];
/**
* Relações
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
public function business(): BelongsTo
{
return $this->belongsTo(Business::class);
}
public function linkedLiability(): BelongsTo
{
return $this->belongsTo(LiabilityAccount::class, 'linked_liability_id');
}
/**
* Scopes
*/
public function scopeActive($query)
{
return $query->where('status', self::STATUS_ACTIVE);
}
public function scopeOfType($query, string $type)
{
return $query->where('asset_type', $type);
}
public function scopeDepreciable($query)
{
return $query->where('is_depreciable', true);
}
/**
* Accessors
*/
public function getAssetTypeNameAttribute(): string
{
return self::ASSET_TYPES[$this->asset_type]['name'] ?? $this->asset_type;
}
public function getStatusNameAttribute(): string
{
return self::STATUSES[$this->status] ?? $this->status;
}
public function getNetValueAttribute(): float
{
return $this->current_value - $this->accumulated_depreciation;
}
public function getGainLossAttribute(): float
{
return $this->current_value - $this->acquisition_value;
}
public function getGainLossPercentAttribute(): float
{
if ($this->acquisition_value == 0) return 0;
return (($this->current_value - $this->acquisition_value) / $this->acquisition_value) * 100;
}
/**
* Calcular depreciação anual (método linear)
*/
public function calculateAnnualDepreciation(): float
{
if (!$this->is_depreciable || !$this->useful_life_years || $this->useful_life_years == 0) {
return 0;
}
$depreciableValue = $this->acquisition_value - ($this->residual_value ?? 0);
return $depreciableValue / $this->useful_life_years;
}
/**
* Verificar se seguro está próximo do vencimento
*/
public function isInsuranceExpiringSoon(int $days = 30): bool
{
if (!$this->has_insurance || !$this->insurance_expiry) {
return false;
}
return $this->insurance_expiry->diffInDays(now()) <= $days;
}
}