|
@@ -7,30 +7,30 @@
|
|
|
use App\Models\StudentMedia;
|
|
use App\Models\StudentMedia;
|
|
|
use Carbon\Carbon;
|
|
use Carbon\Carbon;
|
|
|
use Illuminate\Database\Eloquent\Collection;
|
|
use Illuminate\Database\Eloquent\Collection;
|
|
|
|
|
+use Illuminate\Http\UploadedFile;
|
|
|
|
|
+use Illuminate\Support\Arr;
|
|
|
|
|
+use Illuminate\Support\Facades\DB;
|
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Illuminate\Support\Facades\Storage;
|
|
|
|
|
+use Illuminate\Validation\ValidationException;
|
|
|
|
|
|
|
|
class StudentContractService
|
|
class StudentContractService
|
|
|
{
|
|
{
|
|
|
- public function getFranchisorSummary(array $unitIds = []): array
|
|
|
|
|
- {
|
|
|
|
|
- $base = StudentContract::query()
|
|
|
|
|
- ->when(!empty($unitIds), fn ($q) => $q->whereIn('unit_id', $unitIds));
|
|
|
|
|
-
|
|
|
|
|
- return [
|
|
|
|
|
- 'active' => (clone $base)->where('status', 'active')->count(),
|
|
|
|
|
- 'frozen' => (clone $base)->where('status', 'frozen')->count(),
|
|
|
|
|
- 'cancelled' => (clone $base)->where('status', 'cancelled')->count(),
|
|
|
|
|
- ];
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- public function getFranchisorByStatus(string $status, array $unitIds = []): Collection
|
|
|
|
|
- {
|
|
|
|
|
- return StudentContract::with(['student', 'unit'])
|
|
|
|
|
- ->where('status', $status)
|
|
|
|
|
- ->when(!empty($unitIds), fn ($q) => $q->whereIn('unit_id', $unitIds))
|
|
|
|
|
- ->orderBy('created_at', 'desc')
|
|
|
|
|
- ->get();
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ private const FILE_ATTRIBUTES = [
|
|
|
|
|
+ 'file_url',
|
|
|
|
|
+ 'file_type',
|
|
|
|
|
+ 'signed_file_url',
|
|
|
|
|
+ 'signed_file_type',
|
|
|
|
|
+ ];
|
|
|
|
|
+
|
|
|
|
|
+ private const VERSION_ATTRIBUTES = [
|
|
|
|
|
+ 'id',
|
|
|
|
|
+ 'version',
|
|
|
|
|
+ 'derived_from_contract_id',
|
|
|
|
|
+ 'created_by_user_id',
|
|
|
|
|
+ 'created_at',
|
|
|
|
|
+ 'updated_at',
|
|
|
|
|
+ 'deleted_at',
|
|
|
|
|
+ ];
|
|
|
|
|
|
|
|
public function getAll(int $unitId, ?int $studentId = null): Collection
|
|
public function getAll(int $unitId, ?int $studentId = null): Collection
|
|
|
{
|
|
{
|
|
@@ -39,7 +39,9 @@ public function getAll(int $unitId, ?int $studentId = null): Collection
|
|
|
->withTrashed()
|
|
->withTrashed()
|
|
|
->with(['city', 'state']),
|
|
->with(['city', 'state']),
|
|
|
'classPackageUnit',
|
|
'classPackageUnit',
|
|
|
|
|
+ 'createdBy',
|
|
|
])
|
|
])
|
|
|
|
|
+ ->latestVersion()
|
|
|
->where('unit_id', $unitId)
|
|
->where('unit_id', $unitId)
|
|
|
->when($studentId, fn ($q) => $q->where('student_id', $studentId))
|
|
->when($studentId, fn ($q) => $q->where('student_id', $studentId))
|
|
|
->orderBy('created_at', 'desc')
|
|
->orderBy('created_at', 'desc')
|
|
@@ -48,14 +50,23 @@ public function getAll(int $unitId, ?int $studentId = null): Collection
|
|
|
|
|
|
|
|
public function findById(int $id): ?StudentContract
|
|
public function findById(int $id): ?StudentContract
|
|
|
{
|
|
{
|
|
|
- return StudentContract::with(['student', 'classPackageUnit'])->find($id);
|
|
|
|
|
|
|
+ $contract = StudentContract::with(['student', 'classPackageUnit', 'createdBy'])->find($id);
|
|
|
|
|
+
|
|
|
|
|
+ if (! $contract) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $this->loadVersionHistory($contract);
|
|
|
|
|
+
|
|
|
|
|
+ return $contract;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public function create(array $data): StudentContract
|
|
public function create(array $data): StudentContract
|
|
|
{
|
|
{
|
|
|
- if (!empty($data['due_day'])) {
|
|
|
|
|
|
|
+ if (! empty($data['due_day'])) {
|
|
|
$data['recurring_day'] = (int) $data['due_day'];
|
|
$data['recurring_day'] = (int) $data['due_day'];
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
unset($data['due_day']);
|
|
unset($data['due_day']);
|
|
|
|
|
|
|
|
$contract = StudentContract::create($data);
|
|
$contract = StudentContract::create($data);
|
|
@@ -65,13 +76,224 @@ public function create(array $data): StudentContract
|
|
|
return $contract;
|
|
return $contract;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ public function update(int $id, array $data, ?int $createdByUserId = null): ?StudentContract
|
|
|
|
|
+ {
|
|
|
|
|
+ if (! empty($data['due_day'])) {
|
|
|
|
|
+ $data['recurring_day'] = (int) $data['due_day'];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ unset($data['due_day']);
|
|
|
|
|
+
|
|
|
|
|
+ return DB::transaction(function () use ($id, $data, $createdByUserId): ?StudentContract {
|
|
|
|
|
+ $contract = StudentContract::query()->lockForUpdate()->find($id);
|
|
|
|
|
+
|
|
|
|
|
+ if (! $contract) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $originalAttributes = $contract->getAttributes();
|
|
|
|
|
+
|
|
|
|
|
+ $contract->fill($data);
|
|
|
|
|
+
|
|
|
|
|
+ $dirty = Arr::except(
|
|
|
|
|
+ $contract->getDirty(),
|
|
|
|
|
+
|
|
|
|
|
+ array_merge(self::FILE_ATTRIBUTES, self::VERSION_ATTRIBUTES),
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ if ($dirty === []) {
|
|
|
|
|
+ return $contract->refresh();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if ($contract->derivedVersions()->withTrashed()->lockForUpdate()->first()) {
|
|
|
|
|
+ throw ValidationException::withMessages([
|
|
|
|
|
+ 'contract' => __('validation.student_contract_old_version'),
|
|
|
|
|
+ ]);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $submittedAttributes = array_keys($data);
|
|
|
|
|
+
|
|
|
|
|
+ $copiedAttributes = Arr::except(
|
|
|
|
|
+ $originalAttributes,
|
|
|
|
|
+ array_merge(self::FILE_ATTRIBUTES, self::VERSION_ATTRIBUTES, $submittedAttributes),
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ $editedAttributes = Arr::only($contract->getAttributes(), $submittedAttributes);
|
|
|
|
|
+
|
|
|
|
|
+ $newVersion = StudentContract::create(array_merge(
|
|
|
|
|
+ $copiedAttributes,
|
|
|
|
|
+ $editedAttributes,
|
|
|
|
|
+ [
|
|
|
|
|
+ 'version' => $contract->version + 1,
|
|
|
|
|
+ 'derived_from_contract_id' => $contract->id,
|
|
|
|
|
+ 'created_by_user_id' => $createdByUserId,
|
|
|
|
|
+ ],
|
|
|
|
|
+ ));
|
|
|
|
|
+
|
|
|
|
|
+ StudentContractInstallment::where('student_contract_id', $contract->id)
|
|
|
|
|
+ ->update(['student_contract_id' => $newVersion->id]);
|
|
|
|
|
+
|
|
|
|
|
+ return $newVersion->fresh();
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public function delete(int $id): bool
|
|
|
|
|
+ {
|
|
|
|
|
+ $model = $this->findById($id);
|
|
|
|
|
+
|
|
|
|
|
+ if (! $model) {
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if ($model->file_url) {
|
|
|
|
|
+ Storage::delete($model->file_url);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if ($model->signed_file_url) {
|
|
|
|
|
+ Storage::delete($model->signed_file_url);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return $model->delete();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ //
|
|
|
|
|
+
|
|
|
|
|
+ public function getFranchisorSummary(array $unitIds = []): array
|
|
|
|
|
+ {
|
|
|
|
|
+ $base = StudentContract::query()->latestVersion()
|
|
|
|
|
+ ->when(! empty($unitIds), fn ($q) => $q->whereIn('unit_id', $unitIds));
|
|
|
|
|
+
|
|
|
|
|
+ return [
|
|
|
|
|
+ 'active' => (clone $base)->where('status', 'active')->count(),
|
|
|
|
|
+ 'frozen' => (clone $base)->where('status', 'frozen')->count(),
|
|
|
|
|
+ 'cancelled' => (clone $base)->where('status', 'cancelled')->count(),
|
|
|
|
|
+ ];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public function getFranchisorByStatus(string $status, array $unitIds = []): Collection
|
|
|
|
|
+ {
|
|
|
|
|
+ return StudentContract::with(['student', 'unit', 'createdBy'])->latestVersion()
|
|
|
|
|
+ ->where('status', $status)
|
|
|
|
|
+ ->when(! empty($unitIds), fn ($q) => $q->whereIn('unit_id', $unitIds))
|
|
|
|
|
+ ->orderBy('created_at', 'desc')
|
|
|
|
|
+ ->get();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public function getInstallments(int $contractId): Collection
|
|
|
|
|
+ {
|
|
|
|
|
+ return StudentContractInstallment::where('student_contract_id', $contractId)
|
|
|
|
|
+ ->where('status', 'pending')
|
|
|
|
|
+ ->orderBy('due_date')
|
|
|
|
|
+ ->orderBy('installment_number')
|
|
|
|
|
+ ->get();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ //
|
|
|
|
|
+
|
|
|
|
|
+ public function attachFile(int $id, UploadedFile $file, bool $signed = false): ?StudentContract
|
|
|
|
|
+ {
|
|
|
|
|
+ $model = StudentContract::find($id);
|
|
|
|
|
+
|
|
|
|
|
+ if (! $model) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $path = $file->store('student-media');
|
|
|
|
|
+
|
|
|
|
|
+ StudentMedia::create([
|
|
|
|
|
+ 'student_id' => $model->student_id,
|
|
|
|
|
+ 'student_contract_id' => $model->id,
|
|
|
|
|
+ 'url' => $path,
|
|
|
|
|
+ 'file_type' => $file->getMimeType(),
|
|
|
|
|
+ 'type' => $signed ? 'signed_contract' : 'contract',
|
|
|
|
|
+ ]);
|
|
|
|
|
+
|
|
|
|
|
+ $urlAttribute = $signed ? 'signed_file_url' : 'file_url';
|
|
|
|
|
+
|
|
|
|
|
+ $typeAttribute = $signed ? 'signed_file_type' : 'file_type';
|
|
|
|
|
+
|
|
|
|
|
+ $model->update([
|
|
|
|
|
+ $urlAttribute => $path,
|
|
|
|
|
+ $typeAttribute => $file->getMimeType(),
|
|
|
|
|
+ ]);
|
|
|
|
|
+
|
|
|
|
|
+ return $model->fresh();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ //
|
|
|
|
|
+
|
|
|
|
|
+ public function cancel(int $id, ?int $createdByUserId = null): ?StudentContract
|
|
|
|
|
+ {
|
|
|
|
|
+ return $this->update($id, ['status' => 'cancelled'], $createdByUserId);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public function freeze(int $id, int $months = 0, ?int $createdByUserId = null): ?StudentContract
|
|
|
|
|
+ {
|
|
|
|
|
+ return DB::transaction(function () use ($id, $months, $createdByUserId): ?StudentContract {
|
|
|
|
|
+ $contract = $this->update($id, ['status' => 'frozen'], $createdByUserId);
|
|
|
|
|
+
|
|
|
|
|
+ if (! $contract) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if ($months > 0) {
|
|
|
|
|
+ StudentContractInstallment::where('student_contract_id', $contract->id)
|
|
|
|
|
+ ->where('status', 'pending')
|
|
|
|
|
+ ->get()
|
|
|
|
|
+ ->each(function ($installment) use ($months) {
|
|
|
|
|
+ $newDate = Carbon::parse($installment->due_date)->addMonths($months);
|
|
|
|
|
+
|
|
|
|
|
+ $installment->update(['due_date' => $newDate->format('Y-m-d')]);
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return $contract->fresh();
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public function reactivate(int $id, ?int $createdByUserId = null): ?StudentContract
|
|
|
|
|
+ {
|
|
|
|
|
+ return $this->update($id, ['status' => 'active'], $createdByUserId);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ //
|
|
|
|
|
+
|
|
|
|
|
+ // monta o array de datas para n parcelas
|
|
|
|
|
+ // primeira parcela usa exatamente firstdate, data escolhida pelo usuario
|
|
|
|
|
+ // segunda parcela em diante usa recurringday avancando mes a mes a partir do mes da primeira
|
|
|
|
|
+ // exemplo: firstdate=25/05/2026, recurringday=5, count=3
|
|
|
|
|
+ // resultado: [25/05/2026, 05/06/2026, 05/07/2026]
|
|
|
|
|
+
|
|
|
|
|
+ private function buildInstallmentDates(string $firstDate, int $recurringDay, int $count): array
|
|
|
|
|
+ {
|
|
|
|
|
+ $dates = [];
|
|
|
|
|
+ $first = Carbon::createFromFormat('Y-m-d', $firstDate);
|
|
|
|
|
+ $dates[] = $first->copy();
|
|
|
|
|
+
|
|
|
|
|
+ $baseMonth = $first->copy()->startOfMonth();
|
|
|
|
|
+
|
|
|
|
|
+ for ($i = 1; $i < $count; $i++) {
|
|
|
|
|
+ $next = $baseMonth->copy()->addMonths($i);
|
|
|
|
|
+ $day = min($recurringDay, $next->daysInMonth);
|
|
|
|
|
+
|
|
|
|
|
+ $next->setDay($day);
|
|
|
|
|
+
|
|
|
|
|
+ $dates[] = $next;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return $dates;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private function generateInstallments(StudentContract $contract): void
|
|
private function generateInstallments(StudentContract $contract): void
|
|
|
{
|
|
{
|
|
|
$recurringDay = $contract->recurring_day ?? 1;
|
|
$recurringDay = $contract->recurring_day ?? 1;
|
|
|
|
|
+
|
|
|
$rows = [];
|
|
$rows = [];
|
|
|
- $now = now();
|
|
|
|
|
|
|
+
|
|
|
|
|
+ $now = now();
|
|
|
|
|
|
|
|
// Matrícula
|
|
// Matrícula
|
|
|
|
|
+
|
|
|
if ($contract->tax_register && $contract->installments && $contract->enrollment_due_date) {
|
|
if ($contract->tax_register && $contract->installments && $contract->enrollment_due_date) {
|
|
|
$value = round($contract->tax_register / $contract->installments, 2);
|
|
$value = round($contract->tax_register / $contract->installments, 2);
|
|
|
|
|
|
|
@@ -103,9 +325,10 @@ private function generateInstallments(StudentContract $contract): void
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Pacote
|
|
// Pacote
|
|
|
|
|
+
|
|
|
if ($contract->package_value && $contract->package_installments && $contract->package_due_date) {
|
|
if ($contract->package_value && $contract->package_installments && $contract->package_due_date) {
|
|
|
$value = round($contract->package_value / $contract->package_installments, 2);
|
|
$value = round($contract->package_value / $contract->package_installments, 2);
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
$dates = $this->buildInstallmentDates(
|
|
$dates = $this->buildInstallmentDates(
|
|
|
$contract->package_due_date->format('Y-m-d'),
|
|
$contract->package_due_date->format('Y-m-d'),
|
|
|
$recurringDay,
|
|
$recurringDay,
|
|
@@ -133,148 +356,39 @@ private function generateInstallments(StudentContract $contract): void
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if (!empty($rows)) {
|
|
|
|
|
|
|
+ if (! empty($rows)) {
|
|
|
// As parcelas são registradas/calculadas normalmente. A cobrança no Asaas
|
|
// As parcelas são registradas/calculadas normalmente. A cobrança no Asaas
|
|
|
// da franquia será reintroduzida no módulo Franchisee (conta própria por unidade).
|
|
// da franquia será reintroduzida no módulo Franchisee (conta própria por unidade).
|
|
|
- StudentContractInstallment::insert($rows);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * Monta o array de datas para N parcelas.
|
|
|
|
|
- *
|
|
|
|
|
- * - 1ª parcela: usa exatamente $firstDate (data escolhida pelo usuário)
|
|
|
|
|
- * - 2ª em diante: dia $recurringDay avançando mês a mês a partir do mês da 1ª
|
|
|
|
|
- *
|
|
|
|
|
- * Exemplo: firstDate=25/05/2026, recurringDay=5, count=3
|
|
|
|
|
- * → [25/05/2026, 05/06/2026, 05/07/2026]
|
|
|
|
|
- */
|
|
|
|
|
- private function buildInstallmentDates(string $firstDate, int $recurringDay, int $count): array
|
|
|
|
|
- {
|
|
|
|
|
- $dates = [];
|
|
|
|
|
- $first = Carbon::createFromFormat('Y-m-d', $firstDate);
|
|
|
|
|
- $dates[] = $first->copy();
|
|
|
|
|
-
|
|
|
|
|
- $baseMonth = $first->copy()->startOfMonth();
|
|
|
|
|
-
|
|
|
|
|
- for ($i = 1; $i < $count; $i++) {
|
|
|
|
|
- $next = $baseMonth->copy()->addMonths($i);
|
|
|
|
|
- $day = min($recurringDay, $next->daysInMonth);
|
|
|
|
|
- $next->setDay($day);
|
|
|
|
|
- $dates[] = $next;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return $dates;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- public function update(int $id, array $data): ?StudentContract
|
|
|
|
|
- {
|
|
|
|
|
- $model = $this->findById($id);
|
|
|
|
|
-
|
|
|
|
|
- if (!$model) {
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (!empty($data['due_day'])) {
|
|
|
|
|
- $data['recurring_day'] = (int) $data['due_day'];
|
|
|
|
|
- }
|
|
|
|
|
- unset($data['due_day']);
|
|
|
|
|
|
|
|
|
|
- $model->update($data);
|
|
|
|
|
- return $model->fresh();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- public function attachFile(int $id, $file): ?StudentContract
|
|
|
|
|
- {
|
|
|
|
|
- $model = $this->findById($id);
|
|
|
|
|
-
|
|
|
|
|
- if (!$model) {
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /** @var \Illuminate\Http\UploadedFile $file */
|
|
|
|
|
- $path = $file->store('student-media');
|
|
|
|
|
-
|
|
|
|
|
- StudentMedia::create([
|
|
|
|
|
- 'student_id' => $model->student_id,
|
|
|
|
|
- 'student_contract_id' => $model->id,
|
|
|
|
|
- 'url' => $path,
|
|
|
|
|
- 'file_type' => $file->getMimeType(),
|
|
|
|
|
- 'type' => 'contract',
|
|
|
|
|
- ]);
|
|
|
|
|
-
|
|
|
|
|
- $model->update(['file_url' => Storage::url($path), 'file_type' => $file->getMimeType()]);
|
|
|
|
|
-
|
|
|
|
|
- return $model->fresh();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- public function getInstallments(int $contractId): Collection
|
|
|
|
|
- {
|
|
|
|
|
- return StudentContractInstallment::where('student_contract_id', $contractId)
|
|
|
|
|
- ->where('status', 'pending')
|
|
|
|
|
- ->orderBy('due_date')
|
|
|
|
|
- ->orderBy('installment_number')
|
|
|
|
|
- ->get();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- public function freeze(int $id, int $months = 0): ?StudentContract
|
|
|
|
|
- {
|
|
|
|
|
- $model = $this->findById($id);
|
|
|
|
|
-
|
|
|
|
|
- if (!$model) {
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if ($months > 0) {
|
|
|
|
|
- StudentContractInstallment::where('student_contract_id', $id)
|
|
|
|
|
- ->where('status', 'pending')
|
|
|
|
|
- ->get()
|
|
|
|
|
- ->each(function ($installment) use ($months) {
|
|
|
|
|
- $newDate = Carbon::parse($installment->due_date)->addMonths($months);
|
|
|
|
|
- $installment->update(['due_date' => $newDate->format('Y-m-d')]);
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ StudentContractInstallment::insert($rows);
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- $model->update(['status' => 'frozen']);
|
|
|
|
|
- return $model->fresh();
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- public function cancel(int $id): ?StudentContract
|
|
|
|
|
|
|
+ private function loadVersionHistory(StudentContract $contract): void
|
|
|
{
|
|
{
|
|
|
- $model = $this->findById($id);
|
|
|
|
|
|
|
+ $root = $contract;
|
|
|
|
|
|
|
|
- if (!$model) {
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- $model->update(['status' => 'cancelled']);
|
|
|
|
|
- return $model->fresh();
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ while ($root->derived_from_contract_id !== null) {
|
|
|
|
|
+ $parent = StudentContract::with('createdBy')->find($root->derived_from_contract_id);
|
|
|
|
|
|
|
|
- public function reactivate(int $id): ?StudentContract
|
|
|
|
|
- {
|
|
|
|
|
- $model = $this->findById($id);
|
|
|
|
|
|
|
+ if (! $parent) {
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- if (!$model) {
|
|
|
|
|
- return null;
|
|
|
|
|
|
|
+ $root = $parent;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- $model->update(['status' => 'active']);
|
|
|
|
|
- return $model->fresh();
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ $history = new Collection;
|
|
|
|
|
|
|
|
- public function delete(int $id): bool
|
|
|
|
|
- {
|
|
|
|
|
- $model = $this->findById($id);
|
|
|
|
|
|
|
+ $current = $root;
|
|
|
|
|
|
|
|
- if (!$model) {
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ while ($current) {
|
|
|
|
|
+ $current->loadMissing('createdBy');
|
|
|
|
|
+ $history->push($current);
|
|
|
|
|
|
|
|
- if ($model->file_url) {
|
|
|
|
|
- Storage::delete($model->file_url);
|
|
|
|
|
|
|
+ $current = $current->derivedVersions()->with('createdBy')->first();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- return $model->delete();
|
|
|
|
|
|
|
+ $contract->setRelation('versionHistory', $history->sortByDesc('version')->values());
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|