Skip to content

Commit a71e1c5

Browse files
authored
Merge pull request #263 from puzzle/feature/64086-reminders-for-not-billed-times
2 parents d1b0bf9 + d242ad8 commit a71e1c5

14 files changed

+194
-57
lines changed

app/controllers/accounting_posts_controller.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class AccountingPostsController < CrudController
1414
self.permitted_attrs = [:closed, :offered_hours, :offered_rate, :offered_total,
1515
:remaining_hours, :portfolio_item_id, :service_id, :billable,
1616
:description_required, :ticket_required, :from_to_times_required,
17-
:meal_compensation,
17+
:meal_compensation, :billing_reminder_active,
1818
{ work_item_attributes: %i[name shortname description] }]
1919

2020
helper_method :order
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# frozen_string_literal: true
2+
3+
# Copyright (c) 2006-2025, Puzzle ITC GmbH. This file is part of
4+
# PuzzleTime and licensed under the Affero General Public License version 3
5+
# or later. See the COPYING file at the top-level directory or at
6+
# https://github.com/puzzle/puzzletime.
7+
8+
class NotBilledTimesReminderJob < CronJob
9+
self.cron_expression = '0 5 10 * *'
10+
11+
def perform
12+
responsible_employees_with_not_billed_times_last_month.each do |employee_data|
13+
EmployeeMailer.not_billed_times_reminder_mail(employee_data).deliver_now
14+
end
15+
end
16+
17+
def responsible_employees_with_not_billed_times_last_month
18+
Employee.joins(:employments)
19+
.joins(:worktimes)
20+
.joins('INNER JOIN work_items ON work_items.id = worktimes.work_item_id')
21+
.joins('INNER JOIN accounting_posts ON accounting_posts.work_item_id = work_items.id')
22+
.joins('INNER JOIN orders ON orders.work_item_id = ANY (work_items.path_ids)')
23+
.joins('INNER JOIN employees as responsibles ON responsibles.id = orders.responsible_id')
24+
.where(accounting_posts: { billing_reminder_active: true })
25+
.where(Period.parse('-1m').where_condition('worktimes.work_date'))
26+
.merge(Employment.active.during(Period.previous_month))
27+
.where(worktimes: { billable: true, invoice_id: nil })
28+
.select('responsibles.*, orders.id as order_id, work_items.path_names as client')
29+
.distinct
30+
end
31+
end

app/mailers/employee_mailer.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,13 @@ def worktime_commit_reminder_mail(employee)
1717
subject: 'PuzzleTime Zeiten freigeben'
1818
)
1919
end
20+
21+
def not_billed_times_reminder_mail(employee_data)
22+
@employee_data = employee_data
23+
24+
mail(
25+
to: "#{employee_data.firstname} #{employee_data.lastname} <#{employee_data.email}>",
26+
subject: "PTime: #{employee_data.client} - nicht verrechnete Leistungen im letzten Monat gefunden"
27+
)
28+
end
2029
end

app/models/accounting_post.rb

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,21 @@
99
#
1010
# Table name: accounting_posts
1111
#
12-
# id :integer not null, primary key
13-
# work_item_id :integer not null
14-
# portfolio_item_id :integer
15-
# offered_hours :float
16-
# offered_rate :decimal(12, 2)
17-
# offered_total :decimal(12, 2)
18-
# remaining_hours :integer
19-
# billable :boolean default(TRUE), not null
20-
# description_required :boolean default(FALSE), not null
21-
# ticket_required :boolean default(FALSE), not null
22-
# from_to_times_required :boolean default(FALSE), not null
23-
# closed :boolean default(FALSE), not null
24-
# service_id :integer
25-
# meal_compensation :boolean default(FALSE), not null
12+
# id :integer not null, primary key
13+
# work_item_id :integer not null
14+
# portfolio_item_id :integer
15+
# offered_hours :float
16+
# offered_rate :decimal(12, 2)
17+
# offered_total :decimal(12, 2)
18+
# remaining_hours :integer
19+
# billable :boolean default(TRUE), not null
20+
# description_required :boolean default(FALSE), not null
21+
# ticket_required :boolean default(FALSE), not null
22+
# closed :boolean default(FALSE), not null
23+
# from_to_times_required :boolean default(FALSE), not null
24+
# service_id :integer
25+
# billing_reminder_active :boolean default(TRUE), not null
26+
# meal_compensation :boolean default(FALSE), not null
2627
#
2728

2829
class AccountingPost < ApplicationRecord

