- Add business_settings table for Markup configuration - Add product_sheets table for product technical sheets (CMV) - Add product_sheet_items table for cost components - Create BusinessSetting model with calculateMarkup() method - Create ProductSheet model with recalculateCmv() method - Create ProductSheetItem model for cost breakdown - Add BusinessSettingController with CRUD + simulate-price endpoint - Add ProductSheetController with CRUD + items management + duplicate - Add Business page with 3 tabs (Settings, Products, Calculator) - Add BusinessSettingsTab component with markup cards - Add ProductSheetsTab component with product grid - Add PriceCalculatorTab component with interactive simulator - Add i18n translations in ES, PT-BR, EN - Multi-currency support (EUR, BRL, USD)
216 lines
13 KiB
PHP
216 lines
13 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;
|
|
|
|
// 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']);
|
|
});
|
|
|