importService = $importService; } /** * Upload file and get preview */ public function upload(Request $request): JsonResponse { $validator = Validator::make($request->all(), [ 'file' => 'required|file|max:10240', // 10MB max ]); if ($validator->fails()) { return response()->json([ 'success' => false, 'message' => 'Validation failed', 'errors' => $validator->errors(), ], 422); } $file = $request->file('file'); $extension = strtolower($file->getClientOriginalExtension()); // Verificar extensão permitida if (!in_array($extension, ImportMapping::FILE_TYPES)) { return response()->json([ 'success' => false, 'message' => "Unsupported file type: $extension. Allowed: " . implode(', ', ImportMapping::FILE_TYPES), ], 422); } // Salvar arquivo temporariamente $filename = Str::uuid() . '.' . $extension; $path = $file->storeAs('imports/temp', $filename); $fullPath = Storage::path($path); try { // Obter preview $preview = $this->importService->getPreview($fullPath, 15); return response()->json([ 'success' => true, 'data' => [ 'temp_file' => $filename, 'original_name' => $file->getClientOriginalName(), 'file_type' => $extension, 'size' => $file->getSize(), 'preview' => $preview, ], ]); } catch (\Exception $e) { // Limpar arquivo em caso de erro Storage::delete($path); return response()->json([ 'success' => false, 'message' => 'Error processing file: ' . $e->getMessage(), ], 500); } } /** * Get headers from file with specific row */ public function getHeaders(Request $request): JsonResponse { $validator = Validator::make($request->all(), [ 'temp_file' => 'required|string', 'header_row' => 'required|integer|min:0', ]); if ($validator->fails()) { return response()->json([ 'success' => false, 'errors' => $validator->errors(), ], 422); } $fullPath = Storage::path('imports/temp/' . $request->temp_file); if (!file_exists($fullPath)) { return response()->json([ 'success' => false, 'message' => 'Temporary file not found. Please upload again.', ], 404); } try { $headers = $this->importService->getHeaders($fullPath, [ 'header_row' => $request->header_row, ]); // Sugerir mapeamentos $suggestions = $this->importService->suggestMapping($headers); return response()->json([ 'success' => true, 'data' => [ 'headers' => $headers, 'suggestions' => $suggestions, ], ]); } catch (\Exception $e) { return response()->json([ 'success' => false, 'message' => 'Error reading headers: ' . $e->getMessage(), ], 500); } } /** * Process import with mapping */ public function import(Request $request): JsonResponse { $validator = Validator::make($request->all(), [ 'temp_file' => 'required|string', 'mapping_id' => 'nullable|exists:import_mappings,id', 'column_mappings' => 'required_without:mapping_id|array', 'header_row' => 'required_without:mapping_id|integer|min:0', 'data_start_row' => 'required_without:mapping_id|integer|min:0', 'date_format' => 'nullable|string', 'decimal_separator' => 'nullable|string|max:1', 'thousands_separator' => 'nullable|string|max:1', 'account_id' => 'nullable|exists:accounts,id', 'category_id' => 'nullable|exists:categories,id', 'cost_center_id' => 'nullable|exists:cost_centers,id', 'save_mapping' => 'nullable|boolean', 'mapping_name' => 'required_if:save_mapping,true|nullable|string|max:255', 'bank_name' => 'nullable|string|max:100', ]); if ($validator->fails()) { return response()->json([ 'success' => false, 'errors' => $validator->errors(), ], 422); } $fullPath = Storage::path('imports/temp/' . $request->temp_file); if (!file_exists($fullPath)) { return response()->json([ 'success' => false, 'message' => 'Temporary file not found. Please upload again.', ], 404); } $userId = auth()->id(); try { // Usar mapeamento existente ou criar novo if ($request->mapping_id) { $mapping = ImportMapping::where('user_id', $userId) ->findOrFail($request->mapping_id); } else { $extension = pathinfo($request->temp_file, PATHINFO_EXTENSION); $mappingData = [ 'user_id' => $userId, 'name' => $request->mapping_name ?? 'Importação ' . now()->format('d/m/Y H:i'), 'bank_name' => $request->bank_name, 'file_type' => $extension, 'header_row' => $request->header_row, 'data_start_row' => $request->data_start_row, 'date_format' => $request->date_format ?? 'd/m/Y', 'decimal_separator' => $request->decimal_separator ?? ',', 'thousands_separator' => $request->thousands_separator ?? '.', 'column_mappings' => $request->column_mappings, 'default_account_id' => $request->account_id, 'default_category_id' => $request->category_id, 'default_cost_center_id' => $request->cost_center_id, 'is_active' => $request->save_mapping ?? false, ]; if ($request->save_mapping) { $mapping = ImportMapping::create($mappingData); } else { $mapping = new ImportMapping($mappingData); // Não definir ID para mapeamento temporário - será tratado no ImportService } } // Executar importação $importLog = $this->importService->importTransactions( $fullPath, $mapping, $userId, $request->account_id, $request->category_id, $request->cost_center_id ); // Limpar arquivo temporário Storage::delete('imports/temp/' . $request->temp_file); return response()->json([ 'success' => true, 'message' => "Importação concluída: {$importLog->imported_rows} transações importadas", 'data' => [ 'import_log' => $importLog, 'mapping_saved' => $request->save_mapping && isset($mapping->id) && $mapping->id > 0, ], ]); } catch (\Exception $e) { return response()->json([ 'success' => false, 'message' => 'Import failed: ' . $e->getMessage(), ], 500); } } /** * List saved mappings */ public function mappings(Request $request): JsonResponse { $mappings = ImportMapping::where('user_id', auth()->id()) ->where('is_active', true) ->with(['defaultAccount', 'defaultCategory', 'defaultCostCenter']) ->orderBy('name') ->get(); return response()->json([ 'success' => true, 'data' => $mappings, ]); } /** * Get single mapping */ public function getMapping(int $id): JsonResponse { $mapping = ImportMapping::where('user_id', auth()->id()) ->with(['defaultAccount', 'defaultCategory', 'defaultCostCenter']) ->findOrFail($id); return response()->json([ 'success' => true, 'data' => $mapping, ]); } /** * Update mapping */ public function updateMapping(Request $request, int $id): JsonResponse { $mapping = ImportMapping::where('user_id', auth()->id()) ->findOrFail($id); $validator = Validator::make($request->all(), [ 'name' => 'sometimes|string|max:255', 'bank_name' => 'nullable|string|max:100', 'header_row' => 'sometimes|integer|min:0', 'data_start_row' => 'sometimes|integer|min:0', 'date_format' => 'sometimes|string', 'decimal_separator' => 'sometimes|string|max:1', 'thousands_separator' => 'sometimes|string|max:1', 'column_mappings' => 'sometimes|array', 'default_account_id' => 'nullable|exists:accounts,id', 'default_category_id' => 'nullable|exists:categories,id', 'default_cost_center_id' => 'nullable|exists:cost_centers,id', 'is_active' => 'sometimes|boolean', ]); if ($validator->fails()) { return response()->json([ 'success' => false, 'errors' => $validator->errors(), ], 422); } $mapping->update($request->all()); return response()->json([ 'success' => true, 'data' => $mapping->fresh(), ]); } /** * Delete mapping */ public function deleteMapping(int $id): JsonResponse { $mapping = ImportMapping::where('user_id', auth()->id()) ->findOrFail($id); $mapping->delete(); return response()->json([ 'success' => true, 'message' => 'Mapping deleted successfully', ]); } /** * Get available bank presets */ public function presets(): JsonResponse { $presets = $this->importService->getAvailablePresets(); return response()->json([ 'success' => true, 'data' => $presets, ]); } /** * Create mapping from preset */ public function createFromPreset(Request $request): JsonResponse { $validator = Validator::make($request->all(), [ 'preset' => 'required|string', ]); if ($validator->fails()) { return response()->json([ 'success' => false, 'errors' => $validator->errors(), ], 422); } try { $mapping = $this->importService->createBankPreset( $request->preset, auth()->id() ); return response()->json([ 'success' => true, 'data' => $mapping, ]); } catch (\Exception $e) { return response()->json([ 'success' => false, 'message' => $e->getMessage(), ], 400); } } /** * Get import history */ public function history(Request $request): JsonResponse { $logs = ImportLog::where('user_id', auth()->id()) ->with('importMapping') ->orderBy('created_at', 'desc') ->limit(50) ->get(); return response()->json([ 'success' => true, 'data' => $logs, ]); } /** * Get mappable fields info */ public function fields(): JsonResponse { return response()->json([ 'success' => true, 'data' => [ 'fields' => ImportMapping::MAPPABLE_FIELDS, 'date_formats' => ImportMapping::DATE_FORMATS, 'file_types' => ImportMapping::FILE_TYPES, ], ]); } }