TbrCalculationService.php 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. <?php
  2. namespace App\Services;
  3. use App\Models\FranchiseeFnmBracket;
  4. use App\Models\FranchiseeMaintenanceBracket;
  5. use App\Models\FranchiseeRoyaltiesBracket;
  6. use App\Models\FranchiseeTbr;
  7. use App\Models\FranchiseeUnit;
  8. use App\Models\TbrCalculation;
  9. use App\Models\Unit;
  10. use Illuminate\Support\Facades\DB;
  11. use Illuminate\Pagination\LengthAwarePaginator;
  12. use Illuminate\Support\Facades\Auth;
  13. class TbrCalculationService
  14. {
  15. private const ROYALTIES_REVENUE_RATE = 0.08;
  16. private const FNM_REVENUE_RATE = 0.02;
  17. private const EXEMPT_THRESHOLD_MONTH = 3;
  18. public function paginate(int $perPage = 15): LengthAwarePaginator
  19. {
  20. return TbrCalculation::with(['unit', 'user'])
  21. ->orderBy('created_at', 'desc')
  22. ->paginate($perPage);
  23. }
  24. public function findById(int $id): ?TbrCalculation
  25. {
  26. return TbrCalculation::with([
  27. 'unit',
  28. 'user',
  29. 'royaltiesBracket',
  30. 'fnmBracket',
  31. 'maintenanceBracket',
  32. ])->find($id);
  33. }
  34. public function calculate(array $data): TbrCalculation
  35. {
  36. return DB::transaction(function () use ($data) {
  37. $unit = Unit::findOrFail($data['unit_id']);
  38. $franchiseeUnit = FranchiseeUnit::where('unit_id', $unit->id)->firstOrFail();
  39. $franchiseeTbr = FranchiseeTbr::where('franchisee_id', $franchiseeUnit->franchisee_id)
  40. ->where('year', now()->year)
  41. ->firstOrFail();
  42. $tbrValue = (float) $franchiseeTbr->tbr_value;
  43. $contractMonth = (int) $data['contract_month_reference'];
  44. $revenueValue = (float) $data['revenue_value'];
  45. $royaltiesBracket = $this->findBracket(FranchiseeRoyaltiesBracket::class, $franchiseeUnit->franchisee_id, $contractMonth);
  46. $fnmBracket = $this->findBracket(FranchiseeFnmBracket::class, $franchiseeUnit->franchisee_id, $contractMonth);
  47. $maintenanceBracket = $this->findBracket(FranchiseeMaintenanceBracket::class, $franchiseeUnit->franchisee_id, $contractMonth);
  48. $royaltiesBracketValue = round((float) $royaltiesBracket->percentage * $tbrValue, 2);
  49. $fnmBracketValue = round((float) $fnmBracket->percentage * $tbrValue, 2);
  50. $maintenanceBracketValue = round((float) $maintenanceBracket->percentage * $tbrValue, 2);
  51. [$royaltiesEffectiveValue, $royaltiesEffectivePercentage, $royaltiesAppliedCriteria,
  52. $fnmEffectiveValue, $fnmEffectivePercentage] = $this->resolveEffectiveValues(
  53. $contractMonth,
  54. $revenueValue,
  55. $royaltiesBracket,
  56. $royaltiesBracketValue,
  57. $fnmBracket,
  58. $fnmBracketValue,
  59. );
  60. $maintenanceEffectiveValue = $maintenanceBracketValue;
  61. $maintenanceEffectivePercentage = (float) $maintenanceBracket->percentage;
  62. $bracketSubtotal = round($royaltiesBracketValue + $fnmBracketValue + $maintenanceBracketValue, 2);
  63. $subtotal = round($royaltiesEffectiveValue + $fnmEffectiveValue + $maintenanceEffectiveValue, 2);
  64. return TbrCalculation::create([
  65. 'unit_id' => $unit->id,
  66. 'revenue_value' => $revenueValue,
  67. 'contract_month_reference' => $contractMonth,
  68. 'tbr_value' => $tbrValue,
  69. 'royalties_bracket_id' => $royaltiesBracket->id,
  70. 'royalties_bracket_percentage' => $royaltiesBracket->percentage,
  71. 'royalties_bracket_value' => $royaltiesBracketValue,
  72. 'fnm_bracket_id' => $fnmBracket->id,
  73. 'fnm_bracket_percentage' => $fnmBracket->percentage,
  74. 'fnm_bracket_value' => $fnmBracketValue,
  75. 'maintenance_bracket_id' => $maintenanceBracket->id,
  76. 'maintenance_bracket_percentage' => $maintenanceBracket->percentage,
  77. 'maintenance_bracket_value' => $maintenanceBracketValue,
  78. 'royalties_effective_percentage' => $royaltiesEffectivePercentage,
  79. 'royalties_effective_value' => $royaltiesEffectiveValue,
  80. 'fnm_effective_percentage' => $fnmEffectivePercentage,
  81. 'fnm_effective_value' => $fnmEffectiveValue,
  82. 'maintenance_effective_percentage' => $maintenanceEffectivePercentage,
  83. 'maintenance_effective_value' => $maintenanceEffectiveValue,
  84. 'bracket_subtotal' => $bracketSubtotal,
  85. 'subtotal' => $subtotal,
  86. 'final_value' => $subtotal,
  87. 'user_id' => Auth::id(),
  88. 'royalties_applied_criteria' => $royaltiesAppliedCriteria,
  89. 'receivable_generated' => false,
  90. ]);
  91. });
  92. }
  93. private function resolveEffectiveValues(
  94. int $contractMonth,
  95. float $revenueValue,
  96. FranchiseeRoyaltiesBracket $royaltiesBracket,
  97. float $royaltiesBracketValue,
  98. FranchiseeFnmBracket $fnmBracket,
  99. float $fnmBracketValue,
  100. ): array {
  101. if ($contractMonth <= self::EXEMPT_THRESHOLD_MONTH) {
  102. return [0.0, 0.0, 'tbr_fixo', 0.0, 0.0];
  103. }
  104. $royaltiesFromRevenue = round(self::ROYALTIES_REVENUE_RATE * $revenueValue, 2);
  105. $fnmFromRevenue = round(self::FNM_REVENUE_RATE * $revenueValue, 2);
  106. if ($royaltiesBracketValue >= $royaltiesFromRevenue) {
  107. $royaltiesEffectiveValue = $royaltiesBracketValue;
  108. $royaltiesEffectivePercentage = (float) $royaltiesBracket->percentage;
  109. $royaltiesAppliedCriteria = 'tbr_fixo';
  110. } else {
  111. $royaltiesEffectiveValue = $royaltiesFromRevenue;
  112. $royaltiesEffectivePercentage = self::ROYALTIES_REVENUE_RATE;
  113. $royaltiesAppliedCriteria = 'percentual_faturamento';
  114. }
  115. if ($fnmBracketValue >= $fnmFromRevenue) {
  116. $fnmEffectiveValue = $fnmBracketValue;
  117. $fnmEffectivePercentage = (float) $fnmBracket->percentage;
  118. } else {
  119. $fnmEffectiveValue = $fnmFromRevenue;
  120. $fnmEffectivePercentage = self::FNM_REVENUE_RATE;
  121. }
  122. return [$royaltiesEffectiveValue, $royaltiesEffectivePercentage, $royaltiesAppliedCriteria, $fnmEffectiveValue, $fnmEffectivePercentage];
  123. }
  124. private function findBracket(string $modelClass, int $franchiseeId, int $contractMonth): mixed
  125. {
  126. return $modelClass::where('franchisee_id', $franchiseeId)
  127. ->where('start_month', '<=', $contractMonth)
  128. ->where(function ($q) use ($contractMonth) {
  129. $q->whereNull('end_month')->orWhere('end_month', '>=', $contractMonth);
  130. })
  131. ->firstOrFail();
  132. }
  133. }