diff --git a/client/package.json b/client/package.json index e7de5e54..d84b4ab8 100644 --- a/client/package.json +++ b/client/package.json @@ -57,5 +57,6 @@ "vite-plugin-vuetify": "^2.0.1", "vitest": "^1.0.4", "vue-tsc": "^1.8.25" - } + }, + "packageManager": "pnpm@10.11.0+sha512.6540583f41cc5f628eb3d9773ecee802f4f9ef9923cc45b69890fb47991d4b092964694ec3a4f738a420c918a333062c8b925d312f42e4f0c263eb603551f977" } diff --git a/client/src/components/dashboard/SetUserVacationBalance.vue b/client/src/components/dashboard/SetUserVacationBalance.vue index 15c89e4c..44ff10f7 100644 --- a/client/src/components/dashboard/SetUserVacationBalance.vue +++ b/client/src/components/dashboard/SetUserVacationBalance.vue @@ -143,12 +143,16 @@ export default { emergency: +totalBalanceFields.value.find((i) => i.key === 'emergency')!.value, excuse: +totalBalanceFields.value.find((i) => i.key === 'excuse')!.value, compensation: +totalBalanceFields.value.find((i) => i.key === 'compensation')!.value, + paternity: +totalBalanceFields.value.find((i) => i.key === 'paternity')!.value, + maternity: +totalBalanceFields.value.find((i) => i.key === 'maternity')!.value, }, remaining_days: { annual: +remainingBalanceFields.value.find((i) => i.key === 'annual')!.value, emergency: +remainingBalanceFields.value.find((i) => i.key === 'emergency')!.value, excuse: +remainingBalanceFields.value.find((i) => i.key === 'excuse')!.value, compensation: +remainingBalanceFields.value.find((i) => i.key === 'compensation')!.value, + paternity: +remainingBalanceFields.value.find((i) => i.key === 'paternity')!.value, + maternity: +remainingBalanceFields.value.find((i) => i.key === 'maternity')!.value, } } } @@ -169,6 +173,8 @@ export default { { key: 'emergency', label: 'Emergency', value: 10 }, { key: 'excuse', label: 'Excuse', value: 10 }, { key: 'compensation', label: 'Compensation', value: 30 }, + { key: 'paternity', label: 'Paternity', value: 30 }, + { key: 'maternity', label: 'Maternity', value: 30 }, ] // Fetch Office Users @@ -219,6 +225,18 @@ export default { value: 0, validationRules: getBalanceRules('compensation', 30) }, + { + label: 'Paternity', + key: 'paternity', + value: 0, + validationRules: getBalanceRules('paternity', 365) + }, + { + label: 'Maternity', + key: 'maternity', + value: 0, + validationRules: getBalanceRules('maternity', 365) + }, ] ) @@ -248,6 +266,18 @@ export default { value: 0, validationRules: getBalanceRules('compensation', 30) }, + { + label: 'Paternity', + key: 'paternity', + value: 0, + validationRules: getBalanceRules('paternity', 365) + }, + { + label: 'Maternity', + key: 'maternity', + value: 0, + validationRules: getBalanceRules('maternity', 365) + }, ] ) diff --git a/client/src/components/vacationBalance.vue b/client/src/components/vacationBalance.vue index 335fe170..d2ed0251 100644 --- a/client/src/components/vacationBalance.vue +++ b/client/src/components/vacationBalance.vue @@ -51,8 +51,8 @@ export default { }, }, setup() { - const vacationInfoHeaders = ['Annual', 'Emergency', 'Excuse', 'Compensation'] - const keys = ['annual', 'emergency', 'excuse', 'compensation'] + const vacationInfoHeaders = ['Annual', 'Emergency', 'Excuse', 'Compensation', 'Paternity', 'Maternity'] + const keys = ['annual', 'emergency', 'excuse', 'compensation', 'paternity', 'maternity'] const getDisplayValue = (balance: Api.Balance, keyIndex: number): string => { return Reflect.get(balance, keys[keyIndex]) || '0' diff --git a/client/src/types/api.ts b/client/src/types/api.ts index ca4cfa36..9da10d0b 100644 --- a/client/src/types/api.ts +++ b/client/src/types/api.ts @@ -33,7 +33,7 @@ export module Api { } export type RequestStatus = "approved" | "rejected" | "pending" | "requested_to_cancel" | "cancel_approved" | "cancel_rejected" | "canceled"; - export type VacationReasonWrapper = "emergency" | "annual" | "excuse" | "sick" | "unpaid" | "compensation"; + export type VacationReasonWrapper = "emergency" | "annual" | "excuse" | "sick" | "unpaid" | "compensation" | "maternity" | "paternity"; export interface Vacation { id: number @@ -141,6 +141,8 @@ export module Api { emergency: number; excuse: number; compensation: number; + paternity: number; + maternity: number; sick?: number; unpaid?: number; is_locked?: boolean, @@ -394,6 +396,8 @@ export module Api { emergency_leaves: number leave_excuses: number compensation: number + paternity: number + maternity: number } } } diff --git a/client/src/utils/add_update_user_form.ts b/client/src/utils/add_update_user_form.ts index e1faa9a9..9df94d9e 100644 --- a/client/src/utils/add_update_user_form.ts +++ b/client/src/utils/add_update_user_form.ts @@ -102,6 +102,24 @@ export const userBalance = ref([ isLoading: false, disabled: false, }, + { + label: 'Paternity', + key: 'paternity', + value: 30, + type: 'number', + rules: getBalanceRules('paternity', 0), + isLoading: false, + disabled: false, + }, + { + label: 'Maternity', + key: 'maternity', + value: 30, + type: 'number', + rules: getBalanceRules('maternity', 0), + isLoading: false, + disabled: false, + }, ]) export const userInputs = ref([ diff --git a/client/src/utils/helpers.ts b/client/src/utils/helpers.ts index 8c77cf61..5b9307ff 100644 --- a/client/src/utils/helpers.ts +++ b/client/src/utils/helpers.ts @@ -262,6 +262,14 @@ export function formatVacationBalanceCategorySelectAria(data: Api.BalanceVacatio name: `Compensation ${data.remaining_days.compensation} / ${data.total_days.compensation}`, reason: 'compensation' }, + { + name: `Paternity ${data.remaining_days.paternity} / ${data.total_days.paternity}`, + reason: 'paternity' + }, + { + name: `Maternity ${data.remaining_days.maternity} / ${data.total_days.maternity}`, + reason: 'maternity' + }, ] } @@ -283,5 +291,13 @@ export function formatVacationOldBalanceCategorySelectAria(data: Api.BalanceVaca name: `${data.year! - 1} compensation Leave | ${data.transferred_days!.compensation} ${data.transferred_days!.compensation <= 1 ? 'day' : 'days'}`, reason: 'compensation', }, + { + name: `${data.year! - 1} paternity Leave | ${data.transferred_days!.paternity} ${data.transferred_days!.paternity <= 1 ? 'day' : 'days'}`, + reason: 'paternity', + }, + { + name: `${data.year! - 1} maternity Leave | ${data.transferred_days!.maternity} ${data.transferred_days!.maternity <= 1 ? 'day' : 'days'}`, + reason: 'maternity', + }, ] } \ No newline at end of file diff --git a/server/cshr/management/commands/migrate_reasons.py b/server/cshr/management/commands/migrate_reasons.py index 74c7ee2a..ee60a049 100644 --- a/server/cshr/management/commands/migrate_reasons.py +++ b/server/cshr/management/commands/migrate_reasons.py @@ -10,6 +10,8 @@ "sick_leaves": ReasonChoices.Sick.value, "unpaid": ReasonChoices.Unpaid.value, "compensation": ReasonChoices.Compensation.value, + "paternity": ReasonChoices.Paternity.value, + "maternity": ReasonChoices.Maternity.value, } diff --git a/server/cshr/migrations/0034_vacationbalancemodel_maternity_and_more.py b/server/cshr/migrations/0034_vacationbalancemodel_maternity_and_more.py new file mode 100644 index 00000000..afd1e98c --- /dev/null +++ b/server/cshr/migrations/0034_vacationbalancemodel_maternity_and_more.py @@ -0,0 +1,42 @@ +# Generated by Django 4.2.17 on 2025-05-29 12:01 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("cshr", "0033_vacation_is_old_balance"), + ] + + operations = [ + migrations.AddField( + model_name="vacationbalancemodel", + name="maternity", + field=models.FloatField(default=365), + ), + migrations.AddField( + model_name="vacationbalancemodel", + name="paternity", + field=models.FloatField(default=365), + ), + migrations.AlterField( + model_name="vacation", + name="reason", + field=models.CharField( + choices=[ + ("public_holiday", "Public holiday"), + ("emergency", "Emergency"), + ("annual", "Annual"), + ("excuse", "Excuse"), + ("sick", "Sick"), + ("unpaid", "Unpaid"), + ("compensation", "Compensation"), + ("maternity", "Maternity"), + ("paternity", "Paternity"), + ], + default="annual", + max_length=20, + ), + ), + ] diff --git a/server/cshr/models/vacations.py b/server/cshr/models/vacations.py index 26998a26..7dce7055 100644 --- a/server/cshr/models/vacations.py +++ b/server/cshr/models/vacations.py @@ -20,6 +20,8 @@ class ReasonChoices(models.TextChoices): Sick = "sick", "Sick" Unpaid = "unpaid", "Unpaid" Compensation = "compensation", "Compensation" + Maternity = "maternity", "Maternity" + Paternity = "paternity", "Paternity" class Vacation(Requests): @@ -53,6 +55,8 @@ class VacationBalanceModel(TimeStamp): ### Fields - sick: The number of sick leaves the user has - compensation: The number of compensation leaves the user has + - paternity: The number of paternity leaves the user has + - maternity: The number of maternity leaves the user has - unpaid: The number of unpaid leaves the user has - annual: The number of annual leaves the user has - emergency: The number of emergency leaves the user has @@ -62,6 +66,8 @@ class VacationBalanceModel(TimeStamp): sick = models.FloatField(default=365) compensation = models.FloatField(default=365) + paternity = models.FloatField(default=365) + maternity = models.FloatField(default=365) unpaid = models.FloatField(default=365) annual = models.FloatField(default=15) emergency = models.FloatField(default=6) diff --git a/server/cshr/serializers/vacations.py b/server/cshr/serializers/vacations.py index e9d7237f..25c00f71 100644 --- a/server/cshr/serializers/vacations.py +++ b/server/cshr/serializers/vacations.py @@ -118,6 +118,8 @@ class Meta: "id", "sick", "compensation", + "paternity", + "maternity", "unpaid", "annual", "emergency", @@ -131,6 +133,8 @@ class UserBalanceSerializer(Serializer): emergency = FloatField() excuse = FloatField() compensation = FloatField() + paternity = FloatField() + maternity = FloatField() class BalanceObjectSerializer(Serializer): diff --git a/server/cshr/utils/balance_calculator.py b/server/cshr/utils/balance_calculator.py index e4b6e572..47b193d7 100644 --- a/server/cshr/utils/balance_calculator.py +++ b/server/cshr/utils/balance_calculator.py @@ -74,7 +74,7 @@ def check_user_balance(self): year=self.CURRENT_YEAR, defaults={ "total_days": VacationBalanceModel.objects.create(), - "remaining_days": VacationBalanceModel.objects.create(compensation=0), + "remaining_days": VacationBalanceModel.objects.create(compensation=0, paternity=0, maternity=0), "transferred_days": transferred_days, }, ) diff --git a/server/cshr/views/auth.py b/server/cshr/views/auth.py index 74a37ea2..4bff5eaa 100644 --- a/server/cshr/views/auth.py +++ b/server/cshr/views/auth.py @@ -89,6 +89,8 @@ def _validate_user_balance(user_balance: Dict) -> bool: ReasonChoices.Emergency, ReasonChoices.Excuse, ReasonChoices.Compensation, + ReasonChoices.Paternity, + ReasonChoices.Maternity, ] missing_fields = [ field for field in required_fields if field not in user_balance @@ -113,6 +115,8 @@ def _create_user_vacation_balance(user: User, user_balance: Dict) -> None: emergency=user_balance["emergency"], excuse=user_balance["excuse"], compensation=user_balance["compensation"], + paternity=user_balance["paternity"], + maternity=user_balance["maternity"], ) remaining_balance = VacationBalanceModel.objects.create( @@ -120,6 +124,8 @@ def _create_user_vacation_balance(user: User, user_balance: Dict) -> None: emergency=user_balance["emergency"], excuse=user_balance["excuse"], compensation=user_balance["compensation"], + paternity=user_balance["paternity"], + maternity=user_balance["maternity"], ) UserVacationBalance.objects.create( diff --git a/server/cshr/views/vacations.py b/server/cshr/views/vacations.py index dd542d27..01395d9a 100644 --- a/server/cshr/views/vacations.py +++ b/server/cshr/views/vacations.py @@ -306,6 +306,8 @@ def update_user_vacation_balance(self, user, balance: dict, year: int): def create_vacation_balance_model(self, balance_data: dict): return VacationBalanceModel.objects.create( compensation=balance_data.get("compensation"), + paternity=balance_data.get("paternity"), + maternity=balance_data.get("maternity"), annual=balance_data.get("annual"), emergency=balance_data.get("emergency"), excuse=balance_data.get("excuse"),