Skip to content

Commit

Permalink
#445 - seniority calculation refactor (#458)
Browse files Browse the repository at this point in the history
* #445 - rafactor: changed way how seniority is calculated

* #445 - refactor: added updating user employment data
  • Loading branch information
kamilpiech97 authored Jul 4, 2024
1 parent 0a9e1fb commit d40b4b2
Show file tree
Hide file tree
Showing 12 changed files with 173 additions and 11 deletions.
14 changes: 14 additions & 0 deletions app/Console/Commands/MigrateProfileDataIntoUserHistory.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public function handle(): void
foreach ($users as $user) {
$this->moveMedicalDataToHistory($user);
$this->moveOhsDataToHistory($user);
$this->moveEmploymentDataToHistory($user);
}
}

Expand All @@ -46,4 +47,17 @@ private function moveOhsDataToHistory(User $user): void
]);
}
}

private function moveEmploymentDataToHistory(User $user): void
{
if ($user->profile->employment_date) {
$user->histories()->create([
"from" => $user->profile->employment_date,
"to" => null,
"type" => UserHistoryType::Employment,
"employment_form" => $user->profile->employment_form,
"is_employed_at_current_company" => true,
]);
}
}
}
13 changes: 12 additions & 1 deletion app/Domain/EmployeesMilestonesRetriever.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@

use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Toby\Enums\UserHistoryType;
use Toby\Models\User;
use Toby\Models\UserHistory;

class EmployeesMilestonesRetriever
{
Expand Down Expand Up @@ -42,7 +44,16 @@ public function getSeniority(?string $searchText, string $direction = "asc"): Co
{
return User::query()
->search($searchText)
->orderByProfileField("employment_date", $direction)
->orderBy(
UserHistory::query()
->select("from")
->whereColumn("users.id", "user_histories.user_id")
->where("type", UserHistoryType::Employment)
->where("is_employed_at_current_company", true)
->orderBy("from", $direction)
->limit(1),
$direction,
)
->get();
}
}
1 change: 1 addition & 0 deletions app/Http/Requests/UserHistoryRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public function data(): array
"type" => $this->get("type"),
"employment_form" => $this->get("type") === UserHistoryType::Employment->value ? $this->get("employmentForm") : null,
"comment" => $this->get("comment"),
"is_employed_at_current_company" => $this->get("type") === UserHistoryType::Employment->value ? $this->boolean("isEmployedAtCurrentCompany") : null,
];
}
}
2 changes: 1 addition & 1 deletion app/Http/Resources/EmployeeMilestoneResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public function toArray($request): array
"isBirthdayToday" => (bool)$upcomingBirthday?->isToday(),
"seniorityDisplayDate" => $seniority,
"isWorkAnniversaryToday" => $isSeniorityAnniversaryToday,
"employmentDate" => $this->profile->employment_date->toDisplayString(),
"employmentDate" => $this->startOfEmploymentInCurrentCompany()?->from->toDisplayString(),
];
}
}
1 change: 1 addition & 0 deletions app/Http/Resources/UserHistoryResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public function toArray($request): array
"typeLabel" => $this->type->label(),
"employmentFormLabel" => $this->employment_form?->label(),
"employmentForm" => $this->employment_form?->value,
"isEmployedAtCurrentCompany" => $this->is_employed_at_current_company,
"comment" => $this->comment,
"userId" => $this->user_id,
];
Expand Down
38 changes: 36 additions & 2 deletions app/Models/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,24 @@ public function lastOhsTraining(): ?UserHistory
->first();
}

public function startOfEmploymentInCurrentCompany(): ?UserHistory
{
return $this->histories()
->where("type", UserHistoryType::Employment)
->where("is_employed_at_current_company", true)
->orderBy("from", "asc")
->first();
}

public function endOfEmploymentInCurrentCompany(): ?UserHistory
{
return $this->histories()
->where("type", UserHistoryType::Employment)
->where("is_employed_at_current_company", true)
->orderBy("from", "desc")
->first();
}