app/views/accounting_posts/_form.html.haml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,4 @@
4444
.col-md-2
4545
.help-block.col-md-5
4646
Die Einstellungen zur Beschreibung, Ticket oder Von-Bis-Zeiten können nicht geändert werden, da bereits Leistungen ohne diese Angaben erfasst wurden.
47+
= f.labeled_input_field :billing_reminder_active, html_options = {caption: 'Erinnerung bei unverrechneten Leistungen senden', title: "Am 10. Tag des Monats wird eine Mail an den/die Auftragsverantwortliche:n gesendet, wenn im Vormonat verrechenbare Leistungen auf diese Position gebucht wurden, die keiner Rechnung zugewiesen sind", data: {toggle: :tooltip}}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
%h1.h3{:style => "box-sizing: border-box; margin: 0.67em 0; font-family: inherit; font-weight: 400; line-height: 1.1; color: inherit; margin-top: 20px; font-size: 24px; margin-bottom: 20px;"}
2+
Hallo
3+
= @employee_data.firstname
4+
%div{:style => "box-sizing: border-box;"}
5+
.lead
6+
Beim Auftrag
7+
= @employee_data.client
8+
wurden im letzten Monat verrechenbare Leistungen gebucht, welche noch keiner Rechnung zugeteilt wurden.
9+
%br
10+
Bitte
11+
= link_to 'überprüfe die Leistungen', order_order_services_url(order_id: @employee_data.order_id, invoice_id: '[leer]')
12+
%br
13+
.lead
14+
Liebe Grüsse
15+
%br
16+
Dein PuzzleTime
17+
%br
18+
Möchtest du zu einer Buchungsposition künftig keine Erinnerungsmail mehr erhalten, deaktiviere in den Einstellungen der Position die Checkbox "Erinnerung bei unverrechneten Leistungen senden".
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
Hallo <%= @employee_data.firstname %>
2+
3+
Beim Auftrag <%= @employee_data.client %> wurden im letzten Monat verrechenbare Leistungen gebucht, welche noch keiner Rechnung zugeteilt wurden.
4+
5+
Bitte <%= link_to 'überprüfe die Leistungen', order_order_services_url(order_id: @employee_data.order_id, invoice_id: '[leer]') %>
6+
7+
Möchtest du zu einer Buchungsposition künftig keine Erinnerungsmail mehr erhalten, deaktiviere in den Einstellungen der Position die Checkbox "Erinnerung bei unverrechneten Leistungen senden".
8+
9+
Liebe Grüsse
10+
Dein PuzzleTime
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# frozen_string_literal: true
2+
3+
class AddBillingReminderActiveToAccountingPosts < ActiveRecord::Migration[7.1]
4+
def change
5+
add_column :accounting_posts, :billing_reminder_active, :boolean, default: true, null: false
6+
end
7+
end

db/schema.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
t.boolean "closed", default: false, null: false
3636
t.integer "service_id"
3737
t.boolean "meal_compensation", default: false, null: false
38+
t.boolean "billing_reminder_active", default: true, null: false
3839
t.index ["portfolio_item_id"], name: "index_accounting_posts_on_portfolio_item_id"
3940
t.index ["service_id"], name: "index_accounting_posts_on_service_id"
4041
t.index ["work_item_id"], name: "index_accounting_posts_on_work_item_id"

test/fabricators/accounting_post_fabricator.rb

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,20 @@
99
#
1010
# Table name: accounting_posts
1111
#
12-
# id :integer not null, primary key
13-
# work_item_id :integer not null
14-
# portfolio_item_id :integer
15-
# offered_hours :float
16-
# offered_rate :decimal(12, 2)
17-
# offered_total :decimal(12, 2)
18-
# remaining_hours :integer
19-
# billable :boolean default(TRUE), not null
20-
# description_required :boolean default(FALSE), not null
21-
# ticket_required :boolean default(FALSE), not null
22-
# from_to_times_required :boolean default(FALSE), not null
23-
# closed :boolean default(FALSE), not null
24-
# service_id :integer
25-
# meal_compensation :boolean default(FALSE), not null
12+
# id :integer not null, primary key
13+
# work_item_id :integer not null
14+
# portfolio_item_id :integer
15+
# offered_hours :float
16+
# offered_rate :decimal(12, 2)
17+
# offered_total :decimal(12, 2)
18+
# remaining_hours :integer
19+
# billable :boolean default(TRUE), not null
20+
# description_required :boolean default(FALSE), not null
21+
# ticket_required :boolean default(FALSE), not null
22+
# from_to_times_required :boolean default(FALSE), not null
23+
# closed :boolean default(FALSE), not null
24+
# service_id :integer
25+
# billing_reminder_active :boolean default(TRUE), not null
2626
#
2727

2828
Fabricator(:accounting_post) do

0 commit comments

Comments
 (0)