|
@@ -6,6 +6,7 @@
|
|
|
use App\Models\FranchiseeAccountReceiveDetail;
|
|
use App\Models\FranchiseeAccountReceiveDetail;
|
|
|
use App\Models\FranchiseeContract;
|
|
use App\Models\FranchiseeContract;
|
|
|
use App\Models\InhabitantClassification;
|
|
use App\Models\InhabitantClassification;
|
|
|
|
|
+use App\Models\StudentContractInstallment;
|
|
|
use App\Models\Tbr;
|
|
use App\Models\Tbr;
|
|
|
use App\Models\TbrCalculation;
|
|
use App\Models\TbrCalculation;
|
|
|
use App\Models\UnitFinancial;
|
|
use App\Models\UnitFinancial;
|
|
@@ -23,6 +24,45 @@ class TbrCalculationService
|
|
|
private const MAINTENANCE_RATE = 0.30;
|
|
private const MAINTENANCE_RATE = 0.30;
|
|
|
private const EXEMPT_THRESHOLD_MONTH = 3;
|
|
private const EXEMPT_THRESHOLD_MONTH = 3;
|
|
|
|
|
|
|
|
|
|
+ // Faturamento (competência): por padrão usa o próprio mês de referência
|
|
|
|
|
+ // (Opção A — assume-se o mês já fechado ao gerar). Ligue para usar o mês
|
|
|
|
|
+ // anterior fechado (Opção B) quando a geração ocorrer dentro do mês corrente.
|
|
|
|
|
+ private const REVENUE_FROM_PREVIOUS_MONTH = false;
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Faturamento da unidade no mês (competência): soma das parcelas de aluno com
|
|
|
|
|
+ * vencimento no mês, de contratos não cancelados. Alimenta a regra "maior entre
|
|
|
|
|
+ * valor fixo e % do faturamento".
|
|
|
|
|
+ */
|
|
|
|
|
+ public function resolveRevenue(int $unitId, int $referenceYear, int $referenceMonth): float
|
|
|
|
|
+ {
|
|
|
|
|
+ $base = Carbon::createFromDate($referenceYear, $referenceMonth, 1);
|
|
|
|
|
+
|
|
|
|
|
+ if (self::REVENUE_FROM_PREVIOUS_MONTH) {
|
|
|
|
|
+ $base = $base->subMonthNoOverflow();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return (float) StudentContractInstallment::whereNull('deleted_at')
|
|
|
|
|
+ ->where('unit_id', $unitId)
|
|
|
|
|
+ ->where('status', '!=', 'cancelled')
|
|
|
|
|
+ ->whereBetween('due_date', [
|
|
|
|
|
+ $base->copy()->startOfMonth()->toDateString(),
|
|
|
|
|
+ $base->copy()->endOfMonth()->toDateString(),
|
|
|
|
|
+ ])
|
|
|
|
|
+ ->sum('value');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Faturamento do cálculo individual: usa o valor informado manualmente
|
|
|
|
|
+ * (override) quando > 0; senão calcula automaticamente pelas parcelas do mês.
|
|
|
|
|
+ */
|
|
|
|
|
+ private function pickRevenue(array $data, int $unitId, int $year, int $month): float
|
|
|
|
|
+ {
|
|
|
|
|
+ $manual = (float) ($data['revenue_value'] ?? 0);
|
|
|
|
|
+
|
|
|
|
|
+ return $manual > 0 ? $manual : $this->resolveRevenue($unitId, $year, $month);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
public function paginate(int $perPage = 15): LengthAwarePaginator
|
|
public function paginate(int $perPage = 15): LengthAwarePaginator
|
|
|
{
|
|
{
|
|
|
return TbrCalculation::with(['unit', 'user'])
|
|
return TbrCalculation::with(['unit', 'user'])
|
|
@@ -50,7 +90,10 @@ public function findById(int $id): ?TbrCalculation
|
|
|
public function preview(array $data): array
|
|
public function preview(array $data): array
|
|
|
{
|
|
{
|
|
|
$contract = $this->resolveContract($data['unit_id']);
|
|
$contract = $this->resolveContract($data['unit_id']);
|
|
|
- return $this->buildPreview($contract, (int) $data['reference_year'], (int) $data['reference_month'], (float) ($data['revenue_value'] ?? 0));
|
|
|
|
|
|
|
+ $year = (int) $data['reference_year'];
|
|
|
|
|
+ $month = (int) $data['reference_month'];
|
|
|
|
|
+
|
|
|
|
|
+ return $this->buildPreview($contract, $year, $month, $this->pickRevenue($data, $contract->unit_id, $year, $month));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public function previewBatch(int $referenceYear, int $referenceMonth): array
|
|
public function previewBatch(int $referenceYear, int $referenceMonth): array
|
|
@@ -59,7 +102,8 @@ public function previewBatch(int $referenceYear, int $referenceMonth): array
|
|
|
|
|
|
|
|
return $contracts->map(function (FranchiseeContract $contract) use ($referenceYear, $referenceMonth) {
|
|
return $contracts->map(function (FranchiseeContract $contract) use ($referenceYear, $referenceMonth) {
|
|
|
try {
|
|
try {
|
|
|
- return $this->buildPreview($contract, $referenceYear, $referenceMonth, 0);
|
|
|
|
|
|
|
+ $revenue = $this->resolveRevenue($contract->unit_id, $referenceYear, $referenceMonth);
|
|
|
|
|
+ return $this->buildPreview($contract, $referenceYear, $referenceMonth, $revenue);
|
|
|
} catch (ValidationException $e) {
|
|
} catch (ValidationException $e) {
|
|
|
return [
|
|
return [
|
|
|
'unit_id' => $contract->unit_id,
|
|
'unit_id' => $contract->unit_id,
|
|
@@ -74,7 +118,9 @@ public function calculate(array $data): TbrCalculation
|
|
|
{
|
|
{
|
|
|
return DB::transaction(function () use ($data) {
|
|
return DB::transaction(function () use ($data) {
|
|
|
$contract = $this->resolveContract($data['unit_id']);
|
|
$contract = $this->resolveContract($data['unit_id']);
|
|
|
- $payload = $this->buildPreview($contract, (int) $data['reference_year'], (int) $data['reference_month'], (float) ($data['revenue_value'] ?? 0));
|
|
|
|
|
|
|
+ $year = (int) $data['reference_year'];
|
|
|
|
|
+ $month = (int) $data['reference_month'];
|
|
|
|
|
+ $payload = $this->buildPreview($contract, $year, $month, $this->pickRevenue($data, $contract->unit_id, $year, $month));
|
|
|
|
|
|
|
|
return $this->persistCalculation($payload);
|
|
return $this->persistCalculation($payload);
|
|
|
});
|
|
});
|
|
@@ -127,7 +173,8 @@ public function generateBatch(int $referenceYear, int $referenceMonth, ?array $u
|
|
|
foreach ($contracts as $contract) {
|
|
foreach ($contracts as $contract) {
|
|
|
try {
|
|
try {
|
|
|
DB::transaction(function () use ($contract, $referenceYear, $referenceMonth, &$generated, &$skipped) {
|
|
DB::transaction(function () use ($contract, $referenceYear, $referenceMonth, &$generated, &$skipped) {
|
|
|
- $payload = $this->buildPreview($contract, $referenceYear, $referenceMonth, 0);
|
|
|
|
|
|
|
+ $revenue = $this->resolveRevenue($contract->unit_id, $referenceYear, $referenceMonth);
|
|
|
|
|
+ $payload = $this->buildPreview($contract, $referenceYear, $referenceMonth, $revenue);
|
|
|
|
|
|
|
|
if ($payload['receivable_already_generated']) {
|
|
if ($payload['receivable_already_generated']) {
|
|
|
$skipped[] = [
|
|
$skipped[] = [
|