public function keys(): HasMany
{
return $this->hasMany(Key::class);
Expand Down Expand Up @@ -147,6 +165,13 @@ public function scopeOrderByProfileField(Builder $query, string $field, string $
return $query->orderBy($profileQuery, $direction);
}

public function scopeOrderByUserHistoryField(Builder $query, string $field, string $direction = "asc"): Builder
{
$profileQuery = UserHistory::query()->select($field)->whereColumn("users.id", "histories.user_id");

return $query->orderBy($profileQuery, $direction);
}

public function scopeWithVacationLimitIn(Builder $query, YearPeriod $yearPeriod): Builder
{
return $query->whereRelation(
Expand Down Expand Up @@ -186,12 +211,21 @@ public function upcomingBirthday(): ?Carbon

public function seniority(): ?string
{
$employmentDate = $this->profile->employment_date;
$startOfEmploymentInCurrentCompany = $this->startOfEmploymentInCurrentCompany();
$employmentDate = $startOfEmploymentInCurrentCompany?->from;

if ($employmentDate->isFuture() || $employmentDate->isToday()) {
if (!$employmentDate || $employmentDate->isFuture() || $employmentDate->isToday()) {
return null;
}

if ($startOfEmploymentInCurrentCompany->to !== null) {
$endOfEmploymentInCurrentCompany = $this->endOfEmploymentInCurrentCompany();

if ($endOfEmploymentInCurrentCompany->to !== null) {
return $employmentDate->longAbsoluteDiffForHumans($endOfEmploymentInCurrentCompany->to, 2);
}
}

return $employmentDate->longAbsoluteDiffForHumans(Carbon::today(), 2);
}

Expand Down
2 changes: 2 additions & 0 deletions app/Models/UserHistory.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
* @property ?Carbon $to
* @property UserHistoryType $type
* @property EmploymentForm $employment_form
* @property bool $is_employed_at_current_company
* @property User $user
*/
class UserHistory extends Model
Expand All @@ -31,6 +32,7 @@ class UserHistory extends Model
"to" => "date",
"type" => UserHistoryType::class,
"employment_form" => EmploymentForm::class,
"is_employed_at_current_company" => "boolean",
];

public function user(): BelongsTo
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class() extends Migration {
public function up(): void
{
Schema::table("user_histories", function (Blueprint $table): void {
$table->boolean("is_employed_at_current_company")->nullable();
});
}

public function down(): void
{
Schema::table("user_histories", function (Blueprint $table): void {
$table->dropColumn("is_employed_at_current_company");
});
}
};
26 changes: 25 additions & 1 deletion resources/js/Pages/UserHistory/Create.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup>
import { useForm } from '@inertiajs/inertia-vue3'
import FlatPickr from 'vue-flatpickr-component'
import { Listbox, ListboxButton, ListboxLabel, ListboxOption, ListboxOptions } from '@headlessui/vue'
import { Listbox, ListboxButton, ListboxLabel, ListboxOption, ListboxOptions, Switch } from '@headlessui/vue'
import { CheckIcon, ChevronUpDownIcon } from '@heroicons/vue/24/solid'
const props = defineProps({
Expand All @@ -16,6 +16,7 @@ const form = useForm({
comment: '',
type: '',
employmentForm: '',
isEmployedAtCurrentCompany: false,
})
function createForm() {
Expand All @@ -25,6 +26,7 @@ function createForm() {
type: data.type.value,
from: data.from,
to: data.to,
isEmployedAtCurrentCompany: data.isEmployedAtCurrentCompany,
}))
.post(`/users/${props.userId}/history`)
}
Expand Down Expand Up @@ -250,6 +252,28 @@ function createForm() {
</p>
</div>
</div>
<div
v-if="form.type?.value === 'employment'"
class="items-center py-4 sm:grid sm:grid-cols-3"
>
<label
for="flowSkipped"
class="block text-sm font-medium text-gray-700"
>
Zatrudniony w obecnej firmie
</label>
<div class="mt-2 sm:col-span-2 sm:mt-0">
<Switch
id="flowSkipped"
v-model="form.isEmployedAtCurrentCompany"
:class="[form.isEmployedAtCurrentCompany ? 'bg-blumilk-500' : 'bg-gray-200', 'relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blumilk-500']"
>
<span
:class="[form.isEmployedAtCurrentCompany ? 'translate-x-5' : 'translate-x-0', 'pointer-events-none inline-block h-5 w-5 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200']"
/>
</Switch>
</div>
</div>
<div class="flex justify-end py-3">
<div class="space-x-3">
<InertiaLink
Expand Down
25 changes: 24 additions & 1 deletion resources/js/Pages/UserHistory/Edit.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup>
import { useForm } from '@inertiajs/inertia-vue3'
import FlatPickr from 'vue-flatpickr-component'
import { Listbox, ListboxButton, ListboxLabel, ListboxOption, ListboxOptions } from '@headlessui/vue'
import { Listbox, ListboxButton, ListboxLabel, ListboxOption, ListboxOptions, Switch } from '@headlessui/vue'
import { CheckIcon, ChevronUpDownIcon } from '@heroicons/vue/24/solid'
const props = defineProps({
Expand All @@ -16,6 +16,7 @@ const form = useForm({
comment: props.history.comment,
type: props.types.find(type => type.value === props.history.type),
employmentForm: props.employmentForms.find(employmentForm => employmentForm.value === props.history.employment_form) ?? '',
isEmployedAtCurrentCompany: props.history.is_employed_at_current_company,
})
function updateForm() {
Expand Down Expand Up @@ -250,6 +251,28 @@ function updateForm() {
</p>
</div>
</div>
<div
v-if="form.type?.value === 'employment'"
class="items-center py-4 sm:grid sm:grid-cols-3"
>
<label
for="flowSkipped"
class="block text-sm font-medium text-gray-700"
>
Zatrudniony w obecnej firmie
</label>
<div class="mt-2 sm:col-span-2 sm:mt-0">
<Switch
id="flowSkipped"
v-model="form.isEmployedAtCurrentCompany"
:class="[form.isEmployedAtCurrentCompany ? 'bg-blumilk-500' : 'bg-gray-200', 'relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blumilk-500']"
>
<span
:class="[form.isEmployedAtCurrentCompany ? 'translate-x-5' : 'translate-x-0', 'pointer-events-none inline-block h-5 w-5 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200']"
/>
</Switch>
</div>
</div>
<div class="flex justify-end py-3">
<div class="space-x-3">
<InertiaLink
Expand Down
15 changes: 13 additions & 2 deletions resources/js/Pages/UserHistory/Index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,19 @@ defineProps({
:key="item.id"
class="bg-blumilk-25"
>
<td class="p-4 text-sm text-gray-500 whitespace-nowrap">
{{ item.typeLabel }} <span v-if="item.type === 'employment'">({{ item.employmentFormLabel }})</span>
<td class="p-4 text-sm text-gray-500 whitespace-nowrap flex items-center">
{{ item.typeLabel }}
<span
v-if="item.type === 'employment'"
class="flex items-center"
>
({{ item.employmentFormLabel }})
<img
v-if="item.isEmployedAtCurrentCompany === true"
class="h-4 text0red-500 ml-3"
src="/images/logo.svg"
>
</span>
</td>
<td class="p-4 text-sm text-gray-500 whitespace-nowrap">
{{ item.comment || '-' }}
Expand Down
24 changes: 21 additions & 3 deletions tests/Feature/EmployeesMilestonesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,19 +73,37 @@ public function testSortingByBirthdays(): void
public function testSortingBySeniority(): void
{
$user1 = User::factory()
->hasProfile(["employment_date" => Carbon::createFromDate(2023, 1, 31)])
->hasProfile()
->employee()
->create();
$user1->histories()->create([
"from" => Carbon::createFromDate(2023, 1, 31),
"type" => "employment",
"employment_form" => "employment_contract",
"is_employed_at_current_company" => true,
]);

$user2 = User::factory()
->hasProfile(["employment_date" => Carbon::createFromDate(2022, 1, 1)])
->hasProfile()
->employee()
->create();
$user2->histories()->create([
"from" => Carbon::createFromDate(2022, 1, 1),
"type" => "employment",
"employment_form" => "employment_contract",
"is_employed_at_current_company" => true,
]);

$user3 = User::factory()
->hasProfile(["employment_date" => Carbon::createFromDate(2021, 10, 4)])
->hasProfile()
->employee()
->create();
$user3->histories()->create([
"from" => Carbon::createFromDate(2021, 10, 4),
"type" => "employment",
"employment_form" => "employment_contract",
"is_employed_at_current_company" => true,
]);

$sortedUsersByLongestSeniority = $this->employeesMilestonesRetriever->getResults(null, "seniority-asc")->values();

Expand Down

0 comments on commit d40b4b2

Please sign in to comment.