- 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
352 lines
11 KiB
PHP
Executable File
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;
|
|
}
|
|
}
|