webmoney/backend/app/Http/Controllers/Api/CostCenterController.php
marco c787077a39 fix: associar automaticamente transações ao centro de custo Geral
- Corrigir validação de keywords permitindo array vazio
- Atualizar todas transações existentes sem centro de custo para Geral
- Criar centro de custo Geral para usuários sem ele
- Associar automaticamente novas transações criadas ao Geral
- Associar automaticamente transferências ao Geral
- Associar automaticamente transações importadas ao Geral
2025-12-19 16:26:27 +01:00

333 lines
10 KiB
PHP
Executable File

<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\CostCenter;
use App\Models\CostCenterKeyword;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
class CostCenterController extends Controller
{
/**
* Listar todos os centros de custo do usuário
*/
public function index(Request $request): JsonResponse
{
$userId = Auth::id();
// Verificar se o usuário tem um centro de custo do sistema
// Se não tiver, criar automaticamente o "Geral"
$hasSystemCostCenter = CostCenter::where('user_id', $userId)
->where('is_system', true)
->exists();
if (!$hasSystemCostCenter) {
CostCenter::create([
'user_id' => $userId,
'name' => 'Geral',
'code' => 'GERAL',
'description' => 'Centro de custo padrão para transações não categorizadas',
'color' => '#6c757d',
'icon' => 'FaFolder',
'is_active' => true,
'is_system' => true,
]);
}
$query = CostCenter::where('user_id', $userId)
->with('keywords');
if ($request->has('is_active')) {
$query->where('is_active', $request->boolean('is_active'));
}
// Ordenar com centro de custo do sistema primeiro, depois por nome
$costCenters = $query->orderByDesc('is_system')->orderBy('name')->get();
return response()->json([
'success' => true,
'data' => $costCenters,
]);
}
/**
* Criar novo centro de custo
*/
public function store(Request $request): JsonResponse
{
$validated = $request->validate([
'name' => 'required|string|max:100',
'code' => 'nullable|string|max:20|unique:cost_centers,code,NULL,id,user_id,' . Auth::id(),
'description' => 'nullable|string',
'color' => 'nullable|string|max:7',
'icon' => 'nullable|string|max:50',
'is_active' => 'nullable|boolean',
'keywords' => 'nullable|array',
'keywords.*' => 'string|max:100',
]);
$keywords = $validated['keywords'] ?? [];
unset($validated['keywords']);
$validated['user_id'] = Auth::id();
DB::beginTransaction();
try {
$costCenter = CostCenter::create($validated);
// Adicionar palavras-chave
foreach ($keywords as $keyword) {
$costCenter->keywords()->create([
'keyword' => trim($keyword),
'is_case_sensitive' => false,
'is_active' => true,
]);
}
DB::commit();
return response()->json([
'success' => true,
'message' => 'Centro de custo criado com sucesso',
'data' => $costCenter->load('keywords'),
], 201);
} catch (\Exception $e) {
DB::rollBack();
return response()->json([
'success' => false,
'message' => 'Erro ao criar centro de custo: ' . $e->getMessage(),
], 500);
}
}
/**
* Exibir um centro de custo específico
*/
public function show(int $id): JsonResponse
{
$costCenter = CostCenter::where('user_id', Auth::id())
->with('keywords')
->findOrFail($id);
return response()->json([
'success' => true,
'data' => $costCenter,
]);
}
/**
* Atualizar um centro de custo
*/
public function update(Request $request, int $id): JsonResponse
{
$costCenter = CostCenter::where('user_id', Auth::id())->findOrFail($id);
// Impedir edição de centro de custo do sistema
if ($costCenter->is_system) {
return response()->json([
'success' => false,
'message' => 'O centro de custo do sistema não pode ser editado',
], 403);
}
$validated = $request->validate([
'name' => 'sometimes|required|string|max:100',
'code' => 'nullable|string|max:20|unique:cost_centers,code,' . $id . ',id,user_id,' . Auth::id(),
'description' => 'nullable|string',
'color' => 'nullable|string|max:7',
'icon' => 'nullable|string|max:50',
'is_active' => 'nullable|boolean',
'keywords' => 'nullable|array',
'keywords.*' => 'string|max:100',
]);
$keywords = $validated['keywords'] ?? null;
unset($validated['keywords']);
DB::beginTransaction();
try {
$costCenter->update($validated);
// Se keywords foram fornecidas, sincronizar
if ($keywords !== null) {
// Remover antigas
$costCenter->keywords()->delete();
// Adicionar novas
foreach ($keywords as $keyword) {
$costCenter->keywords()->create([
'keyword' => trim($keyword),
'is_case_sensitive' => false,
'is_active' => true,
]);
}
}
DB::commit();
return response()->json([
'success' => true,
'message' => 'Centro de custo atualizado com sucesso',
'data' => $costCenter->fresh()->load('keywords'),
]);
} catch (\Exception $e) {
DB::rollBack();
return response()->json([
'success' => false,
'message' => 'Erro ao atualizar centro de custo: ' . $e->getMessage(),
], 500);
}
}
/**
* Deletar um centro de custo (soft delete)
*/
public function destroy(int $id): JsonResponse
{
$costCenter = CostCenter::where('user_id', Auth::id())->findOrFail($id);
// Impedir exclusão de centro de custo do sistema
if ($costCenter->is_system) {
return response()->json([
'success' => false,
'message' => 'O centro de custo do sistema não pode ser excluído',
], 403);
}
$costCenter->keywords()->delete();
$costCenter->delete();
return response()->json([
'success' => true,
'message' => 'Centro de custo excluído com sucesso',
]);
}
/**
* Atualizar palavras-chave de um centro de custo (incluindo de sistema)
*/
public function updateKeywords(Request $request, int $id): JsonResponse
{
$costCenter = CostCenter::where('user_id', Auth::id())->findOrFail($id);
$validated = $request->validate([
'keywords' => 'nullable|array',
'keywords.*' => 'string|max:100',
]);
DB::beginTransaction();
try {
// Remover antigas
$costCenter->keywords()->delete();
// Adicionar novas
foreach ($validated['keywords'] as $keyword) {
$costCenter->keywords()->create([
'keyword' => trim($keyword),
'is_case_sensitive' => false,
'is_active' => true,
]);
}
DB::commit();
return response()->json([
'success' => true,
'message' => 'Palavras-chave atualizadas com sucesso',
'data' => $costCenter->fresh()->load('keywords'),
]);
} catch (\Exception $e) {
DB::rollBack();
return response()->json([
'success' => false,
'message' => 'Erro ao atualizar palavras-chave: ' . $e->getMessage(),
], 500);
}
}
/**
* Adicionar palavra-chave a um centro de custo
*/
public function addKeyword(Request $request, int $id): JsonResponse
{
$costCenter = CostCenter::where('user_id', Auth::id())->findOrFail($id);
$validated = $request->validate([
'keyword' => 'required|string|max:100',
'is_case_sensitive' => 'nullable|boolean',
]);
$keyword = $costCenter->keywords()->create([
'keyword' => trim($validated['keyword']),
'is_case_sensitive' => $validated['is_case_sensitive'] ?? false,
'is_active' => true,
]);
return response()->json([
'success' => true,
'message' => 'Palavra-chave adicionada com sucesso',
'data' => $keyword,
], 201);
}
/**
* Remover palavra-chave de um centro de custo
*/
public function removeKeyword(int $id, int $keywordId): JsonResponse
{
$costCenter = CostCenter::where('user_id', Auth::id())->findOrFail($id);
$keyword = $costCenter->keywords()->findOrFail($keywordId);
$keyword->delete();
return response()->json([
'success' => true,
'message' => 'Palavra-chave removida com sucesso',
]);
}
/**
* Encontrar centro de custo por texto (usando palavras-chave)
*/
public function matchByText(Request $request): JsonResponse
{
$validated = $request->validate([
'text' => 'required|string',
]);
$text = $validated['text'];
$textLower = strtolower($text);
$keywords = CostCenterKeyword::whereHas('costCenter', function ($query) {
$query->where('user_id', Auth::id())->where('is_active', true);
})
->where('is_active', true)
->with('costCenter')
->get();
$matches = [];
foreach ($keywords as $keyword) {
$searchText = $keyword->is_case_sensitive ? $text : $textLower;
$searchKeyword = $keyword->is_case_sensitive ? $keyword->keyword : strtolower($keyword->keyword);
if (str_contains($searchText, $searchKeyword)) {
$matches[] = [
'cost_center' => $keyword->costCenter,
'matched_keyword' => $keyword->keyword,
];
}
}
return response()->json([
'success' => true,
'data' => $matches,
]);
}
}