From 5a87e58dc2f4f4f055b722b644afcea5b33a98ba Mon Sep 17 00:00:00 2001 From: Linus Degen Date: Wed, 10 Dec 2025 17:30:19 +0100 Subject: [PATCH 1/6] Confirmation dialog when saving new employment and vacation days empty. refs #63096 --- app/assets/javascripts/application.js.coffee | 1 + app/assets/javascripts/employments.js.coffee | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 app/assets/javascripts/employments.js.coffee diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee index 269700e83..7eaf4db51 100644 --- a/app/assets/javascripts/application.js.coffee +++ b/app/assets/javascripts/application.js.coffee @@ -49,6 +49,7 @@ #= require invoices #= require cockpit #= require turbolinks +#= require employments app = window.App ||= {} diff --git a/app/assets/javascripts/employments.js.coffee b/app/assets/javascripts/employments.js.coffee new file mode 100644 index 000000000..56a14c256 --- /dev/null +++ b/app/assets/javascripts/employments.js.coffee @@ -0,0 +1,19 @@ +# Copyright (c) 2006-2017, Puzzle ITC GmbH. This file is part of +# PuzzleTime and licensed under the Affero General Public License version 3 +# or later. See the COPYING file at the top-level directory or at +# https://github.com/puzzle/puzzletime. + +# Wenn edit -> form id: edit_employment_ + +$ -> + $('#new_employment').on 'submit', (event) -> + vacationDaysInput = $('#employment_vacation_days_per_year') + vacationDaysValue = vacationDaysInput.val().trim() + + if vacationDaysValue == '' + confirmation_message = "Möglicherweise ging vergessen die Ferientage pro Jahr einzutragen. Dennoch fortfahren?" + + if not confirm(confirmation_message) + event.preventDefault() + vacationDaysInput.focus() + return false \ No newline at end of file From 7173cd226181e40ec188f5114cfa9b188a03ee56 Mon Sep 17 00:00:00 2001 From: Linus Degen Date: Tue, 23 Dec 2025 10:21:28 +0100 Subject: [PATCH 2/6] Revert "Confirmation dialog when saving new employment and vacation days empty. refs #63096" This reverts commit 5a87e58dc2f4f4f055b722b644afcea5b33a98ba. --- app/assets/javascripts/application.js.coffee | 1 - app/assets/javascripts/employments.js.coffee | 19 ------------------- 2 files changed, 20 deletions(-) delete mode 100644 app/assets/javascripts/employments.js.coffee diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee index 7eaf4db51..269700e83 100644 --- a/app/assets/javascripts/application.js.coffee +++ b/app/assets/javascripts/application.js.coffee @@ -49,7 +49,6 @@ #= require invoices #= require cockpit #= require turbolinks -#= require employments app = window.App ||= {} diff --git a/app/assets/javascripts/employments.js.coffee b/app/assets/javascripts/employments.js.coffee deleted file mode 100644 index 56a14c256..000000000 --- a/app/assets/javascripts/employments.js.coffee +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) 2006-2017, Puzzle ITC GmbH. This file is part of -# PuzzleTime and licensed under the Affero General Public License version 3 -# or later. See the COPYING file at the top-level directory or at -# https://github.com/puzzle/puzzletime. - -# Wenn edit -> form id: edit_employment_ - -$ -> - $('#new_employment').on 'submit', (event) -> - vacationDaysInput = $('#employment_vacation_days_per_year') - vacationDaysValue = vacationDaysInput.val().trim() - - if vacationDaysValue == '' - confirmation_message = "Möglicherweise ging vergessen die Ferientage pro Jahr einzutragen. Dennoch fortfahren?" - - if not confirm(confirmation_message) - event.preventDefault() - vacationDaysInput.focus() - return false \ No newline at end of file From 667e63e2dfef3741df4446c74a3b2bfff0f6192d Mon Sep 17 00:00:00 2001 From: Linus Degen Date: Wed, 24 Dec 2025 10:04:55 +0100 Subject: [PATCH 3/6] Refactor logic for showing confirm dialog when saving employment without vacation days set. refs #63096 --- app/assets/javascripts/application.js.coffee | 1 + app/assets/javascripts/employments.js.coffee | 18 ++++++++++++++++++ app/controllers/employments_controller.rb | 8 ++++++++ app/models/employment.rb | 10 ++++++++++ app/views/employments/_form.html.haml | 2 +- 5 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 app/assets/javascripts/employments.js.coffee diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee index 269700e83..7eaf4db51 100644 --- a/app/assets/javascripts/application.js.coffee +++ b/app/assets/javascripts/application.js.coffee @@ -49,6 +49,7 @@ #= require invoices #= require cockpit #= require turbolinks +#= require employments app = window.App ||= {} diff --git a/app/assets/javascripts/employments.js.coffee b/app/assets/javascripts/employments.js.coffee new file mode 100644 index 000000000..0ac655871 --- /dev/null +++ b/app/assets/javascripts/employments.js.coffee @@ -0,0 +1,18 @@ +# Copyright (c) 2006-2017, Puzzle ITC GmbH. This file is part of +# PuzzleTime and licensed under the Affero General Public License version 3 +# or later. See the COPYING file at the top-level directory or at +# https://github.com/puzzle/puzzletime. + +$ -> + form = $('form[data-needs-confirmation="true"]') + return unless form.length + return if form.data('confirmed') + + if confirm('Möglicherweise ging vergessen die Ferientage pro Jahr einzutragen. Dennoch fortfahren?') + $('') + .attr('type', 'hidden') + .attr('name', 'confirmed') + .val('true') + .appendTo(form) + form.data('confirmed', true) + form.submit() diff --git a/app/controllers/employments_controller.rb b/app/controllers/employments_controller.rb index 6762e81ad..fa9f321d9 100644 --- a/app/controllers/employments_controller.rb +++ b/app/controllers/employments_controller.rb @@ -24,6 +24,7 @@ class EmploymentsController < ManageController before_render_form :load_employment_role_levels before_render_form :prefill_from_newest_employment + before_save :check_vacation_days_set before_save :check_percent before_save :check_employment_role_uniqueness @@ -52,6 +53,13 @@ def prefill_from_newest_employment entry.employment_roles_employments = newest.employment_roles_employments.map(&:dup) end + def check_vacation_days_set + if entry.needs_vacation_days_confirmation? && !params[:confirmed] + @needs_confirmation = true + throw :abort + end + end + def check_percent employment_roles_employments = params[:employment][:employment_roles_employments_attributes] || {} diff --git a/app/models/employment.rb b/app/models/employment.rb index d25fbfdaa..7a6bc5152 100644 --- a/app/models/employment.rb +++ b/app/models/employment.rb @@ -128,6 +128,16 @@ def date_label(date) date ? I18n.l(date) : 'offen' end + # Defines if confirmation before save is needed when vacation days are left empty + # Applies when latest employment is not older than one year and had vacation days set + def needs_vacation_days_confirmation? + latest = Employment.where(employee_id: employee_id) + .order(start_date: :desc).first + return false unless latest&.end_date && (latest.end_date < Time.zone.today - 1.year) + + latest.vacation_days_per_year.present? && vacation_days_per_year.blank? + end + private def vacations_per_period(period, days_per_year) diff --git a/app/views/employments/_form.html.haml b/app/views/employments/_form.html.haml index 395819cf0..39ec7ea2a 100644 --- a/app/views/employments/_form.html.haml +++ b/app/views/employments/_form.html.haml @@ -6,7 +6,7 @@ - @title = ti(:title, model: "#{models_label(false)} von #{h(parent)}") -= crud_form do |f| += crud_form data: { needs_confirmation: @needs_confirmation } do |f| = f.labeled_input_fields :start_date, :end_date = f.labeled_input_field :percent, min: 0, max: 200, step: 2.5 = f.labeled_input_field :vacation_days_per_year, help: 'Feld leer lassen, um den Wert aus den allgemeinen Anstellungsbedingungen zu verwenden.' From aa622c2ae28463f9ac2d7baab4a136b79b8c3ccf Mon Sep 17 00:00:00 2001 From: Linus Degen Date: Wed, 24 Dec 2025 11:04:38 +0100 Subject: [PATCH 4/6] Test cases for confirmation of employments without vacation days. refs #63096 --- app/models/employment.rb | 3 ++- test/models/employment_test.rb | 27 +++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/app/models/employment.rb b/app/models/employment.rb index 7a6bc5152..3f4d3dec8 100644 --- a/app/models/employment.rb +++ b/app/models/employment.rb @@ -133,7 +133,8 @@ def date_label(date) def needs_vacation_days_confirmation? latest = Employment.where(employee_id: employee_id) .order(start_date: :desc).first - return false unless latest&.end_date && (latest.end_date < Time.zone.today - 1.year) + return false unless latest + return false if latest.end_date && latest.end_date < 1.year.ago.to_date latest.vacation_days_per_year.present? && vacation_days_per_year.blank? end diff --git a/test/models/employment_test.rb b/test/models/employment_test.rb index 737359370..6ae88c743 100644 --- a/test/models/employment_test.rb +++ b/test/models/employment_test.rb @@ -117,6 +117,33 @@ def test_before_create_updates_previous_end_date assert_equal Date.parse('1.6.2013'), before2.end_date end + def test_confirmation_vacation_days_set + employee = Fabricate(:employee) + # Last employment has custom vacation days set + _old = Fabricate(:employment, employee:, start_date: 2.years.ago.to_date, end_date: nil, percent: 80, vacation_days_per_year: 27) + new = Employment.new(employee:, start_date: 1.day.ago.to_date, end_date: nil, percent: 80, vacation_days_per_year: nil) + + assert_predicate new, :needs_vacation_days_confirmation? + end + + def test_confirmation_vacation_days_not_set + employee = Fabricate(:employee) + # Last employment has no custom vacation days set + _old = Fabricate(:employment, employee:, start_date: 2.years.ago.to_date, end_date: nil, percent: 80, vacation_days_per_year: nil) + new = Employment.new(employee:, start_date: 1.day.ago.to_date, end_date: nil, percent: 80, vacation_days_per_year: nil) + + assert_not_predicate new, :needs_vacation_days_confirmation? + end + + def test_confirmation_latest_employement_long_ago + employee = Fabricate(:employee) + # Last employment has ended two years ago + _old = Fabricate(:employment, employee:, start_date: 5.years.ago.to_date, end_date: 2.years.ago.to_date, percent: 80, vacation_days_per_year: 27) + new = Employment.new(employee:, start_date: 1.day.ago.to_date, end_date: nil, percent: 80, vacation_days_per_year: nil) + + assert_not_predicate new, :needs_vacation_days_confirmation? + end + def test_vactions assert_equal 20, new_employment('1.1.2000', '31.12.2000').vacations assert_equal 40, new_employment('1.1.2000', '31.12.2001').vacations From 7a1651d3ea773c83151213e11e704e6d0d4a731a Mon Sep 17 00:00:00 2001 From: Linus Degen Date: Mon, 29 Dec 2025 09:41:32 +0100 Subject: [PATCH 5/6] Integration test for confirmation dialog in employment creation. refs #63096 --- test/integration/create_employment_test.rb | 44 ++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 test/integration/create_employment_test.rb diff --git a/test/integration/create_employment_test.rb b/test/integration/create_employment_test.rb new file mode 100644 index 000000000..b20b81fd7 --- /dev/null +++ b/test/integration/create_employment_test.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +# Copyright (c) 2006-2025, Puzzle ITC GmbH. This file is part of +# PuzzleTime and licensed under the Affero General Public License version 3 +# or later. See the COPYING file at the top-level directory or at +# https://github.com/puzzle/puzzletime. + +require 'test_helper' + +class CreateEmploymentTest < ActionDispatch::IntegrationTest + setup :init_view + + test 'new employment' do + page.assert_selector('tbody tr', count: 1) + click_link 'Erstellen' + + page.assert_selector('h1', text: "Anstellung von #{employee.lastname} #{employee.firstname} erstellen") + + fill_in 'employment_start_date', with: Date.current + fill_in 'employment_end_date', with: Date.current + 1 + accept_confirm('Möglicherweise ging vergessen die Ferientage pro Jahr einzutragen. Dennoch fortfahren?') do + click_button 'Speichern' + end + + assert_current_path( + "#{employee_employments_path(employee)}?returning=true", + ignore_query: false + ) + + page.assert_selector('tbody tr', count: 2) + end + + private + + def employee + @employee ||= Fabricate(:employee, management: true) + end + + def init_view + _employment = Fabricate(:employment, employee:, start_date: 2.years.ago.to_date, end_date: 1.day.ago.to_date, percent: 80, vacation_days_per_year: 27) + login_as employee + visit employee_employments_path(employee) + end +end From f7abb34b0ebe6d3b20f54706f2316db01623c2c5 Mon Sep 17 00:00:00 2001 From: Linus Degen Date: Mon, 29 Dec 2025 09:54:47 +0100 Subject: [PATCH 6/6] Rubocop fix. refs #63096 --- app/controllers/employments_controller.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/controllers/employments_controller.rb b/app/controllers/employments_controller.rb index fa9f321d9..fd34feef5 100644 --- a/app/controllers/employments_controller.rb +++ b/app/controllers/employments_controller.rb @@ -54,10 +54,10 @@ def prefill_from_newest_employment end def check_vacation_days_set - if entry.needs_vacation_days_confirmation? && !params[:confirmed] - @needs_confirmation = true - throw :abort - end + return unless entry.needs_vacation_days_confirmation? && !params[:confirmed] + + @needs_confirmation = true + throw :abort end def check_percent