- 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
7.4 KiB
PHP
216 lines
7.4 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Api;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\BusinessSetting;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Validator;
|
|
|
|
class BusinessSettingController extends Controller
|
|
{
|
|
/**
|
|
* Lista todas as configurações de negócio do usuário
|
|
*/
|
|
public function index(Request $request): JsonResponse
|
|
{
|
|
$userId = $request->user()->id;
|
|
|
|
$settings = BusinessSetting::ofUser($userId)
|
|
->orderBy('is_active', 'desc')
|
|
->orderBy('name')
|
|
->get()
|
|
->map(function ($setting) {
|
|
return array_merge($setting->toArray(), [
|
|
'fixed_expenses_rate' => $setting->fixed_expenses_rate,
|
|
'total_variable_costs' => $setting->total_variable_costs,
|
|
'markup_breakdown' => $setting->markup_breakdown,
|
|
]);
|
|
});
|
|
|
|
return response()->json($settings);
|
|
}
|
|
|
|
/**
|
|
* Cria uma nova configuração de negócio
|
|
*/
|
|
public function store(Request $request): JsonResponse
|
|
{
|
|
$validator = Validator::make($request->all(), [
|
|
'name' => 'required|string|max:255',
|
|
'currency' => 'required|string|size:3',
|
|
'monthly_revenue' => 'required|numeric|min:0',
|
|
'fixed_expenses' => 'required|numeric|min:0',
|
|
'tax_rate' => 'required|numeric|min:0|max:100',
|
|
'sales_commission' => 'required|numeric|min:0|max:100',
|
|
'card_fee' => 'required|numeric|min:0|max:100',
|
|
'other_variable_costs' => 'nullable|numeric|min:0|max:100',
|
|
'investment_rate' => 'required|numeric|min:0|max:100',
|
|
'profit_margin' => 'required|numeric|min:0|max:100',
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json(['errors' => $validator->errors()], 422);
|
|
}
|
|
|
|
$data = $validator->validated();
|
|
$data['user_id'] = $request->user()->id;
|
|
$data['other_variable_costs'] = $data['other_variable_costs'] ?? 0;
|
|
|
|
$setting = BusinessSetting::create($data);
|
|
|
|
// Calcular e salvar o markup
|
|
$setting->recalculateMarkup();
|
|
|
|
// Recarregar com os dados calculados
|
|
$setting->refresh();
|
|
|
|
return response()->json(array_merge($setting->toArray(), [
|
|
'fixed_expenses_rate' => $setting->fixed_expenses_rate,
|
|
'total_variable_costs' => $setting->total_variable_costs,
|
|
'markup_breakdown' => $setting->markup_breakdown,
|
|
]), 201);
|
|
}
|
|
|
|
/**
|
|
* Exibe uma configuração específica
|
|
*/
|
|
public function show(Request $request, $id): JsonResponse
|
|
{
|
|
$setting = BusinessSetting::ofUser($request->user()->id)
|
|
->with('productSheets')
|
|
->findOrFail($id);
|
|
|
|
return response()->json(array_merge($setting->toArray(), [
|
|
'fixed_expenses_rate' => $setting->fixed_expenses_rate,
|
|
'total_variable_costs' => $setting->total_variable_costs,
|
|
'markup_breakdown' => $setting->markup_breakdown,
|
|
]));
|
|
}
|
|
|
|
/**
|
|
* Atualiza uma configuração
|
|
*/
|
|
public function update(Request $request, $id): JsonResponse
|
|
{
|
|
$setting = BusinessSetting::ofUser($request->user()->id)->findOrFail($id);
|
|
|
|
$validator = Validator::make($request->all(), [
|
|
'name' => 'sometimes|string|max:255',
|
|
'currency' => 'sometimes|string|size:3',
|
|
'monthly_revenue' => 'sometimes|numeric|min:0',
|
|
'fixed_expenses' => 'sometimes|numeric|min:0',
|
|
'tax_rate' => 'sometimes|numeric|min:0|max:100',
|
|
'sales_commission' => 'sometimes|numeric|min:0|max:100',
|
|
'card_fee' => 'sometimes|numeric|min:0|max:100',
|
|
'other_variable_costs' => 'sometimes|numeric|min:0|max:100',
|
|
'investment_rate' => 'sometimes|numeric|min:0|max:100',
|
|
'profit_margin' => 'sometimes|numeric|min:0|max:100',
|
|
'is_active' => 'sometimes|boolean',
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json(['errors' => $validator->errors()], 422);
|
|
}
|
|
|
|
$setting->update($validator->validated());
|
|
|
|
// Recalcular markup
|
|
$setting->recalculateMarkup();
|
|
|
|
$setting->refresh();
|
|
|
|
return response()->json(array_merge($setting->toArray(), [
|
|
'fixed_expenses_rate' => $setting->fixed_expenses_rate,
|
|
'total_variable_costs' => $setting->total_variable_costs,
|
|
'markup_breakdown' => $setting->markup_breakdown,
|
|
]));
|
|
}
|
|
|
|
/**
|
|
* Remove uma configuração
|
|
*/
|
|
public function destroy(Request $request, $id): JsonResponse
|
|
{
|
|
$setting = BusinessSetting::ofUser($request->user()->id)->findOrFail($id);
|
|
|
|
// Verificar se há fichas técnicas vinculadas
|
|
if ($setting->productSheets()->count() > 0) {
|
|
return response()->json([
|
|
'message' => 'Não é possível excluir: existem fichas técnicas vinculadas a esta configuração.'
|
|
], 422);
|
|
}
|
|
|
|
$setting->delete();
|
|
|
|
return response()->json(['message' => 'Configuração excluída com sucesso']);
|
|
}
|
|
|
|
/**
|
|
* Recalcula o markup de uma configuração
|
|
*/
|
|
public function recalculateMarkup(Request $request, $id): JsonResponse
|
|
{
|
|
$setting = BusinessSetting::ofUser($request->user()->id)->findOrFail($id);
|
|
|
|
$markup = $setting->recalculateMarkup();
|
|
|
|
return response()->json([
|
|
'markup_factor' => $markup,
|
|
'markup_breakdown' => $setting->markup_breakdown,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Simula um preço de venda dado um CMV
|
|
*/
|
|
public function simulatePrice(Request $request, $id): JsonResponse
|
|
{
|
|
$setting = BusinessSetting::ofUser($request->user()->id)->findOrFail($id);
|
|
|
|
$validator = Validator::make($request->all(), [
|
|
'cmv' => 'required|numeric|min:0',
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json(['errors' => $validator->errors()], 422);
|
|
}
|
|
|
|
$cmv = $request->cmv;
|
|
$salePrice = $setting->calculateSalePrice($cmv);
|
|
$contributionMargin = $salePrice - $cmv;
|
|
$contributionMarginPercent = $salePrice > 0 ? round(($contributionMargin / $salePrice) * 100, 2) : 0;
|
|
|
|
return response()->json([
|
|
'cmv' => $cmv,
|
|
'markup_factor' => $setting->markup_factor,
|
|
'sale_price' => $salePrice,
|
|
'contribution_margin' => $contributionMargin,
|
|
'contribution_margin_percent' => $contributionMarginPercent,
|
|
'currency' => $setting->currency,
|
|
'breakdown' => $setting->markup_breakdown,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Retorna a configuração ativa padrão do usuário
|
|
*/
|
|
public function getDefault(Request $request): JsonResponse
|
|
{
|
|
$setting = BusinessSetting::ofUser($request->user()->id)
|
|
->active()
|
|
->first();
|
|
|
|
if (!$setting) {
|
|
return response()->json(['message' => 'Nenhuma configuração ativa encontrada'], 404);
|
|
}
|
|
|
|
return response()->json(array_merge($setting->toArray(), [
|
|
'fixed_expenses_rate' => $setting->fixed_expenses_rate,
|
|
'total_variable_costs' => $setting->total_variable_costs,
|
|
'markup_breakdown' => $setting->markup_breakdown,
|
|
]));
|
|
}
|
|
}
|