webmoney/backend/routes/api.php
marcoitaloesp-ai 19dcdce262
feat: Add daily due payments notification system with user preferences
## New Features
- Email notifications for overdue and upcoming payments
- User preferences page for notification settings
- Daily scheduler to send alerts at user-configured time
- Smart analysis: payable items, transfer suggestions between accounts

## Backend
- Migration for user_preferences table
- SendDuePaymentsAlert Artisan command
- DuePaymentsAlert Mailable with HTML/text templates
- UserPreferenceController with test-notification endpoint
- Scheduler config for notify:due-payments command

## Frontend
- Preferences.jsx page with notification toggle
- API service for preferences
- Route and menu link for settings
- Translations (PT-BR, EN, ES)

## Server
- Cron configured for Laravel scheduler

Version: 1.44.5
2025-12-17 09:57:40 +00:00

291 lines
18 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\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;
// Public routes with rate limiting
Route::post('/register', [AuthController::class, 'register'])->middleware('throttle:register');
Route::post('/login', [AuthController::class, 'login'])->middleware('throttle:login');
// 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::get('/user', function (Request $request) {
return $request->user();
});
// ============================================
// Contas (Accounts)
// ============================================
// 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
Route::apiResource('accounts', AccountController::class);
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)
// ============================================
// 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
Route::apiResource('categories', CategoryController::class);
// 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::post('liability-accounts/import', [LiabilityAccountController::class, 'import']);
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']);
// ============================================
// Transações (Transactions)
// ============================================
Route::apiResource('transactions', TransactionController::class);
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
// ============================================
// 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']);
// ============================================
// 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)
// ============================================
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::apiResource('budgets', BudgetController::class);
// ============================================
// 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']);
});