has('asset_type') && $request->asset_type) { $query->where('asset_type', $request->asset_type); } if ($request->has('status') && $request->status) { $query->where('status', $request->status); } if ($request->has('search') && $request->search) { $search = $request->search; $query->where(function($q) use ($search) { $q->where('name', 'like', "%{$search}%") ->orWhere('description', 'like', "%{$search}%") ->orWhere('document_number', 'like', "%{$search}%"); }); } // Ordenação $sortBy = $request->get('sort_by', 'created_at'); $sortDir = $request->get('sort_dir', 'desc'); $query->orderBy($sortBy, $sortDir); // Paginação ou todos if ($request->has('per_page')) { $assets = $query->paginate($request->per_page); } else { $assets = $query->get(); } return response()->json([ 'success' => true, 'data' => $assets, ]); } /** * Criar novo ativo */ public function store(Request $request): JsonResponse { $validator = Validator::make($request->all(), [ 'asset_type' => ['required', Rule::in(array_keys(AssetAccount::ASSET_TYPES))], 'name' => 'required|string|max:255', 'description' => 'nullable|string', 'currency' => 'required|string|size:3', 'color' => 'nullable|string|max:7', 'acquisition_value' => 'required|numeric|min:0', 'current_value' => 'required|numeric|min:0', 'acquisition_date' => 'nullable|date', // Campos opcionais conforme tipo 'property_type' => 'nullable|string', 'investment_type' => 'nullable|string', 'depreciation_method' => 'nullable|string', ]); if ($validator->fails()) { return response()->json([ 'success' => false, 'errors' => $validator->errors(), ], 422); } $data = $request->all(); $data['user_id'] = Auth::id(); $data['business_id'] = $request->business_id ?? Auth::user()->businesses()->first()?->id; $asset = AssetAccount::create($data); return response()->json([ 'success' => true, 'message' => 'Activo creado con éxito', 'data' => $asset, ], 201); } /** * Ver um ativo específico */ public function show(AssetAccount $assetAccount): JsonResponse { // Verificar se pertence ao usuário if ($assetAccount->user_id !== Auth::id()) { return response()->json([ 'success' => false, 'message' => 'No autorizado', ], 403); } return response()->json([ 'success' => true, 'data' => $assetAccount->load('linkedLiability'), ]); } /** * Atualizar ativo */ public function update(Request $request, AssetAccount $assetAccount): JsonResponse { if ($assetAccount->user_id !== Auth::id()) { return response()->json([ 'success' => false, 'message' => 'No autorizado', ], 403); } $validator = Validator::make($request->all(), [ 'asset_type' => ['nullable', Rule::in(array_keys(AssetAccount::ASSET_TYPES))], 'name' => 'nullable|string|max:255', 'current_value' => 'nullable|numeric|min:0', 'status' => ['nullable', Rule::in(array_keys(AssetAccount::STATUSES))], ]); if ($validator->fails()) { return response()->json([ 'success' => false, 'errors' => $validator->errors(), ], 422); } $assetAccount->update($request->all()); return response()->json([ 'success' => true, 'message' => 'Activo actualizado con éxito', 'data' => $assetAccount->fresh(), ]); } /** * Deletar ativo */ public function destroy(AssetAccount $assetAccount): JsonResponse { if ($assetAccount->user_id !== Auth::id()) { return response()->json([ 'success' => false, 'message' => 'No autorizado', ], 403); } $assetAccount->delete(); return response()->json([ 'success' => true, 'message' => 'Activo eliminado con éxito', ]); } /** * Retornar tipos de ativos e opções para o wizard */ public function assetTypes(): JsonResponse { return response()->json([ 'success' => true, 'data' => AssetAccount::ASSET_TYPES, 'property_types' => AssetAccount::PROPERTY_TYPES, 'investment_types' => AssetAccount::INVESTMENT_TYPES, 'depreciation_methods' => AssetAccount::DEPRECIATION_METHODS, 'index_types' => AssetAccount::INDEX_TYPES, 'statuses' => AssetAccount::STATUSES, ]); } /** * Criar ativo via wizard */ public function storeWithWizard(Request $request): JsonResponse { $rules = [ // Step 1 - Tipo 'asset_type' => ['required', Rule::in(array_keys(AssetAccount::ASSET_TYPES))], // Step 2 - Dados básicos 'name' => 'required|string|max:255', 'description' => 'nullable|string|max:1000', 'currency' => 'required|string|size:3', 'color' => 'nullable|string|max:7', // Step 3 - Valores 'acquisition_value' => 'required|numeric|min:0', 'current_value' => 'required|numeric|min:0', 'acquisition_date' => 'nullable|date', // Depreciação 'is_depreciable' => 'nullable|boolean', 'depreciation_method' => ['nullable', Rule::in(array_keys(AssetAccount::DEPRECIATION_METHODS))], 'useful_life_years' => 'nullable|numeric|min:0.5|max:100', 'residual_value' => 'nullable|numeric|min:0', // Imóveis 'property_type' => ['nullable', Rule::in(array_keys(AssetAccount::PROPERTY_TYPES))], 'address' => 'nullable|string|max:500', 'city' => 'nullable|string|max:100', 'state' => 'nullable|string|max:100', 'postal_code' => 'nullable|string|max:20', 'country' => 'nullable|string|size:2', 'property_area_m2' => 'nullable|numeric|min:0', 'registry_number' => 'nullable|string|max:100', // Veículos 'vehicle_brand' => 'nullable|string|max:100', 'vehicle_model' => 'nullable|string|max:100', 'vehicle_year' => 'nullable|integer|min:1900|max:2100', 'vehicle_plate' => 'nullable|string|max:20', 'vehicle_vin' => 'nullable|string|max:50', 'vehicle_mileage' => 'nullable|integer|min:0', // Investimentos 'investment_type' => ['nullable', Rule::in(array_keys(AssetAccount::INVESTMENT_TYPES))], 'institution' => 'nullable|string|max:100', 'account_number' => 'nullable|string|max:100', 'quantity' => 'nullable|integer|min:0', 'unit_price' => 'nullable|numeric|min:0', 'ticker' => 'nullable|string|max:20', 'maturity_date' => 'nullable|date', 'interest_rate' => 'nullable|numeric|min:0|max:100', 'index_type' => ['nullable', Rule::in(array_keys(AssetAccount::INDEX_TYPES))], // Equipamentos 'equipment_brand' => 'nullable|string|max:100', 'equipment_model' => 'nullable|string|max:100', 'serial_number' => 'nullable|string|max:100', 'warranty_expiry' => 'nullable|date', // Recebíveis 'debtor_name' => 'nullable|string|max:200', 'debtor_document' => 'nullable|string|max:50', 'receivable_due_date' => 'nullable|date', 'receivable_amount' => 'nullable|numeric|min:0', // Garantias 'is_collateral' => 'nullable|boolean', 'collateral_for' => 'nullable|string|max:200', 'linked_liability_id' => 'nullable|integer|exists:liability_accounts,id', // Seguros 'has_insurance' => 'nullable|boolean', 'insurance_company' => 'nullable|string|max:100', 'insurance_policy' => 'nullable|string|max:100', 'insurance_value' => 'nullable|numeric|min:0', 'insurance_expiry' => 'nullable|date', // Gestão 'alert_days_before' => 'nullable|integer|min:0|max:365', 'internal_responsible' => 'nullable|string|max:200', 'internal_notes' => 'nullable|string|max:2000', 'document_number' => 'nullable|string|max:100', ]; $validator = Validator::make($request->all(), $rules); if ($validator->fails()) { return response()->json([ 'success' => false, 'errors' => $validator->errors(), ], 422); } // Criar o ativo $data = $validator->validated(); $data['user_id'] = Auth::id(); $data['business_id'] = $request->business_id ?? Auth::user()->businesses()->first()?->id; $data['status'] = 'active'; $asset = AssetAccount::create($data); return response()->json([ 'success' => true, 'message' => 'Activo creado con éxito', 'data' => $asset, ], 201); } /** * Resumo dos ativos do usuário */ public function summary(): JsonResponse { $userId = Auth::id(); $summary = [ 'total_assets' => AssetAccount::where('user_id', $userId)->active()->count(), 'total_value' => AssetAccount::where('user_id', $userId)->active()->sum('current_value'), 'total_acquisition' => AssetAccount::where('user_id', $userId)->active()->sum('acquisition_value'), 'by_type' => [], ]; // Agrupar por tipo $byType = AssetAccount::where('user_id', $userId) ->active() ->selectRaw('asset_type, COUNT(*) as count, SUM(current_value) as total_value') ->groupBy('asset_type') ->get(); foreach ($byType as $item) { $summary['by_type'][$item->asset_type] = [ 'name' => AssetAccount::ASSET_TYPES[$item->asset_type]['name'] ?? $item->asset_type, 'count' => $item->count, 'total_value' => $item->total_value, ]; } // Ganho/perda total $summary['total_gain_loss'] = $summary['total_value'] - $summary['total_acquisition']; $summary['total_gain_loss_percent'] = $summary['total_acquisition'] > 0 ? (($summary['total_value'] - $summary['total_acquisition']) / $summary['total_acquisition']) * 100 : 0; return response()->json([ 'success' => true, 'data' => $summary, ]); } /** * Atualizar valor de mercado de um ativo */ public function updateValue(Request $request, AssetAccount $assetAccount): JsonResponse { if ($assetAccount->user_id !== Auth::id()) { return response()->json([ 'success' => false, 'message' => 'No autorizado', ], 403); } $validator = Validator::make($request->all(), [ 'current_value' => 'required|numeric|min:0', 'note' => 'nullable|string|max:500', ]); if ($validator->fails()) { return response()->json([ 'success' => false, 'errors' => $validator->errors(), ], 422); } $assetAccount->update([ 'current_value' => $request->current_value, ]); // Adicionar nota se fornecida if ($request->note) { $notes = $assetAccount->internal_notes ?? ''; $notes .= "\n[" . now()->format('d/m/Y') . "] Valor actualizado: {$request->note}"; $assetAccount->update(['internal_notes' => trim($notes)]); } return response()->json([ 'success' => true, 'message' => 'Valor actualizado con éxito', 'data' => $assetAccount->fresh(), ]); } /** * Registrar venda/baixa de ativo */ public function dispose(Request $request, AssetAccount $assetAccount): JsonResponse { if ($assetAccount->user_id !== Auth::id()) { return response()->json([ 'success' => false, 'message' => 'No autorizado', ], 403); } $validator = Validator::make($request->all(), [ 'disposal_date' => 'required|date', 'disposal_value' => 'required|numeric|min:0', 'disposal_reason' => 'nullable|string|max:200', 'status' => ['required', Rule::in(['sold', 'written_off', 'depreciated'])], ]); if ($validator->fails()) { return response()->json([ 'success' => false, 'errors' => $validator->errors(), ], 422); } $assetAccount->update([ 'status' => $request->status, 'disposal_date' => $request->disposal_date, 'disposal_value' => $request->disposal_value, 'disposal_reason' => $request->disposal_reason, ]); return response()->json([ 'success' => true, 'message' => 'Baja registrada con éxito', 'data' => $assetAccount->fresh(), ]); } }