|
@@ -0,0 +1,154 @@
|
|
|
|
|
+<?php
|
|
|
|
|
+
|
|
|
|
|
+namespace App\Services;
|
|
|
|
|
+
|
|
|
|
|
+use App\Models\FranchiseeFnmBracket;
|
|
|
|
|
+use App\Models\FranchiseeMaintenanceBracket;
|
|
|
|
|
+use App\Models\FranchiseeRoyaltiesBracket;
|
|
|
|
|
+use App\Models\FranchiseeTbr;
|
|
|
|
|
+use App\Models\FranchiseeUnit;
|
|
|
|
|
+use App\Models\TbrCalculation;
|
|
|
|
|
+use App\Models\Unit;
|
|
|
|
|
+use Illuminate\Support\Facades\DB;
|
|
|
|
|
+use Illuminate\Pagination\LengthAwarePaginator;
|
|
|
|
|
+use Illuminate\Support\Facades\Auth;
|
|
|
|
|
+
|
|
|
|
|
+class TbrCalculationService
|
|
|
|
|
+{
|
|
|
|
|
+ private const ROYALTIES_REVENUE_RATE = 0.08;
|
|
|
|
|
+ private const FNM_REVENUE_RATE = 0.02;
|
|
|
|
|
+ private const EXEMPT_THRESHOLD_MONTH = 3;
|
|
|
|
|
+
|
|
|
|
|
+ public function paginate(int $perPage = 15): LengthAwarePaginator
|
|
|
|
|
+ {
|
|
|
|
|
+ return TbrCalculation::with(['unit', 'user'])
|
|
|
|
|
+ ->orderBy('created_at', 'desc')
|
|
|
|
|
+ ->paginate($perPage);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public function findById(int $id): ?TbrCalculation
|
|
|
|
|
+ {
|
|
|
|
|
+ return TbrCalculation::with([
|
|
|
|
|
+ 'unit',
|
|
|
|
|
+ 'user',
|
|
|
|
|
+ 'royaltiesBracket',
|
|
|
|
|
+ 'fnmBracket',
|
|
|
|
|
+ 'maintenanceBracket',
|
|
|
|
|
+ ])->find($id);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public function calculate(array $data): TbrCalculation
|
|
|
|
|
+ {
|
|
|
|
|
+ return DB::transaction(function () use ($data) {
|
|
|
|
|
+ $unit = Unit::findOrFail($data['unit_id']);
|
|
|
|
|
+
|
|
|
|
|
+ $franchiseeUnit = FranchiseeUnit::where('unit_id', $unit->id)->firstOrFail();
|
|
|
|
|
+
|
|
|
|
|
+ $franchiseeTbr = FranchiseeTbr::where('franchisee_id', $franchiseeUnit->franchisee_id)
|
|
|
|
|
+ ->where('year', now()->year)
|
|
|
|
|
+ ->firstOrFail();
|
|
|
|
|
+
|
|
|
|
|
+ $tbrValue = (float) $franchiseeTbr->tbr_value;
|
|
|
|
|
+ $contractMonth = (int) $data['contract_month_reference'];
|
|
|
|
|
+ $revenueValue = (float) $data['revenue_value'];
|
|
|
|
|
+
|
|
|
|
|
+ $royaltiesBracket = $this->findBracket(FranchiseeRoyaltiesBracket::class, $franchiseeUnit->franchisee_id, $contractMonth);
|
|
|
|
|
+ $fnmBracket = $this->findBracket(FranchiseeFnmBracket::class, $franchiseeUnit->franchisee_id, $contractMonth);
|
|
|
|
|
+ $maintenanceBracket = $this->findBracket(FranchiseeMaintenanceBracket::class, $franchiseeUnit->franchisee_id, $contractMonth);
|
|
|
|
|
+
|
|
|
|
|
+ $royaltiesBracketValue = round((float) $royaltiesBracket->percentage * $tbrValue, 2);
|
|
|
|
|
+ $fnmBracketValue = round((float) $fnmBracket->percentage * $tbrValue, 2);
|
|
|
|
|
+ $maintenanceBracketValue = round((float) $maintenanceBracket->percentage * $tbrValue, 2);
|
|
|
|
|
+
|
|
|
|
|
+ [$royaltiesEffectiveValue, $royaltiesEffectivePercentage, $royaltiesAppliedCriteria,
|
|
|
|
|
+ $fnmEffectiveValue, $fnmEffectivePercentage] = $this->resolveEffectiveValues(
|
|
|
|
|
+ $contractMonth,
|
|
|
|
|
+ $revenueValue,
|
|
|
|
|
+ $royaltiesBracket,
|
|
|
|
|
+ $royaltiesBracketValue,
|
|
|
|
|
+ $fnmBracket,
|
|
|
|
|
+ $fnmBracketValue,
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ $maintenanceEffectiveValue = $maintenanceBracketValue;
|
|
|
|
|
+ $maintenanceEffectivePercentage = (float) $maintenanceBracket->percentage;
|
|
|
|
|
+
|
|
|
|
|
+ $bracketSubtotal = round($royaltiesBracketValue + $fnmBracketValue + $maintenanceBracketValue, 2);
|
|
|
|
|
+ $subtotal = round($royaltiesEffectiveValue + $fnmEffectiveValue + $maintenanceEffectiveValue, 2);
|
|
|
|
|
+
|
|
|
|
|
+ return TbrCalculation::create([
|
|
|
|
|
+ 'unit_id' => $unit->id,
|
|
|
|
|
+ 'revenue_value' => $revenueValue,
|
|
|
|
|
+ 'contract_month_reference' => $contractMonth,
|
|
|
|
|
+ 'tbr_value' => $tbrValue,
|
|
|
|
|
+ 'royalties_bracket_id' => $royaltiesBracket->id,
|
|
|
|
|
+ 'royalties_bracket_percentage' => $royaltiesBracket->percentage,
|
|
|
|
|
+ 'royalties_bracket_value' => $royaltiesBracketValue,
|
|
|
|
|
+ 'fnm_bracket_id' => $fnmBracket->id,
|
|
|
|
|
+ 'fnm_bracket_percentage' => $fnmBracket->percentage,
|
|
|
|
|
+ 'fnm_bracket_value' => $fnmBracketValue,
|
|
|
|
|
+ 'maintenance_bracket_id' => $maintenanceBracket->id,
|
|
|
|
|
+ 'maintenance_bracket_percentage' => $maintenanceBracket->percentage,
|
|
|
|
|
+ 'maintenance_bracket_value' => $maintenanceBracketValue,
|
|
|
|
|
+ 'royalties_effective_percentage' => $royaltiesEffectivePercentage,
|
|
|
|
|
+ 'royalties_effective_value' => $royaltiesEffectiveValue,
|
|
|
|
|
+ 'fnm_effective_percentage' => $fnmEffectivePercentage,
|
|
|
|
|
+ 'fnm_effective_value' => $fnmEffectiveValue,
|
|
|
|
|
+ 'maintenance_effective_percentage' => $maintenanceEffectivePercentage,
|
|
|
|
|
+ 'maintenance_effective_value' => $maintenanceEffectiveValue,
|
|
|
|
|
+ 'bracket_subtotal' => $bracketSubtotal,
|
|
|
|
|
+ 'subtotal' => $subtotal,
|
|
|
|
|
+ 'final_value' => $subtotal,
|
|
|
|
|
+ 'user_id' => Auth::id(),
|
|
|
|
|
+ 'royalties_applied_criteria' => $royaltiesAppliedCriteria,
|
|
|
|
|
+ 'receivable_generated' => false,
|
|
|
|
|
+ ]);
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private function resolveEffectiveValues(
|
|
|
|
|
+ int $contractMonth,
|
|
|
|
|
+ float $revenueValue,
|
|
|
|
|
+ FranchiseeRoyaltiesBracket $royaltiesBracket,
|
|
|
|
|
+ float $royaltiesBracketValue,
|
|
|
|
|
+ FranchiseeFnmBracket $fnmBracket,
|
|
|
|
|
+ float $fnmBracketValue,
|
|
|
|
|
+ ): array {
|
|
|
|
|
+ if ($contractMonth <= self::EXEMPT_THRESHOLD_MONTH) {
|
|
|
|
|
+ return [0.0, 0.0, 'tbr_fixo', 0.0, 0.0];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $royaltiesFromRevenue = round(self::ROYALTIES_REVENUE_RATE * $revenueValue, 2);
|
|
|
|
|
+ $fnmFromRevenue = round(self::FNM_REVENUE_RATE * $revenueValue, 2);
|
|
|
|
|
+
|
|
|
|
|
+ if ($royaltiesBracketValue >= $royaltiesFromRevenue) {
|
|
|
|
|
+ $royaltiesEffectiveValue = $royaltiesBracketValue;
|
|
|
|
|
+ $royaltiesEffectivePercentage = (float) $royaltiesBracket->percentage;
|
|
|
|
|
+ $royaltiesAppliedCriteria = 'tbr_fixo';
|
|
|
|
|
+ } else {
|
|
|
|
|
+ $royaltiesEffectiveValue = $royaltiesFromRevenue;
|
|
|
|
|
+ $royaltiesEffectivePercentage = self::ROYALTIES_REVENUE_RATE;
|
|
|
|
|
+ $royaltiesAppliedCriteria = 'percentual_faturamento';
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if ($fnmBracketValue >= $fnmFromRevenue) {
|
|
|
|
|
+ $fnmEffectiveValue = $fnmBracketValue;
|
|
|
|
|
+ $fnmEffectivePercentage = (float) $fnmBracket->percentage;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ $fnmEffectiveValue = $fnmFromRevenue;
|
|
|
|
|
+ $fnmEffectivePercentage = self::FNM_REVENUE_RATE;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return [$royaltiesEffectiveValue, $royaltiesEffectivePercentage, $royaltiesAppliedCriteria, $fnmEffectiveValue, $fnmEffectivePercentage];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private function findBracket(string $modelClass, int $franchiseeId, int $contractMonth): mixed
|
|
|
|
|
+ {
|
|
|
|
|
+ return $modelClass::where('franchisee_id', $franchiseeId)
|
|
|
|
|
+ ->where('start_month', '<=', $contractMonth)
|
|
|
|
|
+ ->where(function ($q) use ($contractMonth) {
|
|
|
|
|
+ $q->whereNull('end_month')->orWhere('end_month', '>=', $contractMonth);
|
|
|
|
|
+ })
|
|
|
|
|
+ ->firstOrFail();
|
|
|
|
|
+ }
|
|
|
|
|
+}
|