|
@@ -2,21 +2,24 @@
|
|
|
|
|
|
|
|
namespace App\Services;
|
|
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\FranchiseeAccountReceive;
|
|
|
|
|
+use App\Models\FranchiseeAccountReceiveDetail;
|
|
|
|
|
+use App\Models\FranchiseeContract;
|
|
|
|
|
+use App\Models\InhabitantClassification;
|
|
|
|
|
+use App\Models\Tbr;
|
|
|
use App\Models\TbrCalculation;
|
|
use App\Models\TbrCalculation;
|
|
|
-use App\Models\Unit;
|
|
|
|
|
-use Illuminate\Support\Facades\DB;
|
|
|
|
|
|
|
+use Carbon\Carbon;
|
|
|
use Illuminate\Pagination\LengthAwarePaginator;
|
|
use Illuminate\Pagination\LengthAwarePaginator;
|
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Facades\Auth;
|
|
|
|
|
+use Illuminate\Support\Facades\DB;
|
|
|
|
|
+use Illuminate\Validation\ValidationException;
|
|
|
|
|
|
|
|
class TbrCalculationService
|
|
class TbrCalculationService
|
|
|
{
|
|
{
|
|
|
private const ROYALTIES_REVENUE_RATE = 0.08;
|
|
private const ROYALTIES_REVENUE_RATE = 0.08;
|
|
|
private const FNM_REVENUE_RATE = 0.02;
|
|
private const FNM_REVENUE_RATE = 0.02;
|
|
|
|
|
+ private const FNM_BRACKET_PERCENTAGE = 0.20;
|
|
|
|
|
+ private const MAINTENANCE_RATE = 0.30;
|
|
|
private const EXEMPT_THRESHOLD_MONTH = 3;
|
|
private const EXEMPT_THRESHOLD_MONTH = 3;
|
|
|
|
|
|
|
|
public function paginate(int $perPage = 15): LengthAwarePaginator
|
|
public function paginate(int $perPage = 15): LengthAwarePaginator
|
|
@@ -26,92 +29,343 @@ public function paginate(int $perPage = 15): LengthAwarePaginator
|
|
|
->paginate($perPage);
|
|
->paginate($perPage);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ public function listAll(int $limit = 100): \Illuminate\Database\Eloquent\Collection
|
|
|
|
|
+ {
|
|
|
|
|
+ return TbrCalculation::with(['unit', 'user'])
|
|
|
|
|
+ ->orderBy('created_at', 'desc')
|
|
|
|
|
+ ->limit($limit)
|
|
|
|
|
+ ->get();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
public function findById(int $id): ?TbrCalculation
|
|
public function findById(int $id): ?TbrCalculation
|
|
|
{
|
|
{
|
|
|
return TbrCalculation::with([
|
|
return TbrCalculation::with([
|
|
|
'unit',
|
|
'unit',
|
|
|
'user',
|
|
'user',
|
|
|
'royaltiesBracket',
|
|
'royaltiesBracket',
|
|
|
- 'fnmBracket',
|
|
|
|
|
- 'maintenanceBracket',
|
|
|
|
|
])->find($id);
|
|
])->find($id);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ public function preview(array $data): array
|
|
|
|
|
+ {
|
|
|
|
|
+ $contract = $this->resolveContract($data['unit_id']);
|
|
|
|
|
+ return $this->buildPreview($contract, (int) $data['reference_year'], (int) $data['reference_month'], (float) ($data['revenue_value'] ?? 0));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public function previewBatch(int $referenceYear, int $referenceMonth): array
|
|
|
|
|
+ {
|
|
|
|
|
+ $contracts = $this->loadActiveContracts($referenceYear, $referenceMonth);
|
|
|
|
|
+
|
|
|
|
|
+ return $contracts->map(function (FranchiseeContract $contract) use ($referenceYear, $referenceMonth) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ return $this->buildPreview($contract, $referenceYear, $referenceMonth, 0);
|
|
|
|
|
+ } catch (ValidationException $e) {
|
|
|
|
|
+ return [
|
|
|
|
|
+ 'unit_id' => $contract->unit_id,
|
|
|
|
|
+ 'unit_name' => $contract->unit?->fantasy_name,
|
|
|
|
|
+ 'error' => collect($e->errors())->flatten()->first(),
|
|
|
|
|
+ ];
|
|
|
|
|
+ }
|
|
|
|
|
+ })->values()->toArray();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
public function calculate(array $data): TbrCalculation
|
|
public function calculate(array $data): TbrCalculation
|
|
|
{
|
|
{
|
|
|
return DB::transaction(function () use ($data) {
|
|
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,
|
|
|
|
|
- ]);
|
|
|
|
|
|
|
+ $contract = $this->resolveContract($data['unit_id']);
|
|
|
|
|
+ $payload = $this->buildPreview($contract, (int) $data['reference_year'], (int) $data['reference_month'], (float) ($data['revenue_value'] ?? 0));
|
|
|
|
|
+
|
|
|
|
|
+ return $this->persistCalculation($payload);
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public function generateReceivable(int $calculationId): FranchiseeAccountReceive
|
|
|
|
|
+ {
|
|
|
|
|
+ return DB::transaction(function () use ($calculationId) {
|
|
|
|
|
+ $calculation = TbrCalculation::lockForUpdate()->findOrFail($calculationId);
|
|
|
|
|
+
|
|
|
|
|
+ if ($calculation->receivable_generated) {
|
|
|
|
|
+ throw ValidationException::withMessages([
|
|
|
|
|
+ 'tbr_calculation_id' => 'Já existe um título gerado para este cálculo.',
|
|
|
|
|
+ ]);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $duplicate = TbrCalculation::where('unit_id', $calculation->unit_id)
|
|
|
|
|
+ ->where('contract_month_reference', $calculation->contract_month_reference)
|
|
|
|
|
+ ->where('receivable_generated', true)
|
|
|
|
|
+ ->where('id', '!=', $calculation->id)
|
|
|
|
|
+ ->exists();
|
|
|
|
|
+
|
|
|
|
|
+ if ($duplicate) {
|
|
|
|
|
+ throw ValidationException::withMessages([
|
|
|
|
|
+ 'tbr_calculation_id' => 'Já existe um título gerado para esta unidade no mês de contrato '
|
|
|
|
|
+ . $calculation->contract_month_reference . '.',
|
|
|
|
|
+ ]);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $contract = FranchiseeContract::where('unit_id', $calculation->unit_id)
|
|
|
|
|
+ ->orderByDesc('start_date')
|
|
|
|
|
+ ->first();
|
|
|
|
|
+
|
|
|
|
|
+ return $this->buildReceivable($calculation, $contract);
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ public function generateBatch(int $referenceYear, int $referenceMonth, ?array $unitIds = null): array
|
|
|
|
|
+ {
|
|
|
|
|
+ $contracts = $this->loadActiveContracts($referenceYear, $referenceMonth);
|
|
|
|
|
+
|
|
|
|
|
+ if ($unitIds !== null) {
|
|
|
|
|
+ $contracts = $contracts->filter(fn ($c) => in_array($c->unit_id, $unitIds, true))->values();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $generated = [];
|
|
|
|
|
+ $skipped = [];
|
|
|
|
|
+ $errors = [];
|
|
|
|
|
+
|
|
|
|
|
+ foreach ($contracts as $contract) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ DB::transaction(function () use ($contract, $referenceYear, $referenceMonth, &$generated, &$skipped) {
|
|
|
|
|
+ $payload = $this->buildPreview($contract, $referenceYear, $referenceMonth, 0);
|
|
|
|
|
+
|
|
|
|
|
+ if ($payload['receivable_already_generated']) {
|
|
|
|
|
+ $skipped[] = [
|
|
|
|
|
+ 'unit_id' => $contract->unit_id,
|
|
|
|
|
+ 'unit_name' => $payload['unit_name'],
|
|
|
|
|
+ 'reason' => 'Já gerado para este mês de contrato.',
|
|
|
|
|
+ ];
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $calculation = $this->persistCalculation($payload);
|
|
|
|
|
+ $receive = $this->buildReceivable($calculation, $contract);
|
|
|
|
|
+
|
|
|
|
|
+ $generated[] = [
|
|
|
|
|
+ 'unit_id' => $contract->unit_id,
|
|
|
|
|
+ 'unit_name' => $payload['unit_name'],
|
|
|
|
|
+ 'tbr_calculation_id' => $calculation->id,
|
|
|
|
|
+ 'receivable_id' => $receive->id,
|
|
|
|
|
+ 'total' => $payload['final_value'],
|
|
|
|
|
+ ];
|
|
|
|
|
+ });
|
|
|
|
|
+ } catch (ValidationException $e) {
|
|
|
|
|
+ $errors[] = [
|
|
|
|
|
+ 'unit_id' => $contract->unit_id,
|
|
|
|
|
+ 'unit_name' => $contract->unit?->fantasy_name,
|
|
|
|
|
+ 'reason' => collect($e->errors())->flatten()->first(),
|
|
|
|
|
+ ];
|
|
|
|
|
+ } catch (\Throwable $e) {
|
|
|
|
|
+ $errors[] = [
|
|
|
|
|
+ 'unit_id' => $contract->unit_id,
|
|
|
|
|
+ 'unit_name' => $contract->unit?->fantasy_name,
|
|
|
|
|
+ 'reason' => $e->getMessage(),
|
|
|
|
|
+ ];
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return [
|
|
|
|
|
+ 'generated_count' => count($generated),
|
|
|
|
|
+ 'skipped_count' => count($skipped),
|
|
|
|
|
+ 'error_count' => count($errors),
|
|
|
|
|
+ 'generated' => $generated,
|
|
|
|
|
+ 'skipped' => $skipped,
|
|
|
|
|
+ 'errors' => $errors,
|
|
|
|
|
+ ];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private function loadActiveContracts(int $referenceYear, int $referenceMonth): \Illuminate\Support\Collection
|
|
|
|
|
+ {
|
|
|
|
|
+ $referenceLastDay = Carbon::createFromDate($referenceYear, $referenceMonth, 1)->endOfMonth()->toDateString();
|
|
|
|
|
+ $referenceFirstDay = Carbon::createFromDate($referenceYear, $referenceMonth, 1)->startOfMonth()->toDateString();
|
|
|
|
|
+
|
|
|
|
|
+ return FranchiseeContract::with(['unit', 'municipalitySize'])
|
|
|
|
|
+ ->whereNotNull('start_date')
|
|
|
|
|
+ ->whereNotNull('municipality_size_id')
|
|
|
|
|
+ ->where('start_date', '<=', $referenceLastDay)
|
|
|
|
|
+ ->where(function ($q) use ($referenceFirstDay) {
|
|
|
|
|
+ $q->whereNull('end_date')->orWhere('end_date', '>=', $referenceFirstDay);
|
|
|
|
|
+ })
|
|
|
|
|
+ ->orderByDesc('start_date')
|
|
|
|
|
+ ->orderByDesc('id')
|
|
|
|
|
+ ->get()
|
|
|
|
|
+ ->unique('unit_id')
|
|
|
|
|
+ ->values();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private function resolveContract(int $unitId): FranchiseeContract
|
|
|
|
|
+ {
|
|
|
|
|
+ $contract = FranchiseeContract::with(['unit', 'municipalitySize'])
|
|
|
|
|
+ ->where('unit_id', $unitId)
|
|
|
|
|
+ ->whereNotNull('start_date')
|
|
|
|
|
+ ->orderByDesc('start_date')
|
|
|
|
|
+ ->orderByDesc('id')
|
|
|
|
|
+ ->first();
|
|
|
|
|
+
|
|
|
|
|
+ if (!$contract) {
|
|
|
|
|
+ throw ValidationException::withMessages([
|
|
|
|
|
+ 'unit_id' => 'Unidade não possui contrato cadastrado.',
|
|
|
|
|
+ ]);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!$contract->municipality_size_id) {
|
|
|
|
|
+ throw ValidationException::withMessages([
|
|
|
|
|
+ 'unit_id' => 'O contrato da unidade não tem a faixa de habitantes definida. Edite o contrato para informá-la.',
|
|
|
|
|
+ ]);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return $contract;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private function buildPreview(FranchiseeContract $contract, int $referenceYear, int $referenceMonth, float $revenueValue): array
|
|
|
|
|
+ {
|
|
|
|
|
+ $tbrValue = (float) ($contract->tbr_fixed_value ?? 0);
|
|
|
|
|
+
|
|
|
|
|
+ if ($tbrValue <= 0) {
|
|
|
|
|
+ $tbrValue = (float) (Tbr::where('year', $referenceYear)->orderByDesc('id')->value('tbr_value') ?? 0);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if ($tbrValue <= 0) {
|
|
|
|
|
+ throw ValidationException::withMessages([
|
|
|
|
|
+ 'unit_id' => 'TBR não definida para o contrato nem para o ano de referência.',
|
|
|
|
|
+ ]);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $contractMonth = $this->resolveContractMonth($contract->start_date, $referenceYear, $referenceMonth);
|
|
|
|
|
+ $municipalitySizeId = (int) $contract->municipality_size_id;
|
|
|
|
|
+
|
|
|
|
|
+ $royaltiesBracket = $this->findRoyaltiesBracket($municipalitySizeId, $contractMonth);
|
|
|
|
|
+ $fnmPercentage = $this->resolveFnmPercentage($contractMonth);
|
|
|
|
|
+ $maintenancePercentage = self::MAINTENANCE_RATE;
|
|
|
|
|
+
|
|
|
|
|
+ $royaltiesBracketValue = round((float) $royaltiesBracket->tbr_percentage * $tbrValue, 2);
|
|
|
|
|
+ $fnmBracketValue = round($fnmPercentage * $tbrValue, 2);
|
|
|
|
|
+ $maintenanceBracketValue = round($maintenancePercentage * $tbrValue, 2);
|
|
|
|
|
+
|
|
|
|
|
+ [$royaltiesEffectiveValue, $royaltiesEffectivePercentage, $royaltiesAppliedCriteria,
|
|
|
|
|
+ $fnmEffectiveValue, $fnmEffectivePercentage] = $this->resolveEffectiveValues(
|
|
|
|
|
+ $contractMonth,
|
|
|
|
|
+ $revenueValue,
|
|
|
|
|
+ (float) $royaltiesBracket->tbr_percentage,
|
|
|
|
|
+ $royaltiesBracketValue,
|
|
|
|
|
+ $fnmPercentage,
|
|
|
|
|
+ $fnmBracketValue,
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ $maintenanceEffectiveValue = $maintenanceBracketValue;
|
|
|
|
|
+ $maintenanceEffectivePercentage = $maintenancePercentage;
|
|
|
|
|
+
|
|
|
|
|
+ $bracketSubtotal = round($royaltiesBracketValue + $fnmBracketValue + $maintenanceBracketValue, 2);
|
|
|
|
|
+ $subtotal = round($royaltiesEffectiveValue + $fnmEffectiveValue + $maintenanceEffectiveValue, 2);
|
|
|
|
|
+
|
|
|
|
|
+ return [
|
|
|
|
|
+ 'unit_id' => $contract->unit_id,
|
|
|
|
|
+ 'unit_name' => $contract->unit?->fantasy_name,
|
|
|
|
|
+ 'contract_id' => $contract->id,
|
|
|
|
|
+ 'reference_year' => $referenceYear,
|
|
|
|
|
+ 'reference_month' => $referenceMonth,
|
|
|
|
|
+ 'contract_month_reference' => $contractMonth,
|
|
|
|
|
+ 'revenue_value' => $revenueValue,
|
|
|
|
|
+ 'tbr_value' => $tbrValue,
|
|
|
|
|
+ 'municipality_size_id' => $municipalitySizeId,
|
|
|
|
|
+ 'municipality_size_name' => $contract->municipalitySize?->description,
|
|
|
|
|
+ 'royalties_bracket_id' => $royaltiesBracket->id,
|
|
|
|
|
+ 'royalties_bracket_percentage' => (float) $royaltiesBracket->tbr_percentage,
|
|
|
|
|
+ 'royalties_bracket_value' => $royaltiesBracketValue,
|
|
|
|
|
+ 'fnm_bracket_percentage' => $fnmPercentage,
|
|
|
|
|
+ 'fnm_bracket_value' => $fnmBracketValue,
|
|
|
|
|
+ 'maintenance_bracket_percentage' => $maintenancePercentage,
|
|
|
|
|
+ '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,
|
|
|
|
|
+ 'royalties_applied_criteria' => $royaltiesAppliedCriteria,
|
|
|
|
|
+ 'receivable_already_generated' => $this->existingReceivable($contract->unit_id, $contractMonth),
|
|
|
|
|
+ ];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private function persistCalculation(array $payload): TbrCalculation
|
|
|
|
|
+ {
|
|
|
|
|
+ return TbrCalculation::create([
|
|
|
|
|
+ 'unit_id' => $payload['unit_id'],
|
|
|
|
|
+ 'revenue_value' => $payload['revenue_value'],
|
|
|
|
|
+ 'contract_month_reference' => $payload['contract_month_reference'],
|
|
|
|
|
+ 'tbr_value' => $payload['tbr_value'],
|
|
|
|
|
+ 'royalties_bracket_id' => $payload['royalties_bracket_id'],
|
|
|
|
|
+ 'royalties_bracket_percentage' => $payload['royalties_bracket_percentage'],
|
|
|
|
|
+ 'royalties_bracket_value' => $payload['royalties_bracket_value'],
|
|
|
|
|
+ 'fnm_bracket_percentage' => $payload['fnm_bracket_percentage'],
|
|
|
|
|
+ 'fnm_bracket_value' => $payload['fnm_bracket_value'],
|
|
|
|
|
+ 'maintenance_bracket_percentage' => $payload['maintenance_bracket_percentage'],
|
|
|
|
|
+ 'maintenance_bracket_value' => $payload['maintenance_bracket_value'],
|
|
|
|
|
+ 'royalties_effective_percentage' => $payload['royalties_effective_percentage'],
|
|
|
|
|
+ 'royalties_effective_value' => $payload['royalties_effective_value'],
|
|
|
|
|
+ 'fnm_effective_percentage' => $payload['fnm_effective_percentage'],
|
|
|
|
|
+ 'fnm_effective_value' => $payload['fnm_effective_value'],
|
|
|
|
|
+ 'maintenance_effective_percentage' => $payload['maintenance_effective_percentage'],
|
|
|
|
|
+ 'maintenance_effective_value' => $payload['maintenance_effective_value'],
|
|
|
|
|
+ 'bracket_subtotal' => $payload['bracket_subtotal'],
|
|
|
|
|
+ 'subtotal' => $payload['subtotal'],
|
|
|
|
|
+ 'final_value' => $payload['final_value'],
|
|
|
|
|
+ 'user_id' => Auth::id(),
|
|
|
|
|
+ 'royalties_applied_criteria' => $payload['royalties_applied_criteria'],
|
|
|
|
|
+ 'receivable_generated' => false,
|
|
|
|
|
+ ]);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private function buildReceivable(TbrCalculation $calculation, ?FranchiseeContract $contract): FranchiseeAccountReceive
|
|
|
|
|
+ {
|
|
|
|
|
+ $referenceDate = Carbon::parse($calculation->created_at);
|
|
|
|
|
+ $referenceLabel = $referenceDate->format('m/Y');
|
|
|
|
|
+ $dueDate = $this->resolveDueDate($contract, $referenceDate);
|
|
|
|
|
+
|
|
|
|
|
+ $receive = FranchiseeAccountReceive::create([
|
|
|
|
|
+ 'unit_id' => $calculation->unit_id,
|
|
|
|
|
+ 'tbr_calculation_id' => $calculation->id,
|
|
|
|
|
+ 'order' => $calculation->contract_month_reference,
|
|
|
|
|
+ 'history' => 'Royalties / FNM / Manutenção — ' . $referenceLabel,
|
|
|
|
|
+ 'value' => $calculation->final_value,
|
|
|
|
|
+ 'paid_value' => 0,
|
|
|
|
|
+ 'due_date' => $dueDate,
|
|
|
|
|
+ 'discount' => 0,
|
|
|
|
|
+ 'fees' => 0,
|
|
|
|
|
+ 'obs' => null,
|
|
|
|
|
+ 'asaas_id' => null,
|
|
|
|
|
+ 'status' => 'pending',
|
|
|
|
|
+ ]);
|
|
|
|
|
+
|
|
|
|
|
+ FranchiseeAccountReceiveDetail::create([
|
|
|
|
|
+ 'franchisee_account_receive_id' => $receive->id,
|
|
|
|
|
+ 'value' => $calculation->royalties_effective_value,
|
|
|
|
|
+ 'history' => 'Royalties ' . $referenceLabel,
|
|
|
|
|
+ ]);
|
|
|
|
|
+ FranchiseeAccountReceiveDetail::create([
|
|
|
|
|
+ 'franchisee_account_receive_id' => $receive->id,
|
|
|
|
|
+ 'value' => $calculation->fnm_effective_value,
|
|
|
|
|
+ 'history' => 'FNM ' . $referenceLabel,
|
|
|
|
|
+ ]);
|
|
|
|
|
+ FranchiseeAccountReceiveDetail::create([
|
|
|
|
|
+ 'franchisee_account_receive_id' => $receive->id,
|
|
|
|
|
+ 'value' => $calculation->maintenance_effective_value,
|
|
|
|
|
+ 'history' => 'Taxa Manutenção ' . $referenceLabel,
|
|
|
|
|
+ ]);
|
|
|
|
|
+
|
|
|
|
|
+ $calculation->update(['receivable_generated' => true]);
|
|
|
|
|
+
|
|
|
|
|
+ return $receive->load('details');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private function resolveEffectiveValues(
|
|
private function resolveEffectiveValues(
|
|
|
int $contractMonth,
|
|
int $contractMonth,
|
|
|
float $revenueValue,
|
|
float $revenueValue,
|
|
|
- FranchiseeRoyaltiesBracket $royaltiesBracket,
|
|
|
|
|
|
|
+ float $royaltiesBracketPercentage,
|
|
|
float $royaltiesBracketValue,
|
|
float $royaltiesBracketValue,
|
|
|
- FranchiseeFnmBracket $fnmBracket,
|
|
|
|
|
|
|
+ float $fnmBracketPercentage,
|
|
|
float $fnmBracketValue,
|
|
float $fnmBracketValue,
|
|
|
): array {
|
|
): array {
|
|
|
if ($contractMonth <= self::EXEMPT_THRESHOLD_MONTH) {
|
|
if ($contractMonth <= self::EXEMPT_THRESHOLD_MONTH) {
|
|
@@ -123,7 +377,7 @@ private function resolveEffectiveValues(
|
|
|
|
|
|
|
|
if ($royaltiesBracketValue >= $royaltiesFromRevenue) {
|
|
if ($royaltiesBracketValue >= $royaltiesFromRevenue) {
|
|
|
$royaltiesEffectiveValue = $royaltiesBracketValue;
|
|
$royaltiesEffectiveValue = $royaltiesBracketValue;
|
|
|
- $royaltiesEffectivePercentage = (float) $royaltiesBracket->percentage;
|
|
|
|
|
|
|
+ $royaltiesEffectivePercentage = $royaltiesBracketPercentage;
|
|
|
$royaltiesAppliedCriteria = 'tbr_fixo';
|
|
$royaltiesAppliedCriteria = 'tbr_fixo';
|
|
|
} else {
|
|
} else {
|
|
|
$royaltiesEffectiveValue = $royaltiesFromRevenue;
|
|
$royaltiesEffectiveValue = $royaltiesFromRevenue;
|
|
@@ -133,7 +387,7 @@ private function resolveEffectiveValues(
|
|
|
|
|
|
|
|
if ($fnmBracketValue >= $fnmFromRevenue) {
|
|
if ($fnmBracketValue >= $fnmFromRevenue) {
|
|
|
$fnmEffectiveValue = $fnmBracketValue;
|
|
$fnmEffectiveValue = $fnmBracketValue;
|
|
|
- $fnmEffectivePercentage = (float) $fnmBracket->percentage;
|
|
|
|
|
|
|
+ $fnmEffectivePercentage = $fnmBracketPercentage;
|
|
|
} else {
|
|
} else {
|
|
|
$fnmEffectiveValue = $fnmFromRevenue;
|
|
$fnmEffectiveValue = $fnmFromRevenue;
|
|
|
$fnmEffectivePercentage = self::FNM_REVENUE_RATE;
|
|
$fnmEffectivePercentage = self::FNM_REVENUE_RATE;
|
|
@@ -142,13 +396,57 @@ private function resolveEffectiveValues(
|
|
|
return [$royaltiesEffectiveValue, $royaltiesEffectivePercentage, $royaltiesAppliedCriteria, $fnmEffectiveValue, $fnmEffectivePercentage];
|
|
return [$royaltiesEffectiveValue, $royaltiesEffectivePercentage, $royaltiesAppliedCriteria, $fnmEffectiveValue, $fnmEffectivePercentage];
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private function findBracket(string $modelClass, int $franchiseeId, int $contractMonth): mixed
|
|
|
|
|
|
|
+ private function resolveContractMonth(?Carbon $startDate, int $year, int $month): int
|
|
|
|
|
+ {
|
|
|
|
|
+ if (!$startDate) {
|
|
|
|
|
+ return 1;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $start = $startDate->copy()->startOfMonth();
|
|
|
|
|
+ $reference = Carbon::createFromDate($year, $month, 1)->startOfMonth();
|
|
|
|
|
+ $diff = (int) $start->diffInMonths($reference);
|
|
|
|
|
+
|
|
|
|
|
+ return max(1, $diff + 1);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private function findRoyaltiesBracket(int $municipalitySizeId, int $contractMonth): InhabitantClassification
|
|
|
{
|
|
{
|
|
|
- return $modelClass::where('franchisee_id', $franchiseeId)
|
|
|
|
|
- ->where('start_month', '<=', $contractMonth)
|
|
|
|
|
|
|
+ $bracket = InhabitantClassification::where('municipality_size_id', $municipalitySizeId)
|
|
|
|
|
+ ->where('is_renewal', false)
|
|
|
|
|
+ ->where('start', '<=', $contractMonth)
|
|
|
->where(function ($q) use ($contractMonth) {
|
|
->where(function ($q) use ($contractMonth) {
|
|
|
- $q->whereNull('end_month')->orWhere('end_month', '>=', $contractMonth);
|
|
|
|
|
|
|
+ $q->whereNull('end')->orWhere('end', '>=', $contractMonth);
|
|
|
})
|
|
})
|
|
|
- ->firstOrFail();
|
|
|
|
|
|
|
+ ->orderBy('start')
|
|
|
|
|
+ ->first();
|
|
|
|
|
+
|
|
|
|
|
+ if (!$bracket) {
|
|
|
|
|
+ throw ValidationException::withMessages([
|
|
|
|
|
+ 'municipality_size_id' => 'Não foi encontrada faixa de royalties para o porte e mês de contrato informados.',
|
|
|
|
|
+ ]);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return $bracket;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private function resolveFnmPercentage(int $contractMonth): float
|
|
|
|
|
+ {
|
|
|
|
|
+ return $contractMonth <= self::EXEMPT_THRESHOLD_MONTH ? 0.0 : self::FNM_BRACKET_PERCENTAGE;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private function resolveDueDate(?FranchiseeContract $contract, Carbon $referenceDate): Carbon
|
|
|
|
|
+ {
|
|
|
|
|
+ $dueDay = (int) ($contract?->invoice_due_date ?? 10);
|
|
|
|
|
+ $dueDay = max(1, min(28, $dueDay));
|
|
|
|
|
+
|
|
|
|
|
+ return $referenceDate->copy()->addMonthNoOverflow()->day($dueDay);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private function existingReceivable(int $unitId, int $contractMonth): bool
|
|
|
|
|
+ {
|
|
|
|
|
+ return TbrCalculation::where('unit_id', $unitId)
|
|
|
|
|
+ ->where('contract_month_reference', $contractMonth)
|
|
|
|
|
+ ->where('receivable_generated', true)
|
|
|
|
|
+ ->exists();
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|