- Redesigned category create/edit modal with elegant wizard-style UI - Redesigned batch categorization modal with visual cards and better preview - Added missing i18n translations (common.continue, creating, remove) - Added budgets.general and wizard translations for ES, PT-BR, EN - Fixed 3 demo user transactions that were missing categories
372 lines
24 KiB
PHP
372 lines
24 KiB
PHP
<?php
|
|
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Route;
|
|
use App\Http\Controllers\Api\AuthController;
|
|
use App\Http\Controllers\Api\EmailTestController;
|
|
use App\Http\Controllers\Api\AccountController;
|
|
use App\Http\Controllers\Api\CostCenterController;
|
|
use App\Http\Controllers\Api\CategoryController;
|
|
use App\Http\Controllers\Api\LiabilityAccountController;
|
|
use App\Http\Controllers\Api\AssetAccountController;
|
|
use App\Http\Controllers\Api\TransactionController;
|
|
use App\Http\Controllers\Api\ImportController;
|
|
use App\Http\Controllers\Api\TransferDetectionController;
|
|
use App\Http\Controllers\Api\DashboardController;
|
|
use App\Http\Controllers\Api\RecurringTemplateController;
|
|
use App\Http\Controllers\Api\BusinessSettingController;
|
|
use App\Http\Controllers\Api\ProductSheetController;
|
|
use App\Http\Controllers\Api\ServiceSheetController;
|
|
use App\Http\Controllers\Api\PromotionalCampaignController;
|
|
use App\Http\Controllers\Api\FinancialGoalController;
|
|
use App\Http\Controllers\Api\BudgetController;
|
|
use App\Http\Controllers\Api\ReportController;
|
|
use App\Http\Controllers\Api\FinancialHealthController;
|
|
use App\Http\Controllers\Api\UserPreferenceController;
|
|
use App\Http\Controllers\Api\PlanController;
|
|
use App\Http\Controllers\Api\SubscriptionController;
|
|
use App\Http\Controllers\Api\UserManagementController;
|
|
use App\Http\Controllers\Api\SiteSettingsController;
|
|
|
|
// Public routes with rate limiting
|
|
Route::post('/register', [AuthController::class, 'register'])->middleware('throttle:register');
|
|
Route::post('/login', [AuthController::class, 'login'])->middleware('throttle:login');
|
|
|
|
// Account activation (public)
|
|
Route::post('/activate', [AuthController::class, 'activateAccount']);
|
|
Route::post('/resend-activation', [AuthController::class, 'resendActivation']);
|
|
Route::post('/cancel-registration', [AuthController::class, 'cancelRegistration']);
|
|
|
|
// Plans (public - for pricing page)
|
|
Route::get('/plans', [PlanController::class, 'index']);
|
|
Route::get('/plans/{slug}', [PlanController::class, 'show']);
|
|
|
|
// PayPal config (public - needed for frontend SDK)
|
|
Route::get('/paypal/config', [SubscriptionController::class, 'paypalConfig']);
|
|
|
|
// PayPal webhook (public - called by PayPal)
|
|
Route::post('/paypal/webhook', [SubscriptionController::class, 'webhook']);
|
|
|
|
// Subscription start for new users (public - used after registration)
|
|
Route::post('/subscription/start', [SubscriptionController::class, 'startSubscription']);
|
|
|
|
// Subscription confirm for new users (public - called after PayPal redirect)
|
|
Route::post('/subscription/confirm-public', [SubscriptionController::class, 'confirmPublic']);
|
|
|
|
// Email testing routes (should be protected in production)
|
|
Route::post('/email/send-test', [EmailTestController::class, 'sendTest']);
|
|
Route::get('/email/anti-spam-info', [EmailTestController::class, 'getAntiSpamInfo']);
|
|
|
|
// Protected routes (require authentication)
|
|
Route::middleware('auth:sanctum')->group(function () {
|
|
// Auth routes
|
|
Route::post('/logout', [AuthController::class, 'logout']);
|
|
Route::get('/me', [AuthController::class, 'me']);
|
|
Route::put('/profile', [AuthController::class, 'updateProfile']);
|
|
Route::get('/user', function (Request $request) {
|
|
return $request->user();
|
|
});
|
|
|
|
// ============================================
|
|
// Subscriptions (Assinaturas)
|
|
// ============================================
|
|
Route::get('/subscription/status', [SubscriptionController::class, 'status']);
|
|
Route::post('/subscription/subscribe', [SubscriptionController::class, 'subscribe']);
|
|
Route::post('/subscription/confirm', [SubscriptionController::class, 'confirm']);
|
|
Route::post('/subscription/cancel', [SubscriptionController::class, 'cancel']);
|
|
Route::get('/subscription/invoices', [SubscriptionController::class, 'invoices']);
|
|
|
|
// ============================================
|
|
// Contas (Accounts) - Com limite de plano
|
|
// ============================================
|
|
// Rotas específicas ANTES do apiResource
|
|
Route::post('accounts/recalculate-all', [AccountController::class, 'recalculateBalances']);
|
|
Route::post('accounts/{id}/recalculate', [AccountController::class, 'recalculateBalance']);
|
|
Route::post('accounts/{id}/adjust-balance', [AccountController::class, 'adjustBalance']);
|
|
|
|
// Resource principal com middleware de limite no store
|
|
Route::post('accounts', [AccountController::class, 'store'])->middleware('plan.limits:accounts');
|
|
Route::apiResource('accounts', AccountController::class)->except(['store']);
|
|
Route::get('accounts-total', [AccountController::class, 'totalBalance']);
|
|
|
|
// ============================================
|
|
// Centros de Custo (Cost Centers)
|
|
// ============================================
|
|
Route::apiResource('cost-centers', CostCenterController::class);
|
|
Route::post('cost-centers/{id}/keywords', [CostCenterController::class, 'addKeyword']);
|
|
Route::delete('cost-centers/{id}/keywords/{keywordId}', [CostCenterController::class, 'removeKeyword']);
|
|
Route::post('cost-centers/match', [CostCenterController::class, 'matchByText']);
|
|
|
|
// ============================================
|
|
// Categorias (Categories) - Com limite de plano
|
|
// ============================================
|
|
// Rotas específicas ANTES do apiResource
|
|
Route::post('categories/categorize-batch/preview', [CategoryController::class, 'categorizeBatchPreview']);
|
|
Route::post('categories/categorize-batch', [CategoryController::class, 'categorizeBatch']);
|
|
Route::post('categories/categorize-batch/manual', [CategoryController::class, 'categorizeBatchManual']);
|
|
Route::post('categories/match', [CategoryController::class, 'matchByText']);
|
|
Route::post('categories/reorder', [CategoryController::class, 'reorder']);
|
|
|
|
// Resource principal com middleware de limite no store
|
|
Route::post('categories', [CategoryController::class, 'store'])->middleware('plan.limits:categories');
|
|
Route::apiResource('categories', CategoryController::class)->except(['store']);
|
|
|
|
// Rotas com parâmetros (depois do apiResource)
|
|
Route::post('categories/{id}/keywords', [CategoryController::class, 'addKeyword']);
|
|
Route::delete('categories/{id}/keywords/{keywordId}', [CategoryController::class, 'removeKeyword']);
|
|
|
|
// ============================================
|
|
// Contas Passivo (Liability Accounts)
|
|
// ============================================
|
|
// Rotas específicas ANTES do apiResource (para evitar conflito com {id})
|
|
Route::get('liability-accounts/pending-reconciliation', [LiabilityAccountController::class, 'pendingReconciliation']);
|
|
Route::get('liability-accounts/template', [LiabilityAccountController::class, 'downloadTemplate']);
|
|
Route::get('liability-accounts/contract-types', [LiabilityAccountController::class, 'contractTypes']);
|
|
Route::post('liability-accounts/import', [LiabilityAccountController::class, 'import']);
|
|
Route::post('liability-accounts/wizard', [LiabilityAccountController::class, 'storeWithWizard']);
|
|
Route::get('liability-summary', [LiabilityAccountController::class, 'summary']);
|
|
|
|
// Resource principal
|
|
Route::apiResource('liability-accounts', LiabilityAccountController::class);
|
|
|
|
// Rotas com parâmetros (depois do apiResource)
|
|
Route::get('liability-accounts/{id}/installments', [LiabilityAccountController::class, 'installments']);
|
|
Route::put('liability-accounts/{accountId}/installments/{installmentId}', [LiabilityAccountController::class, 'updateInstallment']);
|
|
|
|
// Conciliação de parcelas
|
|
Route::get('liability-accounts/{accountId}/installments/{installmentId}/eligible-transactions', [LiabilityAccountController::class, 'eligibleTransactions']);
|
|
Route::post('liability-accounts/{accountId}/installments/{installmentId}/reconcile', [LiabilityAccountController::class, 'reconcile']);
|
|
Route::delete('liability-accounts/{accountId}/installments/{installmentId}/reconcile', [LiabilityAccountController::class, 'unreconcile']);
|
|
|
|
// ============================================
|
|
// Contas Ativo (Asset Accounts)
|
|
// ============================================
|
|
// Rotas específicas ANTES do apiResource
|
|
Route::get('asset-accounts/asset-types', [AssetAccountController::class, 'assetTypes']);
|
|
Route::post('asset-accounts/wizard', [AssetAccountController::class, 'storeWithWizard']);
|
|
Route::get('asset-summary', [AssetAccountController::class, 'summary']);
|
|
|
|
// Resource principal
|
|
Route::apiResource('asset-accounts', AssetAccountController::class);
|
|
|
|
// Rotas com parâmetros
|
|
Route::put('asset-accounts/{assetAccount}/value', [AssetAccountController::class, 'updateValue']);
|
|
Route::post('asset-accounts/{assetAccount}/dispose', [AssetAccountController::class, 'dispose']);
|
|
|
|
// ============================================
|
|
// Transações (Transactions) - Com limite de plano
|
|
// ============================================
|
|
Route::post('transactions', [TransactionController::class, 'store'])->middleware('plan.limits:transactions');
|
|
Route::apiResource('transactions', TransactionController::class)->except(['store']);
|
|
Route::get('transactions-by-week', [TransactionController::class, 'byWeek']);
|
|
Route::get('transactions-summary', [TransactionController::class, 'summary']);
|
|
|
|
// Ações em transações
|
|
Route::post('transactions/{transaction}/complete', [TransactionController::class, 'complete']);
|
|
Route::post('transactions/{transaction}/quick-complete', [TransactionController::class, 'quickComplete']);
|
|
Route::post('transactions/{transaction}/cancel', [TransactionController::class, 'cancel']);
|
|
Route::post('transactions/{transaction}/revert', [TransactionController::class, 'revert']);
|
|
Route::post('transactions/{transaction}/duplicate', [TransactionController::class, 'duplicate']);
|
|
|
|
// Divisão de transações
|
|
Route::post('transactions/{transaction}/split', [TransactionController::class, 'split']);
|
|
Route::post('transactions/{transaction}/unsplit', [TransactionController::class, 'unsplit']);
|
|
Route::get('transactions/{transaction}/splits', [TransactionController::class, 'getSplits']);
|
|
|
|
// Transferências entre contas
|
|
Route::post('transactions/transfer', [TransactionController::class, 'transfer']);
|
|
Route::post('transactions/{transaction}/unlink-transfer', [TransactionController::class, 'unlinkTransfer']);
|
|
|
|
// Conciliação com Passivos
|
|
Route::get('transactions/{transaction}/liability-installments', [TransactionController::class, 'findLiabilityInstallments']);
|
|
Route::post('transactions/{transaction}/reconcile-liability', [TransactionController::class, 'reconcileWithLiability']);
|
|
|
|
// ============================================
|
|
// Importação de Extratos (Bank Statement Import)
|
|
// ============================================
|
|
Route::post('import/upload', [ImportController::class, 'upload']);
|
|
Route::post('import/headers', [ImportController::class, 'getHeaders']);
|
|
Route::post('import/process', [ImportController::class, 'import']);
|
|
Route::get('import/mappings', [ImportController::class, 'mappings']);
|
|
Route::get('import/mappings/{id}', [ImportController::class, 'getMapping']);
|
|
Route::put('import/mappings/{id}', [ImportController::class, 'updateMapping']);
|
|
Route::delete('import/mappings/{id}', [ImportController::class, 'deleteMapping']);
|
|
Route::get('import/presets', [ImportController::class, 'presets']);
|
|
Route::post('import/presets/create', [ImportController::class, 'createFromPreset']);
|
|
Route::get('import/history', [ImportController::class, 'history']);
|
|
Route::get('import/fields', [ImportController::class, 'fields']);
|
|
|
|
// Detecção de Transferências entre Contas (Transfer Detection)
|
|
// ============================================================
|
|
Route::get('transfer-detection', [TransferDetectionController::class, 'index']);
|
|
Route::get('transfer-detection/stats', [TransferDetectionController::class, 'stats']);
|
|
Route::get('transfer-detection/{transaction}/pairs', [TransferDetectionController::class, 'findPairs']);
|
|
Route::post('transfer-detection/confirm', [TransferDetectionController::class, 'confirm']);
|
|
Route::post('transfer-detection/confirm-batch', [TransferDetectionController::class, 'confirmBatch']);
|
|
Route::post('transfer-detection/ignore', [TransferDetectionController::class, 'ignore']);
|
|
Route::post('transfer-detection/delete-both', [TransferDetectionController::class, 'deleteBoth']);
|
|
|
|
// Detecção de Reembolsos (Refund Detection)
|
|
// ============================================================
|
|
Route::get('refund-detection', [TransferDetectionController::class, 'refunds']);
|
|
Route::post('refund-detection/confirm', [TransferDetectionController::class, 'confirmRefund']);
|
|
Route::post('refund-detection/confirm-batch', [TransferDetectionController::class, 'confirmRefundBatch']);
|
|
Route::post('refund-detection/ignore', [TransferDetectionController::class, 'ignoreRefund']);
|
|
Route::post('refund-detection/undo', [TransferDetectionController::class, 'undoRefund']);
|
|
|
|
// ============================================
|
|
// Dashboard
|
|
// ============================================
|
|
Route::get('dashboard/summary', [DashboardController::class, 'summary']);
|
|
Route::get('dashboard/cashflow', [DashboardController::class, 'cashflow']);
|
|
Route::get('dashboard/expenses-by-category', [DashboardController::class, 'expensesByCategory']);
|
|
Route::get('dashboard/income-by-category', [DashboardController::class, 'incomeByCategory']);
|
|
Route::get('dashboard/payment-variances', [DashboardController::class, 'paymentVariances']);
|
|
Route::get('dashboard/calendar', [DashboardController::class, 'calendar']);
|
|
Route::get('dashboard/calendar-day', [DashboardController::class, 'calendarDay']);
|
|
Route::get('dashboard/upcoming', [DashboardController::class, 'upcomingTransactions']);
|
|
Route::get('dashboard/overdue', [DashboardController::class, 'overdueTransactions']);
|
|
|
|
// ============================================
|
|
// Transações Recorrentes (Recurring Transactions)
|
|
// ============================================
|
|
// Rotas de listagem geral
|
|
Route::get('recurring/frequencies', [RecurringTemplateController::class, 'frequencies']);
|
|
Route::get('recurring/pending', [RecurringTemplateController::class, 'allPendingInstances']);
|
|
Route::get('recurring/overdue', [RecurringTemplateController::class, 'overdueInstances']);
|
|
Route::get('recurring/due-soon', [RecurringTemplateController::class, 'dueSoonInstances']);
|
|
Route::post('recurring/regenerate-all', [RecurringTemplateController::class, 'regenerateAll']);
|
|
|
|
// Criar a partir de transação existente
|
|
Route::post('recurring/from-transaction', [RecurringTemplateController::class, 'createFromTransaction']);
|
|
|
|
// Templates CRUD
|
|
Route::apiResource('recurring', RecurringTemplateController::class)->parameters(['recurring' => 'recurringTemplate']);
|
|
|
|
// Ações em templates
|
|
Route::post('recurring/{recurringTemplate}/pause', [RecurringTemplateController::class, 'pause']);
|
|
Route::post('recurring/{recurringTemplate}/resume', [RecurringTemplateController::class, 'resume']);
|
|
Route::get('recurring/{recurringTemplate}/instances', [RecurringTemplateController::class, 'instances']);
|
|
|
|
// Ações em instâncias
|
|
Route::post('recurring-instances/{recurringInstance}/pay', [RecurringTemplateController::class, 'markAsPaid']);
|
|
Route::post('recurring-instances/{recurringInstance}/reconcile', [RecurringTemplateController::class, 'reconcile']);
|
|
Route::get('recurring-instances/{recurringInstance}/candidates', [RecurringTemplateController::class, 'findCandidates']);
|
|
Route::post('recurring-instances/{recurringInstance}/skip', [RecurringTemplateController::class, 'skip']);
|
|
Route::post('recurring-instances/{recurringInstance}/cancel', [RecurringTemplateController::class, 'cancel']);
|
|
Route::put('recurring-instances/{recurringInstance}', [RecurringTemplateController::class, 'updateInstance']);
|
|
|
|
// ============================================
|
|
// Business - Precificação de Produtos (Admin Only)
|
|
// ============================================
|
|
Route::middleware('admin.only')->group(function () {
|
|
// User Management (Admin Only)
|
|
Route::get('admin/users/summary', [UserManagementController::class, 'summary']);
|
|
Route::get('admin/users', [UserManagementController::class, 'index']);
|
|
Route::post('admin/users', [UserManagementController::class, 'store']);
|
|
Route::get('admin/users/{id}', [UserManagementController::class, 'show']);
|
|
Route::put('admin/users/{id}', [UserManagementController::class, 'update']);
|
|
Route::post('admin/users/{id}/reset-password', [UserManagementController::class, 'resetPassword']);
|
|
Route::post('admin/users/{id}/change-plan', [UserManagementController::class, 'changePlan']);
|
|
Route::delete('admin/users/{id}', [UserManagementController::class, 'destroy']);
|
|
|
|
// Configurações de Negócio (Markup)
|
|
Route::get('business-settings/default', [BusinessSettingController::class, 'getDefault']);
|
|
Route::apiResource('business-settings', BusinessSettingController::class);
|
|
Route::post('business-settings/{id}/recalculate-markup', [BusinessSettingController::class, 'recalculateMarkup']);
|
|
Route::post('business-settings/{id}/simulate-price', [BusinessSettingController::class, 'simulatePrice']);
|
|
|
|
// Fichas Técnicas de Produtos (CMV)
|
|
Route::get('product-sheets/categories', [ProductSheetController::class, 'categories']);
|
|
Route::get('product-sheets/item-types', [ProductSheetController::class, 'itemTypes']);
|
|
Route::apiResource('product-sheets', ProductSheetController::class);
|
|
Route::post('product-sheets/{id}/items', [ProductSheetController::class, 'addItem']);
|
|
Route::put('product-sheets/{sheetId}/items/{itemId}', [ProductSheetController::class, 'updateItem']);
|
|
Route::delete('product-sheets/{sheetId}/items/{itemId}', [ProductSheetController::class, 'removeItem']);
|
|
Route::post('product-sheets/{id}/recalculate-price', [ProductSheetController::class, 'recalculatePrice']);
|
|
Route::post('product-sheets/{id}/duplicate', [ProductSheetController::class, 'duplicate']);
|
|
|
|
// Fichas Técnicas de Serviços (CSV - Custo do Serviço Vendido)
|
|
Route::get('service-sheets/categories', [ServiceSheetController::class, 'categories']);
|
|
Route::get('service-sheets/item-types', [ServiceSheetController::class, 'itemTypes']);
|
|
Route::post('service-sheets/simulate', [ServiceSheetController::class, 'simulate']);
|
|
Route::apiResource('service-sheets', ServiceSheetController::class);
|
|
Route::post('service-sheets/{id}/items', [ServiceSheetController::class, 'addItem']);
|
|
Route::put('service-sheets/{sheetId}/items/{itemId}', [ServiceSheetController::class, 'updateItem']);
|
|
Route::delete('service-sheets/{sheetId}/items/{itemId}', [ServiceSheetController::class, 'removeItem']);
|
|
Route::post('service-sheets/{id}/duplicate', [ServiceSheetController::class, 'duplicate']);
|
|
|
|
// Campanhas Promocionais (Black Friday, etc.)
|
|
Route::get('campaigns/presets', [PromotionalCampaignController::class, 'presets']);
|
|
Route::post('campaigns/preview', [PromotionalCampaignController::class, 'preview']);
|
|
Route::apiResource('campaigns', PromotionalCampaignController::class);
|
|
Route::post('campaigns/{id}/duplicate', [PromotionalCampaignController::class, 'duplicate']);
|
|
Route::post('campaigns/{id}/products', [PromotionalCampaignController::class, 'addProducts']);
|
|
Route::delete('campaigns/{id}/products', [PromotionalCampaignController::class, 'removeProducts']);
|
|
Route::put('campaigns/{campaignId}/products/{productId}', [PromotionalCampaignController::class, 'updateProductDiscount']);
|
|
}); // End admin.only group
|
|
|
|
// ============================================
|
|
// Metas Financieras (Financial Goals)
|
|
// ============================================
|
|
Route::apiResource('financial-goals', FinancialGoalController::class);
|
|
Route::post('financial-goals/{id}/contributions', [FinancialGoalController::class, 'addContribution']);
|
|
Route::delete('financial-goals/{goalId}/contributions/{contributionId}', [FinancialGoalController::class, 'removeContribution']);
|
|
|
|
// ============================================
|
|
// Presupuestos (Budgets) - Com limite de plano
|
|
// ============================================
|
|
Route::get('budgets/available-categories', [BudgetController::class, 'availableCategories']);
|
|
Route::get('budgets/year-summary', [BudgetController::class, 'yearSummary']);
|
|
Route::post('budgets/copy-to-next-month', [BudgetController::class, 'copyToNextMonth']);
|
|
Route::post('budgets', [BudgetController::class, 'store'])->middleware('plan.limits:budgets');
|
|
Route::apiResource('budgets', BudgetController::class)->except(['store']);
|
|
|
|
// ============================================
|
|
// Reportes (Reports)
|
|
// ============================================
|
|
Route::prefix('reports')->group(function () {
|
|
Route::get('summary', [ReportController::class, 'summary']);
|
|
Route::get('executive-summary', [ReportController::class, 'executiveSummary']);
|
|
Route::get('by-category', [ReportController::class, 'byCategory']);
|
|
Route::get('by-cost-center', [ReportController::class, 'byCostCenter']);
|
|
Route::get('monthly-evolution', [ReportController::class, 'monthlyEvolution']);
|
|
Route::get('by-day-of-week', [ReportController::class, 'byDayOfWeek']);
|
|
Route::get('top-expenses', [ReportController::class, 'topExpenses']);
|
|
Route::get('compare-periods', [ReportController::class, 'comparePeriods']);
|
|
Route::get('accounts', [ReportController::class, 'accountsReport']);
|
|
Route::get('projection', [ReportController::class, 'projection']);
|
|
Route::get('projection-chart', [ReportController::class, 'projectionChart']);
|
|
Route::get('recurring', [ReportController::class, 'recurringReport']);
|
|
Route::get('liabilities', [ReportController::class, 'liabilities']);
|
|
Route::get('future-transactions', [ReportController::class, 'futureTransactions']);
|
|
Route::get('overdue', [ReportController::class, 'overdueTransactions']);
|
|
});
|
|
|
|
// ============================================
|
|
// Salud Financiera (Financial Health)
|
|
// ============================================
|
|
Route::get('financial-health', [FinancialHealthController::class, 'index']);
|
|
Route::get('financial-health/history', [FinancialHealthController::class, 'history']);
|
|
|
|
// ============================================
|
|
// Preferências do Usuário (User Preferences)
|
|
// ============================================
|
|
Route::get('preferences', [UserPreferenceController::class, 'index']);
|
|
Route::put('preferences', [UserPreferenceController::class, 'update']);
|
|
Route::post('preferences/test-notification', [UserPreferenceController::class, 'testNotification']);
|
|
|
|
// ============================================
|
|
// Site Settings (Admin only - Configurações do Site)
|
|
// ============================================
|
|
Route::middleware('admin.only')->prefix('admin/site-settings')->group(function () {
|
|
Route::get('/', [SiteSettingsController::class, 'index']);
|
|
Route::get('/cnxifly/status', [SiteSettingsController::class, 'getCnxiflyStatus']);
|
|
Route::post('/cnxifly/toggle', [SiteSettingsController::class, 'toggleCnxiflyPage']);
|
|
Route::post('/cnxifly/deploy', [SiteSettingsController::class, 'deployCnxiflyPage']);
|
|
Route::get('/{key}', [SiteSettingsController::class, 'show']);
|
|
Route::put('/{key}', [SiteSettingsController::class, 'update']);
|
|
});
|
|
});
|
|
|