webmoney/backend/app/Models/BusinessSetting.php

368 lines
11 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class BusinessSetting extends Model
{
use HasFactory;
protected $fillable = [
'user_id',
'currency',
'name',
'business_type',
'employees_count',
'hours_per_week',
'working_days_per_week',
'working_days_per_month',
'productivity_rate',
'monthly_revenue',
'fixed_expenses',
'tax_rate',
'price_includes_tax',
'vat_rate',
'sales_commission',
'card_fee',
'other_variable_costs',
'investment_rate',
'profit_margin',
'markup_factor',
'is_active',
];
protected $casts = [
'business_type' => 'string',
'employees_count' => 'integer',
'hours_per_week' => 'decimal:2',
'working_days_per_week' => 'integer',
'working_days_per_month' => 'integer',
'productivity_rate' => 'decimal:2',
'monthly_revenue' => 'decimal:2',
'fixed_expenses' => 'decimal:2',
'tax_rate' => 'decimal:2',
'price_includes_tax' => 'boolean',
'vat_rate' => 'decimal:2',
'sales_commission' => 'decimal:2',
'card_fee' => 'decimal:2',
'other_variable_costs' => 'decimal:2',
'investment_rate' => 'decimal:2',
'profit_margin' => 'decimal:2',
'markup_factor' => 'decimal:4',
'is_active' => 'boolean',
];
/**
* Derived attributes - calculated from hours_per_week
*/
protected $appends = ['hours_per_day', 'productive_hours', 'fixed_cost_per_hour'];
/**
* Calcula horas por dia a partir de horas por semana
* hours_per_day = hours_per_week / working_days_per_week
*/
public function getHoursPerDayAttribute(): float
{
$hoursPerWeek = (float) ($this->hours_per_week ?? 40);
$daysPerWeek = (int) ($this->working_days_per_week ?? 5);
return $daysPerWeek > 0 ? round($hoursPerWeek / $daysPerWeek, 2) : 8;
}
/**
* Relacionamento com usuário
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
/**
* Fichas técnicas de produtos que usam esta configuração
*/
public function productSheets(): HasMany
{
return $this->hasMany(ProductSheet::class);
}
/**
* Fichas técnicas de serviços que usam esta configuração
*/
public function serviceSheets(): HasMany
{
return $this->hasMany(ServiceSheet::class);
}
/**
* Calcula as horas produtivas mensais
*
* Formula baseada em horas por semana:
* Horas Produtivas = Funcionários × (Horas/Semana × 4.33 semanas/mês) × Produtividade%
*
* Ou usando dias do mês:
* Horas Produtivas = Funcionários × (Horas/Semana / Dias/Semana × Dias/Mês) × Produtividade%
*
* @return float
*/
public function getProductiveHoursAttribute(): float
{
$employees = (int) ($this->employees_count ?? 1);
$hoursPerWeek = (float) ($this->hours_per_week ?? 40);
$daysPerWeek = (int) ($this->working_days_per_week ?? 5);
$daysPerMonth = (int) ($this->working_days_per_month ?? 22);
$productivity = (float) ($this->productivity_rate ?? 80) / 100;
// Calcula horas por dia a partir de horas por semana
$hoursPerDay = $daysPerWeek > 0 ? $hoursPerWeek / $daysPerWeek : 8;
// Horas produtivas = funcionários × horas/dia × dias/mês × produtividade
return round($employees * $hoursPerDay * $daysPerMonth * $productivity, 2);
}
/**
* Calcula o gasto fixo por hora
* Gasto Fixo/Hora = Despesas Fixas / Horas Produtivas
*
* @return float
*/
public function getFixedCostPerHourAttribute(): float
{
$productiveHours = $this->productive_hours;
if ($productiveHours <= 0) {
return 0;
}
return round((float) $this->fixed_expenses / $productiveHours, 2);
}
/**
* Calcula o gasto fixo por minuto
*
* @return float
*/
public function getFixedCostPerMinuteAttribute(): float
{
return round($this->fixed_cost_per_hour / 60, 4);
}
/**
* Calcula o preço de venda de um serviço
* Preço = (Gasto Fixo/Hora × Duração em Horas + CSV) × Markup
*
* Para B2C, ainda adiciona o IVA no final
*
* @param float $durationMinutes Duração do serviço em minutos
* @param float $csv Custo do Serviço Vendido (insumos)
* @return float
*/
public function calculateServicePrice(float $durationMinutes, float $csv): float
{
$markup = $this->markup_factor ?? $this->calculateMarkup();
if ($markup <= 0) {
return 0;
}
// Gasto fixo proporcional ao tempo do serviço
$fixedCostPortion = $this->fixed_cost_per_minute * $durationMinutes;
// Preço base = (Gasto Fixo + CSV) × Markup
$priceWithoutTax = ($fixedCostPortion + $csv) * $markup;
// Se B2C, adiciona IVA
if ($this->price_includes_tax) {
$vatRate = (float) ($this->vat_rate ?? 0);
return round($priceWithoutTax * (1 + $vatRate / 100), 2);
}
return round($priceWithoutTax, 2);
}
/**
* Calcula o percentual de despesas fixas sobre a receita
*
* @return float
*/
public function getFixedExpensesRateAttribute(): float
{
if ($this->monthly_revenue <= 0) {
return 0;
}
return round(($this->fixed_expenses / $this->monthly_revenue) * 100, 2);
}
/**
* Calcula o total de custos variáveis (sem CMV)
* Para B2C (price_includes_tax=true), o tax_rate NÃO entra aqui pois é adicionado ao final
* Para B2B (price_includes_tax=false), o tax_rate é uma dedução do preço
*
* @return float
*/
public function getTotalVariableCostsAttribute(): float
{
$costs = $this->sales_commission +
$this->card_fee +
$this->other_variable_costs;
// Se B2B (preço NÃO inclui imposto), adiciona tax_rate como custo
if (!$this->price_includes_tax) {
$costs += $this->tax_rate;
}
return $costs;
}
/**
* Calcula o fator de Markup usando a fórmula:
* Markup = 1 / (1 - (Despesas Fixas % + Custos Variáveis % + Investimento % + Lucro %))
*
* @return float
*/
public function calculateMarkup(): float
{
// Converter percentuais para decimais
$fixedExpensesRate = $this->fixed_expenses_rate / 100;
$variableCosts = $this->total_variable_costs / 100;
$investmentRate = $this->investment_rate / 100;
$profitMargin = $this->profit_margin / 100;
// Total de deduções
$totalDeductions = $fixedExpensesRate + $variableCosts + $investmentRate + $profitMargin;
// Verificar se é possível calcular (não pode ser >= 100%)
if ($totalDeductions >= 1) {
return 0; // Markup inválido - deduções >= 100%
}
// Fórmula: Markup = 1 / (1 - deduções)
$markup = 1 / (1 - $totalDeductions);
return round($markup, 4);
}
/**
* Recalcula e salva o markup
*
* @return float
*/
public function recalculateMarkup(): float
{
$this->markup_factor = $this->calculateMarkup();
$this->save();
return $this->markup_factor;
}
/**
* Calcula o preço de venda para um CMV dado
*
* B2B (price_includes_tax=false): Preço = CMV × Markup (imposto já está nas deduções)
* B2C (price_includes_tax=true): Preço = CMV × Markup × (1 + VAT%) (imposto adicionado ao final)
*
* @param float $cmv Custo da Mercadoria Vendida
* @return float
*/
public function calculateSalePrice(float $cmv): float
{
$markup = $this->markup_factor ?? $this->calculateMarkup();
if ($markup <= 0) {
return 0;
}
$priceWithoutTax = $cmv * $markup;
// Se B2C (preço inclui imposto), adiciona o IVA/VAT ao preço final
if ($this->price_includes_tax) {
$vatRate = (float) ($this->vat_rate ?? 0);
return round($priceWithoutTax * (1 + $vatRate / 100), 2);
}
return round($priceWithoutTax, 2);
}
/**
* Calcula o preço SEM impostos para um CMV dado
* Útil para mostrar o preço base antes do IVA
*
* @param float $cmv Custo da Mercadoria Vendida
* @return float
*/
public function calculatePriceWithoutTax(float $cmv): float
{
$markup = $this->markup_factor ?? $this->calculateMarkup();
if ($markup <= 0) {
return 0;
}
return round($cmv * $markup, 2);
}
/**
* Extrai o valor do IVA de um preço final (B2C)
*
* @param float $finalPrice Preço final com IVA
* @return float
*/
public function extractVat(float $finalPrice): float
{
if (!$this->price_includes_tax) {
return 0;
}
$vatRate = (float) ($this->vat_rate ?? 0);
if ($vatRate <= 0) {
return 0;
}
$priceWithoutVat = $finalPrice / (1 + $vatRate / 100);
return round($finalPrice - $priceWithoutVat, 2);
}
/**
* Retorna o breakdown do Markup para exibição
*
* @return array
*/
public function getMarkupBreakdownAttribute(): array
{
return [
'fixed_expenses_rate' => $this->fixed_expenses_rate,
'tax_rate' => (float) $this->tax_rate,
'price_includes_tax' => (bool) $this->price_includes_tax,
'vat_rate' => (float) ($this->vat_rate ?? 21),
'sales_commission' => (float) $this->sales_commission,
'card_fee' => (float) $this->card_fee,
'other_variable_costs' => (float) $this->other_variable_costs,
'total_variable_costs' => $this->total_variable_costs,
'investment_rate' => (float) $this->investment_rate,
'profit_margin' => (float) $this->profit_margin,
'total_deductions' => $this->fixed_expenses_rate + $this->total_variable_costs + $this->investment_rate + $this->profit_margin,
'markup_factor' => (float) ($this->markup_factor ?? $this->calculateMarkup()),
];
}
/**
* Scope para buscar configurações ativas do usuário
*/
public function scopeOfUser($query, $userId)
{
return $query->where('user_id', $userId);
}
/**
* Scope para buscar apenas configurações ativas
*/
public function scopeActive($query)
{
return $query->where('is_active', true);
}
}